-
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
Switching Surface smoothly #677
Comments
I'd be also interested in the answer to this question, as I'm trying to implement a fullscreen mode and need to change surface on the fly without an image gap. |
Hello @fabrantes, what do you plan to do? What I do, is using two exo players with to surface/texture views and switching them is a lot faster then setting a new surface. Maybe this will help you. |
Is there anyone figured out how to switch surface smoothly? |
At the moment I'm sharing the same SurfaceTexture on two TextureViews, that was the only decent approach I could find, but it requires some care with the View lifecycle. Still open to alternatives 😄 |
So there is no solution with Android API < 14 (use SurfaceView instead of TextureView)? |
What renderers are you using to target API levels under 14 anyway, given that the standard audio/video renderers require API level 16+? Note that only 4% of active Android devices are running API level 14 and below, and only 7% of active Android devices are running API level 15 and below. |
I use MediaPlayer for API < 14. |
@fabrantes, @ojw28 ,I share the same SurfaceTexture,but when goto another TextureView ,it only has voice no video.Do you have any idea about it? when goto another TextureView, I used like this to replay:
|
Hello, My code for activity switch:
And this code workaround has helped me to make exo player render video picture on the new surface on paused state:
|
@fabrantes Hi, I'd like to discuss about the Cubed (3) player project. How shall we proceed?a |
We've made one minor improvement in |
Thanks @ojw28 - I have switching SurfaceView when zooming to fullscreen mostly working using that change. I'm still having fun with rotation. In #1084 you noted:
what were your thoughts towards 'slightly awkward' options please? Whilst I can now switch SurfaceViews it seems that both of them need to be in layout to avoid the player throwing an exception and releasing the codec, and whilst the Activity rotates there's no layout. It would be ok to pause playback for the duration of the rotate - if the playback were actually paused (do I need to wait for playWhenReadyCommitted?) could the surface be swapped without releasing the codec in a similar way to a9617af and it not get upset when the previous surface is destroyed with the Activity? |
It's possible to implement a "dummy surface" that can be attached during the period where you otherwise wouldn't have one. It's possible to use this approach for non DRM'd content and DRM'd content that doesn't require a secure output path in its license policy. For DRM'd content that does require a secure output path, it's possible to use this approach only if the device supports We're aiming to push a change that adds a |
A DummySurface is useful with MediaCodec on API levels 23+. Rather than having to release a MediaCodec instance when the app no longer has a real surface to output to, it's possible to retain the MediaCodec, using MediaCodec.setOutputSurface to target a DummySurface instance instead. When the app has a real surface to output to again, it can call swap this surface back in instantaneously. Without DummySurface a new MediaCodec has to be instantiated at this point, and decoding can only start from a key-frame in the media. A future change may hook this up internally in MediaCodecRenderer for supported use cases, although this looks a little awkward. If this approach isn't viable, we can require applications wanting this to set a DummySurface themselves. This isn't easy to do with the way SimpleExoPlayerView.setPlayer works at the moment, however, so some changes will be needed either way. Issue: #677 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=154931778
Remaining work to do here is as follows:
|
A DummySurface is useful with MediaCodec on API levels 23+. Rather than having to release a MediaCodec instance when the app no longer has a real surface to output to, it's possible to retain the MediaCodec, using MediaCodec.setOutputSurface to target a DummySurface instance instead. When the app has a real surface to output to again, it can call swap this surface back in instantaneously. Without DummySurface a new MediaCodec has to be instantiated at this point, and decoding can only start from a key-frame in the media. A future change may hook this up internally in MediaCodecRenderer for supported use cases, although this looks a little awkward. If this approach isn't viable, we can require applications wanting this to set a DummySurface themselves. This isn't easy to do with the way SimpleExoPlayerView.setPlayer works at the moment, however, so some changes will be needed either way. Issue: #677 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=154931778
Thanks very much for this, I have fullscreen and rotation working well with a combination of 'fast surface switching on API level 23+' and DummySurface. Below 23 I think the best we can do is wait for a keyframe. Please let the YouTube SDK guys know about this :-) |
I implemented the workaround suggested in #2703 for Devices < 23: class SurfaceManagerApi17(private val player: SimpleExoPlayer, private val trackSelector: DefaultTrackSelector) : SurfaceManager {
override fun surfaceCreated(holder: SurfaceHolder) {
player.setVideoSurface(holder.surface)
videoRendererIndex()?.let {
trackSelector.setRendererDisabled(it, false)
}
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}
override fun surfaceDestroyed(holder: SurfaceHolder) {
player.setVideoSurface(null)
videoRendererIndex()?.let {
trackSelector.setRendererDisabled(it, true)
}
}
private fun videoRendererIndex() = (0 until player.rendererCount)
.firstOrNull {
player.getRendererType(it) == C.TRACK_TYPE_VIDEO
}
override fun release() {
player.setVideoSurface(null)
}
} That works fine when the surface gets destroyed but when it gets recreated, the audio pauses for a short time which is very disturbing. Is it possible to prevent that? |
Issue: #677 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=157831796
This will cause the test to exercise the code path of instantiating a DummySurface, rendering to it for 10 seconds, then re-targeting the real surface again. For secure content tests the code path is only exercised if DummySurface.SECURE_SUPPORTED is true. The logic for checking this is within MediaCodecVideoRenderer itself, rather than being part of the test. Issue: #677 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=157833026
This avoids calling getDecoderInfo repeatedly in the case where shouldInitCodec return false (e.g. because we don't have a surface and cannot instantiate a dummy surface). Issue: #677 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=157847702
@molexx - In the |
Thanks for all the efforts to resolve this problem. It's a device specific problem. My HuaWei phone works just fine while others not. |
If the user has paused the video before surface replacement the new surface stays black rather than showing the paused frame. It resumes fine when pressing play. |
I have a doubt about DummySurface. In your guys previous discussion, I get the information that we can use DummySurface to hold MediaCodec after surface is destroyed since api 17. But in the codes as follows: Lines 753 to 756 in e7c60a2
My question is that why the api should be larger than 23 if the DummySurface is designed to hold MediaCodec since api 17? |
It's not. Use of |
@ojw28 Okay, I get it. |
Simpler workaround for anyone who had this problem when switching activities: If you hide the player in onPause(), the black box will not show up. Sorry for Kotlin example
and then set the visible to View.VISIBLE right before you want to show it (and NOT in onResume()). I have a view in front of it to allow fading the player view in/out as well but I don't think that affects this logic.
Side note: Maybe there should be a check in SimpleExoPlayerView#setVisibility if you're setting the same visibility as current. |
Kindly give full solution with proper class Name |
Note,
you need to make sure that hardware acceleration is enabled, go to manifest file and ad this line
|
This is actually more of a question rather than an issue.
Whenever you call MediaCodecVideoTrackRenderer.setSurface() with a new Surface, playback will stop until a new sync frame is received. I haven't looked too deep into the internals but I guess the buffers used by MediaCodec are obtained directly from a given surface so it needs to be reset. Is there any way of passing the current decoding state to a new MediaCodec/Surface?
The question is if it would be possible at all to avoid the playback gap at all and what modifications would it require to ExoPlayer.
Thanks!
The text was updated successfully, but these errors were encountered: