Prevent re-buffering on changing max bit rate in ExoPlayer - android

I am using setMaxBitrate provided by DefaultTrackSelector to set max bit rate when user changes video quality.
val parameters = defaultTrackSelector.buildUponParameters()
.setMaxVideoBitrate(bitrate)
.build()
defaultTrackSelector.parameters = parameters
But as soon as this function is called, the current buffer is discarded & re-buffering is shown right away. Is there any way to keep playing using old buffer & just load the new buffer using the new bitrate settings like YouTube does?
This issue has been discussed here:
https://github.com/google/ExoPlayer/issues/3522
https://github.com/google/ExoPlayer/issues/2250
But there doesn't seem to be any solution yet. Any help regarding this issue would be appreciated. Thanks in advance.

Easily you can do it.You have to use ExoPlayer already and ExoPlayer is provide a seekTo() method.
On this method,You should pass only player current position at which point you stopped before.
Step:-1
You have to change your Quality like 144p to 720p. on this Changing time you have to store your current ExoPlayer current position used this method:-
Private int currentPosition=player.getCurrentPosition();
Step -2
After you have to build your exoplayer media source:-
// Measures bandwidth during playback. Can be null if not required.
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
// Produces DataSource instances through which media data is loaded.
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this, Util.getUserAgent(this, getString(R.string.app_name)), bandwidthMeter);
// This is the MediaSource representing the media to be played.
MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory).createMediaSource("Pass Your Video Url");
// Prepare the player with the source.
player.prepare(videoSource);
Step 3:-
check this condition
if (this.currentPosition > 0) {
player.seekTo(this.currentPosition);
this.currentPosition = 0;
player.setPlayWhenReady(true);
} else {
player.setPlayWhenReady(true);
}
and it's work good you have to watch your video in where are you left.
Step 4:-
If your quality his not good that time used is method.
public int getWifiLevel()
{
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
int linkSpeed = wifiManager.getConnectionInfo().getRssi();
int level = WifiManager.calculateSignalLevel(linkSpeed, 5);
return level;
}
Based on wifi level or link speed you can decide if it has the low connection or high connection internet.

Related

android antmedia webrtc switch camera to Screen Sharing and vice versa

I'm using webrtc-android-framework module provided by Antmedia official website. I was able to make connection and I can see the video published on the other side without any issues. However I'm unable to switch from camera to screen sharing.
I'm using below code to switch from camera capture to screen sharing.
public void MakeScreenCaptureReady() {
final EglBase.Context eglBaseContext = eglBase.getEglBaseContext();
PeerConnectionFactory peerConnectionFactory = peerConnectionClient.factory;
// create AudioSource
AudioSource audioSource = peerConnectionFactory.createAudioSource(new MediaConstraints());
this.audioTrack = peerConnectionFactory.createAudioTrack("101", audioSource);
surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBaseContext);
// create VideoCapturer
videoCapturer = createScreenCapturer();
VideoSource videoSource =
peerConnectionFactory.createVideoSource(videoCapturer.isScreencast());
localVideoTrack = peerConnectionFactory.createVideoTrack("118", videoSource);
videoCapturer.initialize(surfaceTextureHelper, context, videoSource.getCapturerObserver());
videoCapturer.startCapture(720, 1280, 30);
peerConnectionClient.setLocalVideoTrack(localVideoTrack);
peerConnectionClient.localVideoSender.setTrack(localVideoTrack, true); //true for taking ownership and replacing the existing track
}
It buffers screen sharing video for 2-3 seconds and stops throwing source error at the subscribers end. Basically no chunks available on server to further buffer.
I have already taken required permission for screen sharing before hitting the above code.
startActivityForResult(
mMediaProjectionManager!!.createScreenCaptureIntent(),
SCREEN_RECORD_REQUEST_CODE
)
This is the below code I'm using to call the above method on onActivityResult :
intent.putExtra(CallActivity.EXTRA_SCREENCAPTURE, true)
webRTCClient.setMediaProjectionParams(resultCode, data)
webRTCClient.MakeScreenCaptureReady()
How do I achieve switching between camera and screen capture? Any help is much appreciated. Thanks!

For Exoplayer's AdaptiveTrackSelection, should I switch to a single track with multiple bitrates instead of four tracks with separate bitrates?

Currently, I have a server that streams four RTMP MediaSources, one with 720p video source, one with 360p video source, one with 180p video source, and one audio-only source. If I wanted to switch resolutions, I have to stop the ExoPlayer instance, prepare the other track I wanted to switch to, then play.
The code I use to prepare the ExoPlayer instance:
TrackSelection.Factory adaptiveTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector = new DefaultTrackSelector(adaptiveTrackSelectionFactory);
RtmpDataSourceFactory rtmpDataSourceFactory = new RtmpDataSourceFactory(bandwidthMeter);
ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
factory = new AVControlExtractorMediaSource.Factory(rtmpDataSourceFactory);
factory.setExtractorsFactory(extractorsFactory);
createSource();
//noinspection deprecation
mPlayer = ExoPlayerFactory.newSimpleInstance(mActivity, trackSelector, new DefaultLoadControl(
new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE),
1000, // min buffer
2000, // max buffer
1000, // playback
1000, //playback after rebuffer
DefaultLoadControl.DEFAULT_TARGET_BUFFER_BYTES,
true
));
vwExoPlayer.setPlayer(mPlayer);
mPlayer.addAnalyticsListener(mAnalyticsListener);
With createSource() being:
private void createSource() {
factory.setTrackPlaybackFlag(AVControlExtractorMediaSource.PLAYBACK_BOTH_AV);
mMediaSource180 = factory.createMediaSource(Uri.parse(API.GAME_VIDEO_STREAM_URL_180()));
mMediaSource180.addEventListener(getHandler(), new MSourceDebuggerListener("GameMediaSource180"));
mMediaSource360 = factory.createMediaSource(Uri.parse(API.GAME_VIDEO_STREAM_URL_360()));
mMediaSource360.addEventListener(getHandler(), new MSourceDebuggerListener("GameMediaSource360"));
mMediaSource720 = factory.createMediaSource(Uri.parse(API.GAME_VIDEO_STREAM_URL_720()));
mMediaSource720.addEventListener(getHandler(), new MSourceDebuggerListener("GameMediaSource720"));
factory.setTrackPlaybackFlag(AVControlExtractorMediaSource.PLAYBACK_AUDIO_ONLY);
mMediaSourceAudio = factory.createMediaSource(Uri.parse(API.GAME_AUDIO_STREAM_URL()));
mMediaSourceAudio.addEventListener(getHandler(), new MSourceDebuggerListener("GameMediaSourceAudio"));
}
private void releaseSource() {
mMediaSource180.releaseSource(null);
mMediaSource360.releaseSource(null);
mMediaSource720.releaseSource(null);
mMediaSourceAudio.releaseSource(null);
}
And the code I currently use to switch between these MediaSources is:
private void changeTrack(MediaSource source) {
if (currentMediaSource == source) return;
try {
this.currentMediaSource = source;
mPlayer.stop(true);
mPlayer.prepare(source, true, true);
mPlayer.setPlayWhenReady(true);
if (source == mMediaSourceAudio) {
if (!audioOnly) {
try {
TransitionManager.beginDelayedTransition(rootView);
} catch (Exception ignored) {
}
layAudioOnly.setVisibility(View.VISIBLE);
vwExoPlayer.setVisibility(View.INVISIBLE);
audioOnly = true;
try {
GameQnAFragment fragment = findFragment(GameQnAFragment.class);
if (fragment != null) {
fragment.signAudioOnly();
}
} catch (Exception e) {
Trace.e(e);
}
try {
GamePollingFragment fragment = findFragment(GamePollingFragment.class);
if (fragment != null) {
fragment.signAudioOnly();
}
} catch (Exception e) {
Trace.e(e);
}
}
} else {
if (audioOnly) {
TransitionManager.beginDelayedTransition(rootView);
layAudioOnly.setVisibility(View.GONE);
vwExoPlayer.setVisibility(View.VISIBLE);
audioOnly = false;
}
}
} catch (Exception ignore) {
}
}
I wanted to implement a seamless switching between these MediaSources so that I don't need to stop and re-prepare, but it appears that this feature is not supported by ExoPlayer.
In addition, logging each MediaSource structure with the following code:
MappingTrackSelector.MappedTrackInfo info = ((DefaultTrackSelector)trackSelector).getCurrentMappedTrackInfo();
if(info != null) {
for (int i = 0; i < info.getRendererCount(); i++) {
TrackGroupArray trackGroups = info.getTrackGroups(i);
if (trackGroups.length != 0) {
for(int j = 0; j < trackGroups.length; j++) {
TrackGroup tg = trackGroups.get(j);
for(int k = 0; k < tg.length; k++) {
Log.i("track_info_"+i+"-"+j+"-"+k, tg.getFormat(k)+"");
}
}
}
}
}
Just nets me 1 video format and 1 audio format each.
My current workaround is to prepare another ExoPlayer instance in the background, replace the currently running instance with that upon preparations being complete, and release the old instance. That reduces the lag between the MediaSources somewhat, but doesn't come close to achieving seamless resolution changes like Youtube.
Should I implement my own TrackSelector and jam-pack all the 4 sources into that, should I implement another MediaSource that handles all 4 sources, or should I just tell the colleague who maintains the streams to switch to just one RTMP MediaSource with a sort of manifest that lists all the resolutions available for the AdaptiveTrackSelection to switch between them?
Adaptive Bit Rate Streaming is designed to allow easy switching between different bit rate streams, but it requires the streams to be segmented and the player to download the video segment by segment.
In this way the player can decide which bit rate to choose for the next segment depending on the current network conditions (and the device display size and t type). The player is able to seamlessly, apart from the different bitrate and quality, move from one bit rate to another this way.
See here for some more info: https://stackoverflow.com/a/42365034/334402
All the above relies on a delivery protocol which supports this segmentation and different bit rate streams. The most common ones today are HLS and MPEG-DASH.
The easiest way to support what I think you are looking for would be for you colleague who is supplying the stream to supply it using HLS and/or DASH.
Note that at the moment, both HLS and DASH are required as apple devices require HLS while other devices tend to default to DASH. Traditionally HLS used TS as the container for the video in the segments and DASH used fragmented MP4, but there is now a move for both to use CMAF, which is essentially fragmented MP4.
So in theory a single set of bit rate videos can be used for HLS and DASH now - in practice this will depend on whether your content is encrypted or not, as HLS and apple used one encryption mode and everyone else another in the past. This is changing now also but will take time before all devices support the new approach, where all devices can support the same encryption mode, so if your streams are encrypted this is an added complication at the moment.

