Skip to content

32bit/96kHz FLAC file not playing in ExoPlayer but works with platform MediaPlayer and ffplay #2197

Closed
@nift4

Description

@nift4

Version

Media3 main branch

More version details

main branch at commit 66ef013

Devices that reproduce the issue

Pixel 7a Android 14 QPR3
API 35 emulator

Devices that do not reproduce the issue

N/A

Reproducible in the demo app?

Yes

Reproduction steps

  1. add https://temp.nift4.org/testfile.flac to demo app
  2. build demo app without extensions (did not test with extensions)
  3. try to play the file

This progressive media file is intended to be played from disk, and I have tested in my own app that playback fails with the same error when playing from file:// URI.

Expected result

The media file plays successfully.

This expected result can be achieved by using platform MediaPlayer, for example Google Play Music (the newest version without the deprecation timebomb 8.22.8261-1.P) and "Tiny Music Player" plays the file just fine. Additionally, ffplay https://temp.nift4.org/testfile.flac on my computer can play the file successfully.

Actual result

Spamming a warning for around 4 seconds and then failing with stack trace:

2025-02-27 23:51:56.529  5527-5736  DefaultLoadControl      androidx.media3.demo.main            W  Target buffer size reached with less than 500ms of buffered media data.
2025-02-27 23:51:56.541  5527-5736  DefaultLoadControl      androidx.media3.demo.main            W  Target buffer size reached with less than 500ms of buffered media data.
2025-02-27 23:51:56.552  5527-5736  DefaultLoadControl      androidx.media3.demo.main            W  Target buffer size reached with less than 500ms of buffered media data.
2025-02-27 23:51:56.564  5527-5736  DefaultLoadControl      androidx.media3.demo.main            W  Target buffer size reached with less than 500ms of buffered media data.
2025-02-27 23:51:56.574  5527-5736  DefaultLoadControl      androidx.media3.demo.main            W  Target buffer size reached with less than 500ms of buffered media data.
2025-02-27 23:51:56.576  5527-5736  ExoPlayerImplInternal   androidx.media3.demo.main            E  Playback error
                                                                                                      androidx.media3.exoplayer.ExoPlaybackException: Unexpected runtime error
                                                                                                          at androidx.media3.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:849)
                                                                                                          at android.os.Handler.dispatchMessage(Handler.java:103)
                                                                                                          at android.os.Looper.loopOnce(Looper.java:232)
                                                                                                          at android.os.Looper.loop(Looper.java:317)
                                                                                                          at android.os.HandlerThread.run(HandlerThread.java:85)
                                                                                                      Caused by: java.lang.IllegalStateException: Playback stuck buffering and not loading
                                                                                                          at androidx.media3.exoplayer.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:1412)
                                                                                                          at androidx.media3.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:646)
                                                                                                          at android.os.Handler.dispatchMessage(Handler.java:103) 
                                                                                                          at android.os.Looper.loopOnce(Looper.java:232) 
                                                                                                          at android.os.Looper.loop(Looper.java:317) 
                                                                                                          at android.os.HandlerThread.run(HandlerThread.java:85) 

Bug report ZIP (I will NOT email it, because I'll upload it here instead to prevent confusion - it's from an emulator so it does not contain any privacy sensitive data):

bugreport-sdk_gphone64_arm64-AE3A.240806.043-2025-02-27-23-53-26.zip

Media

https://temp.nift4.org/testfile.flac

Bug Report

  • You will email the zip file produced by adb bugreport to android-media-github@google.com after filing this issue.

Activity

self-assigned this
on Feb 28, 2025
icbaker

icbaker commented on Mar 12, 2025

@icbaker
Collaborator

I can reproduce the described behavior.

Debugging what happens, it looks like we end up buffering ~13.8MB of data before we ever call SampleQueue.sampleMetadata - which should be called here:

// Frame found.
if (nextFrameFirstSampleNumber != SAMPLE_NUMBER_UNKNOWN) {
outputSampleMetadata();
currentFrameBytesWritten = 0;
currentFrameFirstSampleNumber = nextFrameFirstSampleNumber;
}

The reason this is never called for the provided file is that findFrame(...) always returns SAMPLE_NUMBER_UNKNOWN - and this is because when we're looking forward for the next frame header, we reject the 'correct' frame header because checkBitsPerSample(...) always returns false here:

return checkChannelAssignment(channelAssignmentKey, flacStreamMetadata)
&& checkBitsPerSample(bitsPerSampleKey, flacStreamMetadata)
&& !reservedBit
&& checkAndReadFirstSampleNumber(
data, flacStreamMetadata, isBlockSizeVariable, sampleNumberHolder)
&& checkAndReadBlockSizeSamples(data, flacStreamMetadata, blockSizeKey)
&& checkAndReadSampleRate(data, flacStreamMetadata, sampleRateKey)
&& checkAndReadCrc(data, frameStartPosition);

Which in turn is because flacStreamMetadata.bitsPerSampleLookupKey == NOT_IN_LOOKUP_TABLE, so this equality is never true:

return bitsPerSampleKey == flacStreamMetadata.bitsPerSampleLookupKey;

and this is because case 32 is missing here:

private static int getBitsPerSampleLookupKey(int bitsPerSample) {
switch (bitsPerSample) {
case 8:
return 1;
case 12:
return 2;
case 16:
return 4;
case 20:
return 5;
case 24:
return 6;
default:
return NOT_IN_LOOKUP_TABLE;
}

Adding that makes this file playable.

It looks like full 32-bit support in FLAC is relatively new (https://xiph.org/flac/2022/09/09/flac-1-4-0-released.html), and support is not mandated in the CDD, so although this works on my Pixel 7a it may not work on every device (however I suspect in this case, the error will be a clearer "format not supported" error).

I'll send a change adding the case 32 and also include a 32-bit test asset. I'll use this asset to see if we can support this in the lib-decoder-flac extension too.

icbaker

icbaker commented on Mar 13, 2025

@icbaker
Collaborator

I tried playing the provided file on an API 27 emulator which has a OMX.google.flac.decoder decoder (with my extractor fix in place).

ExoPlayer thinks the track is supported:

tracks [eventTime=0.34, mediaPos=0.00, window=0, period=0
  group [
    [X] Track:0, id=null, mimeType=audio/flac, container=audio/flac, channels=2, sample_rate=96000, supported=YES
  ]

But playback fails with IllegalStateException from MediaCodec.native_dequeueOutputBuffer.

Just before this there's some logcat which shows the built-in decoder at this API level doesn't support 32 bits:

E FLACDecoder: parseMetadata: unsupported bits per sample 32
E SoftFlacDecoder: onQueueFilled: FLACDecoder parseMetaData returns error -1007
E ACodec  : [OMX.google.flac.decoder] ERROR(0x8000100b)
E ACodec  : signalError(omxError 0x8000100b, internalError -2147483648)

So I think as part of adding 32-bit support to androidx.media3.extractor.flac.FlacExtractor we should finesse the format support check to mark this track as unsupported when the MediaCodec FLAC decoder doesn't support 32-bit.

icbaker

icbaker commented on Mar 13, 2025

@icbaker
Collaborator

Did a bit more testing on other emulator API levels. tl;dr 32-bit FLAC decoding by OMX.google.flac.decoder can only be trusted on API 34+ (and, as above, even that is beyond what is required by the CDD).

This is what I observed:

  • <= API 26: FLAC decoding not supported by MediaCodec at all, ExoPlayer knows this and doesn't even try to play the file.
  • API 27 - 28: MediaCodec exception because 32-bit FLAC not supported (but ExoPlayer doesn't know, so tries to play it anyway)
  • API 29 - 33: No exception, but playback never progresses past 00:00 - silent failure. I guess it's also not supported.
  • API 34 - 35: Playback succeeds
nift4

nift4 commented on Mar 13, 2025

@nift4
Author

Your testing checks out with what can be observed with AOSP: libFLAC 1.4.x was first merged in API 34.

Thanks for the informative comments and prompt response, btw :)

icbaker

icbaker commented on Mar 17, 2025

@icbaker
Collaborator

This should be resolved by the commits linked above.

2 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

    Development

    No branches or pull requests

      Participants

      @icbaker@nift4

      Issue actions

        32bit/96kHz FLAC file not playing in ExoPlayer but works with platform MediaPlayer and ffplay · Issue #2197 · androidx/media