Open skogl opened 2 years ago
I think this happens when the first audio samples end up with negative timestamps. When this happens:
false
. This prevents the first video frame from ever being rendered, which then prevents playback from starting.I can see there being multiple solutions to this:
ProgressiveMediaPeriod
from outputting negative timestamp samples (or at least setting the isDecodeOnly
flag on them). I think this is what happens if there's an explicit seek to t=0
, which is why seeking to the start of the stream allows playback to start.MediaCodecAudioRenderer
and evaluating whether we still need it.MediaCodecVideoRenderer
.Assigning for @tonihei , although it'll probably be some time before this issue is looked at in more detail.
- The current audio playback position is allowed to step backwards, due to this workaround.
- In the video renderer, this line evaluates to
false
. This prevents the first video frame from ever being rendered, which then prevents playback from starting.
I have the same issue, for example buffer.timeUs
was 999999176000 and currentPositionUs
was 1000000000000. What would be the best solution on the app side? I currently solved that with a background task that checks the player.getCurrentPosition()
and calls player.seekTo(0)
if the position is negative until the first frame has been rendered.
- Stop ProgressiveMediaPeriod from outputting negative timestamp samples (or at least setting the isDecodeOnly flag on them). I think this is what happens if there's an explicit seek to t=0, which is why seeking to the start of the stream allows playback to start.
- Looking at the workaround in MediaCodecAudioRenderer and evaluating whether we still need it.
- Tweaking the condition in MediaCodecVideoRenderer.
Not sure if these points are meant as internal or not? Point 2 and 3 require knowledge of the renderers that I don't have so there is no way for me to go in there and make changes that I feel comfortable with. Point 1 looks promising enough but I'm not sure if there's a simple way of overriding the ProgressiveMediaPeriod without having to make changes in the library itself? Is this also internal or is it something that I can try and modify myself?
I tested the workaround proposed by @Rudnev and it seem to work. A bit ugly I admit but then it's more ugly to be faced with a black screen when trying to start playback.
@skogl I tested another workaround, it doesn't require background task management, but I also do not have enough knowledge of the rendering pipeline to validate this solution:
public class AudioRenderer extends MediaCodecAudioRenderer {
public AudioRenderer(
Context context,
MediaCodecAdapter.Factory codecAdapterFactory,
MediaCodecSelector mediaCodecSelector,
boolean enableDecoderFallback,
@Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener,
AudioSink audioSink
) {
super(
context,
codecAdapterFactory,
mediaCodecSelector,
enableDecoderFallback,
eventHandler,
eventListener,
audioSink
);
}
@Override
protected void onQueueInputBuffer(DecoderInputBuffer buffer) {
// TODO Workaround the issue https://github.com/google/ExoPlayer/issues/10035
if (buffer.timeUs >= 1_000_000_000_000L) {
super.onQueueInputBuffer(buffer);
}
}
}
public class RenderersFactory extends DefaultRenderersFactory {
public RenderersFactory(Context context) {
super(context);
}
@Override
protected void buildAudioRenderers(
Context context,
@ExtensionRendererMode int extensionRendererMode,
MediaCodecSelector mediaCodecSelector,
boolean enableDecoderFallback,
AudioSink audioSink,
Handler eventHandler,
AudioRendererEventListener eventListener,
ArrayList<Renderer> out
) {
out.add(new AudioRenderer(
context,
getCodecAdapterFactory(),
mediaCodecSelector,
enableDecoderFallback,
eventHandler,
eventListener,
audioSink
));
// Add extension renderers here
}
}
ExoPlayer player = new ExoPlayer.Builder(context)
.setRenderersFactory(new RenderersFactory(context))
.build();
In my case it works (udp multicast), but it also looks ugly to me: Firstly I have no idea what the original workaround was made for, secondly I have no idea buffer.timeUs
< 1_000_000_000_000L is this the expected value or not, and lastly, I don't like to inherit library classes. I think we should wait for a fix from @tonihei or someone else from the library developers.
Thanks @Rudnev but this workaround does not work for me. Buffer position does not seem to be the issue for me but player position. I use your first workaround but modified it to only check if player's currentPosition()
is a negative value, and if so I immediately to a seekTo(0)
which kicks off playback.
I am not sure about the correlation of buffer vs position. Without knowing about your specific case it sounds feasable that buffer and position are different values. Maybe you have the same issue as me and that current position is just below zero?
I use your first workaround but modified it to only check if player's
currentPosition()
is a negative value, and if so I immediately to aseekTo(0)
which kicks off playback.
On the next line after player.play()? At first I wanted to bind to events, but getCurrentPosition() will return 0 for a while. And often the first event where it goes negative is the end of loading (max buffer size reached). So I run a task right after player.play(), it checks the position every 2 seconds, calls seekTo(0) if necessary, and then I cancel that task when the "rendered first frame" event is received.
I am not sure about the correlation of buffer vs position.
I don't know same issue or not, but my issue is exactly as @ojw28 described: 1) Position 0 is equal to 1000000000000 in the internal representation 2) Received data with a negative timestamp, for example -824000us, after adding offset -824000 + 1000000000000 = 999999176000 3) This condition is triggered, because 824000 > 500000. 4) The current position now is 999999176000, if we convert this from internal representation to human format the current position becomes -0.824ms
I'm still trying to figure out how it all works, but I hope I'm moving in the right direction.
My issue is that I feel that some mpeg-ts streams don't start playing automatically. The stream source is terrestrial dvb-t2 so I imagine that there are some noise in the stream which makes it unable to start.
I have tested this in the demo player in version 2.16.1 and the issue can be resolved by just fast forward and then the stream starts playing again. I haven't found any similar issues to this so that's why I'm filing this issue.
Here's the log output:
When comparing against working streams what I can see is that "mediaPos" has negative values (mediaPos=-0.63). It seems like the player i stuck (or waiting for pos 0).
Like I said, the originating issue is probably noise in the stream but since there's no changing the input from a terrestrial signal I feel that Exoplayer needs to be stable enough to handle this and to be able to start anyway.
I have sent an email with a none-working stream to dev.exoplayer@gmail.com.