SimpleExoplayerView plays audio, screen is white

So I am trying to play a DASH video in a SimpleExoplayerView. I am following the most basic tutorials from https://codelabs.developers.google.com/codelabs/exoplayer-intro/#0.
The trouble is, I am able to hear the audio but the screen is always white.
Things I have already tried -
Using three different .mpd links. Can hear the audio each time and the video is white in each case. So there is no problem with the .mpd links.
Tested in different devices - an API 19 HTC Desire and an API 24 Moto G4. So I'm sure it is not a device specific issue.
Here is the initialising method -
private void initialisePlayer() {
TrackSelection.Factory adaptiveTrackSelectionFactory = new AdaptiveTrackSelection.Factory
(defaultBandwidthMeter);
if (exoPlayer == null) {
exoPlayer = ExoPlayerFactory.newSimpleInstance(new DefaultRenderersFactory(context), new
DefaultTrackSelector(adaptiveTrackSelectionFactory), new DefaultLoadControl());
simpleExoPlayerView.setPlayer(exoPlayer);
exoPlayer.setPlayWhenReady(true);
DashMediaSource mediaSource = buildMediaResource();
exoPlayer.prepare(mediaSource);
}
}
And this is the buildMediaSource method -
private DashMediaSource buildMediaResource() {
String userAgent = Util.getUserAgent(context, getString(R.string.app_name));
DataSource.Factory manifestDataSourceFactory = new DefaultHttpDataSourceFactory(userAgent);
DashChunkSource.Factory dashChunkSourceFactory = new DefaultDashChunkSource.Factory(new
DefaultHttpDataSourceFactory(userAgent, defaultBandwidthMeter));
return new DashMediaSource.Factory(dashChunkSourceFactory, manifestDataSourceFactory).createMediaSource(Uri
.parse(url));
}
Am I missing something?

vlc-android-sdk - cannot view RTSP live video

I've been working on an Android application that shows live streaming video via RTSP.
Assuming I have a well-functioning RTSP server that passes h264 packets, and to view the stream we should connect to rtsp://1.2.3.4:5555/stream
So I tried to use the native MediaPlayer\VideoView, but no luck (the video was stuck after 2-3 seconds of playback, so I loaded mrmaffen's vlc-android-sdk (can be found here) and used the following code:
ArrayList<String> options = new ArrayList<String>();
options.add("--no-drop-late-frames");
options.add("--no-skip-frames");
options.add("-vvv");
videoVlc = new LibVLC(options);
newVideoMediaPlayer = new org.videolan.libvlc.MediaPlayer(videoVlc);
final IVLCVout vOut = newVideoMediaPlayer.getVLCVout();
vOut.addCallback(this);
vOut.setVideoView(videoView); //videoView is a pre-defined view which is part of the layout
vOut.attachViews();
newVideoMediaPlayer.setEventListener(this);
Media videoMedia = new Media (videoVlc, Uri.parse(mVideoPath));
newVideoMediaPlayer.setMedia(videoMedia);
newVideoMediaPlayer.play();
The problem is that I see a blank screen.
Keep in mind that when I put a RTSP link with audio stream only, it works fine.
Is someone familliar with this sdk and have an idea about this issue?
Thanks in advance
Try adding this option:
--rtsp-tcp
I play rtsp streaming with following code
try {
Uri rtspUri=Uri.parse("rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov");
final MediaWrapper mw = new MediaWrapper(rtspUri);
mw.removeFlags(MediaWrapper.MEDIA_FORCE_AUDIO);
mw.addFlags(MediaWrapper.MEDIA_VIDEO);
MediaWrapperListPlayer.getInstance().getMediaList().add(mw);
VLCInstance.getMainMediaPlayer().setEventListener(this);
VLCInstance.get().setOnHardwareAccelerationError(this);
final IVLCVout vlcVout = VLCInstance.getMainMediaPlayer().getVLCVout();
vlcVout.addCallback(this);
vlcVout.setVideoView(mSurfaceView);
vlcVout.attachViews();
final SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
final String aout = VLCOptions.getAout(pref);
VLCInstance.getMainMediaPlayer().setAudioOutput(aout);
MediaWrapperListPlayer.getInstance().playIndex(this, 0);
} catch (Exception e) {
Log.e(TAG, e.toString());
}
When you get playing event, you need enable video track.
private void onPlaying() {
stopLoadingAnimation();
VLCInstance.getMainMediaPlayer().setVideoTrackEnabled(true);
}
This may be helpful for you

How to use ExoPlayer

I want to use ExoPlayer in my app. Could you please tell me which is simplest example? I have tried to do likely https://github.com/google/ExoPlayer/ but it's not easy for me. I tried to import library as module then i received bintray-release error.
As stated in the main Readme.md, you can import ExoPlayer as you will do for any other dependencies :
In your app build.gradle > dependencies add :
compile 'com.google.android.exoplayer:exoplayer:rX.X.X'
The current version is r1.5.1 as of October 27, 2015. see here.
Old question but since there are too few simple ExoPlayer tutorials out there, I wrote this up. I recently converted an app I have from using Android's default media player to ExoPlayer. The performance gains are amazing and it works on a wider range of devices. It is a bit more complicated, however.
This example is tailored specifically to playing an http audio stream but by experimenting you can probably adapt it easily to anything else. This example uses the latest v1.xx of ExoPlayer, currently v1.5.11:
First, put this in your build.gradle (Module: app) file, under "dependencies":
compile 'com.google.android.exoplayer:exoplayer:r1.5.11'
Also your class should implement ExoPlayer.Listener:
...implements ExoPlayer.Listener
Now here's the relevant code to play an http audio stream:
private static final int RENDERER_COUNT = 1; //since we want to render simple audio
private static final int BUFFER_SEGMENT_SIZE = 64 * 1024; // for http mp3 audio stream use these values
private static final int BUFFER_SEGMENT_COUNT = 256; // for http mp3 audio steam use these values
private ExoPlayer exoPlayer;
// for http mp3 audio stream, use these values
int minBufferMs = 1000;
int minRebufferMs = 5000;
// Prepare ExoPlayer
exoPlayer = ExoPlayer.Factory.newInstance(RENDERER_COUNT, minBufferMs, minRebufferMs);
// String with the url of the stream to play
String stream_location = "http://audio_stream_url";
// Convert String URL to Uri
Uri streamUri = Uri.parse(stream_location);
// Settings for ExoPlayer
Allocator allocator = new DefaultAllocator(BUFFER_SEGMENT_SIZE);
String userAgent = Util.getUserAgent(ChicagoPoliceRadioService.this, "ExoPlayer_Test");
DataSource dataSource = new DefaultUriDataSource(ChicagoPoliceRadioService.this, null, userAgent);
ExtractorSampleSource sampleSource = new ExtractorSampleSource(
streamUri, dataSource, allocator, BUFFER_SEGMENT_SIZE * BUFFER_SEGMENT_COUNT);
MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource, MediaCodecSelector.DEFAULT);
// Attach listener we implemented in this class to this ExoPlayer instance
exoPlayer.addListener(this);
// Prepare ExoPlayer
exoPlayer.prepare(audioRenderer);
// Set full volume
exoPlayer.sendMessage(audioRenderer, MediaCodecAudioTrackRenderer.MSG_SET_VOLUME, 1f);
// Play!
exoPlayer.setPlayWhenReady(true);
There are three callback methods:
#Override
public void onPlayWhenReadyCommitted() {
// No idea what would go here, I left it empty
}
// Called when ExoPlayer state changes
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
// If playbackState equals STATE_READY (4), that means ExoPlayer is set to
// play and there are no errors
if (playbackState == ExoPlayer.STATE_READY) {
// ExoPlayer prepared and ready, no error
// Put code here, same as "onPrepared()"
}
}
// Called on ExoPlayer error
#Override
public void onPlayerError(ExoPlaybackException error) {
// ExoPlayer error occurred
// Put your error code here
}
And when you're done playing do the usual:
if (exoPlayer != null) {
exoPlayer.stop();
exoPlayer.release();
}
NOTE: I'm still not 100% sure about the details of all of the ExoPlayer settings. I've never tried playing video. Note this is for version 1.5.x of ExoPlayer, 2.0 changed a lot and I still haven't figured it out. I do highly recommend this code to anyone who has an app that streams audio from the web as the performance gains are incredible and for my app it fixed an issue with Samsung phones that would only play about 30sec of audio before stopping.

Categories

Resources