-
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
Gapless playback is not working #8594
Comments
@krocard can you take a look? |
I can't reproduce on a Pixel 3 on Android 10. Here are my exact steps: git clone -b r2.13.0 https://github.com/google/ExoPlayer.git && cd ExoPlayer
ANDROID_SDK_ROOT=~/Android/Sdk/ ./gradlew installNoDecoderExtensionsDebug
adb push testdata/src/test/assets/media/mp3/test.mp3 /sdcard/
adb shell am start -a com.google.android.exoplayer.demo.action.VIEW_LIST --es uri_0 file:///sdcard/test.mp3 --es uri_1 file:///sdcard/test.mp3 The file plays twice without any audible gap. I also tried to apply a similar change to yours (though test.mp3 is not in the demo asset, so I used a - player.setMediaItems(mediaItems, /* resetPosition= */ !haveStartPosition);
+ ConcatenatingMediaSource concatenatingMediaSource = new ConcatenatingMediaSource();
+ DefaultMediaSourceFactory defaultMediaSourceFactory = new DefaultMediaSourceFactory(this);
+ concatenatingMediaSource.addMediaSource(defaultMediaSourceFactory.createMediaSource(
+ MediaItem.fromUri("file:///sdcard/test.mp3")));
+ concatenatingMediaSource.addMediaSource(defaultMediaSourceFactory.createMediaSource(
+ MediaItem.fromUri("file:///sdcard/test.mp3")));
+ player.setMediaSource(concatenatingMediaSource); Please follow those steps (preferably the first ones a they do not involve any code change) and attach a bug report if you still observe the issue. |
I'm still facing the issue after following your steps (the first ones) with my device Samsung galaxy s10. |
Please take a look at the attached file : Screen_Recording_20210216-185257_ExoPlayer.mp4 |
I was able to reproduce on the emulator (pixel 3 Android 10). I have not yet traced the issue but I can hear underrun though that seem unrelated. |
I confirm this behavior with I clearly hear a gap with a Samsung Galaxy S8 (Android 9) But no gap at all on Pixel 4a and galaxy S20+ (both Android 11) |
I upgraded my Samsung galaxy S10 to Android 11 and I cannot reproduce the issue => Gapless playback seems working on Android 11. |
@krocard Any updates on this issue please? Thanks for any suggestions/clues |
I found what is happening but I have no root caused it yet. Normally (for example on Pixel) during a track transition here is what happens:
The issue I'm observing in the emulator is at step 5. The renderer assumes that if the last buffer of the track queued for decoding has a presentation time of τ, the last buffer decoded of the codec will have a presentation time of τ. This is not the case on the emulator. An additional decoded buffer with a presentation time τ'' is outputted by the codec even though no encoded buffer with a presentation time τ'' was ever pushed. Here is what happens on the emulator:
Following reproduction step from #8594 (comment). The consistent presentation times observed are:
I don't know why the decoder (OMX.google.mp3.decoder) outputs one more buffer than was queued. We might want to rely on the EOS flag on the buffer rather than the presentation time. Regardless, we should avoid the constant double |
Log of the Emulator at the track transition (gap is heard)
Log of the Pixel 3 (gapless)
[internal: patch adding logging at cl/359504935] |
@tonihei what is your opinion on this presentation time mismatch and extra codec buffer? |
I just tried forcing @Samrobbo do you think generalizing |
@Samrobbo @andrewlewis - Assuming the answer to Kevin's question is no, which I think it probably is, we might want to rethink the changes that made Is its primary purpose to avoid the need to pass end-of-stream through the decoder as a way of signalling the point at which the format changes? If so, we may want to consider reverting back to that approach. I think that its other purpose of making sure |
Generalising the If I'm remembering correctly, the timestamp tracker was used because the codec outputted timestamps that did not match up to input, and as a result we were losing track of the Format changes and such. I don't recall us ever passing EOS to signal format changes though? |
@andrewlewis pointed me to the fact that Codec 2 is now alias to OMX: https://cs.android.com/search?q=alias.*OMX.google.*.decoder Finally, as noted in this internal comment, codec are not required to match input and output timestamp. Google's C2 mp3 and QC's AAC decoder are confirmed to have such mismatch, but many other codec might too. I envision 2 possible solutions:
|
Thanks for the feedback. Will this issue be fixed on a future release of ExoPlayer? If so, how far down the line will that be? BR, |
ExoPlayer currently relies on on the timestamp matching on both side of the decoder. Removing this assumption is a pretty significant change. The current fix is to compensate for the only known codec having such discrepancy: Lines 1139 to 1141 in b100094
The issue is that on some devices, the name is aliased to Digging into the platform codec alias code, I realized that |
Looking into the emulator codec config, it is clear that the The config is the same for all Emulated Pixel 3 XL API 29 & 30 and real Pixel 3 XL API 29 and 30:
Nevertheless on API 29 on the emulator, when querying The weird issue is that this only affects an emulated Pixel 3 XL API 29. Emulating a Pixel 3 XL at API 30 doesn't show the bug, all codecs are correctly listed as OMX (aliased) and C2. So whatever is causing aliased codecs to only show as their aliased name seems to only affect some devices on API 29 (Android 10). As a result, if we find a way to differentiate the OMX and C2 codec is seems it will have to be by analysing their decoding behaviour rather than their metadata. Alternatively, we could consider all API 29 |
Investigations suggests that only some devices running on Android 10 are affected by this issue, which is caused by incorrect reporting by the platform of the decoder's name. The platform issue seems to have been fixed in Android 11. |
@krocard Sorry, I missed it between the two issues. How come is it not a regression bug? It was working prior to ExoPlayer r2.12.0 (works with r2.11.8 to be precise). If it is a regression bug, wouldn't the platform conditions be the same for both ExoPlayer versions? If they will be, how does it not occur in r2.11.8 and prior releases? I hope you see my point. I am not familiar with the project internals, but it shouldn't be so hard to fix this. |
There are 2 main mp3 software decoders:
Unfortunately they don't handle timestamp in the same way [1]. Prior to 2.12.0 we threaded both the same as the OMX one so there was no issue for most phones, as only few ones at the time had the c2 decoder. The bug is that some devices name their decoder OMX when the implementation is actually a C2 one. Thus they did not benefited from the 2.12.0 fix. As 2.12.0 changed the code for C2 codec only, for this bug to be a regression the device should be exposing an C2 codec for an OMX implementation. Which is not what I have observed in the only device which I could reproduce the issue (Pixel 3 XL API 29 which exposes OMX for C2 impl). Which device are you testing with? [1] All MP3 decoders remove the first 529 samples. In C2, the output timestamp is calculated based on number of samples returned in output whereas OMX plugin didn't account for the initial samples removed. OMX frame count is thus 529 frames shifted. |
@ashutoshgngwr Could you post the output of |
@krocard I ran both versions of ExoPlayer (r2.11.8 and r2.14.1) on a Pixel 3 emulator running
Before this encounter, when I had originally reported the issue #7994, my device (Xiaomi Mi A3) was running Android 10, and I observed the same behaviour on it as well. Gapless playback worked fine with r2.11.8, but it didn't on r2.12.0 and onwards. I guess we're back to square one. Thanks for taking the time to explain the issue. It seems to me that there isn't much that can be done from ExoPlayer's end to mitigate this issue? |
I am having the same issue, gapless is not working. there is a short gap between tracks.
Is there any update on this? Did anyone find a workaround? |
Tested using ffmepg for mp3 gapless for the users who had issues and it seems that for some of them it still occurs. Do I understand wrongly the explanation here that it's tied to the device codecs and it should work with ffmpeg or there's some other components involved? |
Any updates on this issue? Would be great to have this fixed. We still run 2.11.8, along with others affected by this bug |
MediaCodecRenderer currently has two independent paths to trigger events at stream changes: 1. Detection of the last output buffer of the old stream to trigger onProcessedStreamChange and setting the new output stream offset. 2. Detection of the first input buffer of the new stream to trigger onOutputFormatChanged. Both events are identical for most media. However, there are two problematic cases: A. (1) happens after (2). This may happen if the declared media duration is shorter than the actual last sample timestamp. B. (2) is too late and there are output samples between (1) and (2). This can happen if the new media outputs samples with a timestamp less than the first input timestamp. This can be made more robust by: - Keeping a separate formatQueue for each stream to avoid case A. - Force outputting the first format after a stream change to avoid case B. Issue: #8594 #minor-release PiperOrigin-RevId: 512586838
MediaCodecRenderer currently has two independent paths to trigger events at stream changes: 1. Detection of the last output buffer of the old stream to trigger onProcessedStreamChange and setting the new output stream offset. 2. Detection of the first input buffer of the new stream to trigger onOutputFormatChanged. Both events are identical for most media. However, there are two problematic cases: A. (1) happens after (2). This may happen if the declared media duration is shorter than the actual last sample timestamp. B. (2) is too late and there are output samples between (1) and (2). This can happen if the new media outputs samples with a timestamp less than the first input timestamp. This can be made more robust by: - Keeping a separate formatQueue for each stream to avoid case A. - Force outputting the first format after a stream change to avoid case B. Issue: google/ExoPlayer#8594 #minor-release PiperOrigin-RevId: 512586838
MediaCodecRenderer currently has two independent paths to trigger events at stream changes: 1. Detection of the last output buffer of the old stream to trigger onProcessedStreamChange and setting the new output stream offset. 2. Detection of the first input buffer of the new stream to trigger onOutputFormatChanged. Both events are identical for most media. However, there are two problematic cases: A. (1) happens after (2). This may happen if the declared media duration is shorter than the actual last sample timestamp. B. (2) is too late and there are output samples between (1) and (2). This can happen if the new media outputs samples with a timestamp less than the first input timestamp. This can be made more robust by: - Keeping a separate formatQueue for each stream to avoid case A. - Force outputting the first format after a stream change to avoid case B. Issue: google/ExoPlayer#8594 PiperOrigin-RevId: 512586838 (cherry picked from commit 3970343)
MediaCodecRenderer currently has two independent paths to trigger events at stream changes: 1. Detection of the last output buffer of the old stream to trigger onProcessedStreamChange and setting the new output stream offset. 2. Detection of the first input buffer of the new stream to trigger onOutputFormatChanged. Both events are identical for most media. However, there are two problematic cases: A. (1) happens after (2). This may happen if the declared media duration is shorter than the actual last sample timestamp. B. (2) is too late and there are output samples between (1) and (2). This can happen if the new media outputs samples with a timestamp less than the first input timestamp. This can be made more robust by: - Keeping a separate formatQueue for each stream to avoid case A. - Force outputting the first format after a stream change to avoid case B. Issue: #8594 #minor-release PiperOrigin-RevId: 512586838 (cherry picked from commit a02c8d8)
Issue description
When trying to play a gapless playback between two MP3 files, a noticeable gap is detected between them.
The issue is also reproduced when using Exoplayer demo app with the
test.mp3
file already added in assets.I added the following snippet to
PlayerActivity
in Exoplayer demo app L299 for test purpose => the issue is reproduced : a gap between the two media sources is heard.Please notice that the delay/padding information is well detected by Exoplayer for the
test.mp3
in demo app assets.Please let me know if you need more details.
Thank you
The text was updated successfully, but these errors were encountered: