-
Notifications
You must be signed in to change notification settings - Fork 6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ExoPlayer stuck in buffering after re-adding the surface view a few time #2703
Comments
Any chance of a simple example in regular Java? Working out an unfamiliar (albeit fairly obvious, by the looks of things) language and having to mess around installing Android Studio extensions isn't that efficient for us :). It's also worth trying your sample against the latest |
I could not find any snapshot repo. Here the top version in Java. Its really trivial and just adds and removes views like I described. public class A2 extends AppCompatActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ViewGroup container = (ViewGroup) this.findViewById(android.R.id.content);
final SurfaceView surface = new SurfaceView(this);
container.addView(surface);
Factory videoTrackSelectionFactory = new Factory(new DefaultBandwidthMeter());
final DefaultTrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
DefaultLoadControl loadControl = new DefaultLoadControl();
SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, loadControl);
player.addListener(new EventListener() {
public void onTracksChanged(@Nullable TrackGroupArray trackGroups, @Nullable TrackSelectionArray trackSelections) {
Log.d("player", "onTracksChanged");
}
public void onPlayerError(@Nullable ExoPlaybackException error) {
Log.d("player", "onPlayerError");
}
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
String state;
switch (playbackState) {
case ExoPlayer.STATE_IDLE:
state = "idle";
break;
case ExoPlayer.STATE_BUFFERING:
state = "buffering";
break;
case ExoPlayer.STATE_READY:
state = "ready";
break;
case ExoPlayer.STATE_ENDED:
state = "ended";
break;
default:
state = "unknownState" + playbackState;
}
Log.d("player", "onPlayerStateChanged with playWhenRead=" + playWhenReady + " and playbackState=" + state);
}
public void onLoadingChanged(boolean isLoading) {
Log.d("player", "onLoadingChanged");
}
public void onPositionDiscontinuity() {
Log.d("player", "onPositionDiscontinuity");
}
public void onTimelineChanged(@Nullable Timeline timeline, @Nullable Object manifest) {
Log.d("player", "onTimelineChanged");
}
});
DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(this, this.getPackageName());
Uri dashUri = Uri.parse("http://www-itec.uni-klu.ac.at/ftp/datasets/DASHDataset2014/BigBuckBunny/15sec/BigBuckBunny_15s_simple_2014_05_09.mpd");
DashMediaSource mediaSource = new DashMediaSource(dashUri, dataSourceFactory, new com.google.android.exoplayer2.source.dash.DefaultDashChunkSource.Factory(dataSourceFactory), null, null);
player.prepare(mediaSource);
player.setVideoSurfaceView(surface);
player.setPlayWhenReady(true);
container.setOnClickListener(new OnClickListener() {
public final void onClick(View it) {
if (container.getChildCount() == 0) {
container.addView(surface);
} else {
container.removeView(surface);
}
}
});
}
} |
Thanks. I cannot reproduce the issue with the latest |
Facing same issue while reattching playerview after detached from parent ViewGroup.
Would be great help if you can short it out. Waiting for your replay. |
Any news on this? I made a sample project here: It immediately starts a video. When you click on the screen it removes the view. When you click it again it re-adds the view. After a few times the video stops playing. |
It's a real blocker for me as I can't use ExoPlayer inside a RecyclerView as it does exactly that. Any more info I can provide? |
I took a look at this and was able to reproduce, thanks. It's a fundamental limitation of MediaCodec (in the Android platform) that it needs a Surface attached to it at all times. When the SurfaceView is removed from the view the underlying Surface is destroyed. This in turn forces ExoPlayer to destroy its MediaCodec instance, because there's no longer a Surface for it to be attached to. When the SurfaceView is added back to the view hierarchy its underlying Surface is created again. ExoPlayer creates a new MediaCodec instance, however since it's a new instance it can only start decoding from the next key-frame. It does this and displays the next key-frame immediately, then waits for the playback position to reach the position of that key-frame before rendering subsequent frames. This is why even after a single remove/add cycle, you observe that the first frame that's displayed is from the future and frozen for a while. If you remove/add multiple times the player skips ahead 1 key-frame each time. If you do this enough, the player gets stuck in a weird state where it's buffered a long way into the future compared to the correct playback position, but by successively rendering all of the key-frames it actually doesn't have any frame to render. The player doesn't think it needs to buffer, but it's also in a state where it cannot transition to the playing state because it hasn't rendered a frame. The fundamental limitation of MediaCodec is a real pain, unfortunately. There are however things we can do in the library, and things you can do in your application: On the library side:
On the application side, there are a bunch of things you can do to try and provide a better user experience.
With:
In the future, we hope to be able to hook
|
Thanks for your response! What is The optimal behaviour would be for the playback just to immediately continue and not stuck. Also after each attach / detach - scroll up / down there is a different frame when paused which looks quite awkward. |
It's the index of the video renderer that you're disabling. You can query the type of each renderer using ExoPlayer.getRendererType(), which should allow you to figure out which index to use. |
We're going to use #677 to track the library side change mentioned above. |
Closing because #677 is tracking the library side change. |
ExoPlayer gets stuck in buffering after re-adding the surface view a few time.
I created a minimal example that demonstrates that problem. I used ExoPlayer 2.3.1 and tested it on a Nexus 5X with Android 7.1.2 and on a Moto G4 Play with Android 6.
I found this issue because I am removing the surface from ExoPlayer in a recyclerview when the view gets recycled but while trying to find the cause of this I created this demo without all the complexity.
It just prepares an ExoPlayer and when the user clicks on the screen it attaches / detaches the
SurfaceView
.I logged all events and it gets stuck in buffering state:
The text was updated successfully, but these errors were encountered: