media icon indicating copy to clipboard operation
media copied to clipboard

Subtitle parsing broken in 1.4.0-alpha02+

Open JonWatson opened this issue 1 year ago • 17 comments

Version

Media3 main branch

More version details

Subtitle parsing for a given DASH asset (DRM protected) works fine in 1.4.0-alpha01 but in 1.4.0-alpha02 and 1.4.0-beta01 the playback fails with the following exception. Without provding the asset, can you help figure this out? Any info from the DASH manifest that I could provide to help? Again, same asset plays fine and displays subtitles in 1.4.0-alpha01

androidx.media3.exoplayer.ExoPlaybackException: Source error
      at androidx.media3.exoplayer.ExoPlayerImplInternal.handleIoException(ExoPlayerImplInternal.java:741)
      at androidx.media3.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:711)
      at android.os.Handler.dispatchMessage(Handler.java:102)
      at android.os.Looper.loop(Looper.java:223)
      at android.os.HandlerThread.run(HandlerThread.java:67)
  Caused by: androidx.media3.common.ParserException: SubtitleParser failed.{contentIsMalformed=true, dataType=1}
      at androidx.media3.extractor.text.SubtitleExtractor.parseAndWriteToOutput(SubtitleExtractor.java:258)
      at androidx.media3.extractor.text.SubtitleExtractor.read(SubtitleExtractor.java:161)
      at androidx.media3.exoplayer.source.chunk.BundledChunkExtractor.read(BundledChunkExtractor.java:241)
      at androidx.media3.exoplayer.source.chunk.ContainerMediaChunk.load(ContainerMediaChunk.java:132)
      at androidx.media3.exoplayer.upstream.Loader$LoadTask.run(Loader.java:421)
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
      at java.lang.Thread.run(Thread.java:923)
  Caused by: java.lang.IllegalArgumentException
      at androidx.media3.common.util.Assertions.checkArgument(Assertions.java:40)
      at androidx.media3.extractor.text.webvtt.WebvttSubtitle.getEventTime(WebvttSubtitle.java:63)
      at androidx.media3.extractor.text.LegacySubtitleUtil.toCuesWithTiming(LegacySubtitleUtil.java:44)
      at androidx.media3.extractor.text.webvtt.WebvttParser.parse(WebvttParser.java:108)
      at androidx.media3.extractor.text.SubtitleParser.parse(SubtitleParser.java:152)
      at androidx.media3.extractor.text.SubtitleExtractor.parseAndWriteToOutput(SubtitleExtractor.java:238)
      at androidx.media3.extractor.text.SubtitleExtractor.read(SubtitleExtractor.java:161) 
      at androidx.media3.exoplayer.source.chunk.BundledChunkExtractor.read(BundledChunkExtractor.java:241) 
      at androidx.media3.exoplayer.source.chunk.ContainerMediaChunk.load(ContainerMediaChunk.java:132) 
      at androidx.media3.exoplayer.upstream.Loader$LoadTask.run(Loader.java:421) 
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
      at java.lang.Thread.run(Thread.java:923) 

Devices that reproduce the issue

Nvidia Shield TV Pixel 7 Pro

Devices that do not reproduce the issue

None known

Reproducible in the demo app?

Yes

Reproduction steps

player.trackSelectionParameters =
  player.trackSelectionParameters.buildUpon()
    .setTrackTypeDisabled(TRACK_TYPE_TEXT, !showSubtitles)
    .setPreferredTextLanguage("eng")
    .build()

Expected result

Playback succeeds even when displaying subtitles

Actual result

Receive ERROR_CODE_PARSING_CONTAINER_MALFORMED 3001 during playback when attempting to display subtitles

Media

Private URL. Please let me know if there is some info I can provide from the DASH manifest

Bug Report

  • [ ] You will email the zip file produced by adb bugreport to [email protected] after filing this issue.

JonWatson avatar Jul 01 '24 14:07 JonWatson

1.4.0-alpha02 moved subtitle parsing to happen during extraction by default (instead of during rendering). I wonder if your problem is resolved by moving it back to during rendering? You can do this by calling both MediaSource.Factory.experimentalParseSubtitlesDuringExtraction(false) and TextRenderer.experimentalSetLegacyDecodingEnabled(true) (see also the release notes: https://github.com/androidx/media/releases/tag/1.4.0-alpha02).

Looking at the code, it seems the exception you're seeing is basically an index out of bounds issue:

https://github.com/androidx/media/blob/023e9d1479d5a99251cf2163e84ce645732de5b8/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttSubtitle.java#L61-L65

I think I can see the issue with the logic here - we need to either not return subtitle.getEventTimeCount() from getStartIndex(...) or check that startIndex < subtitle.getEventTimeCount() before calling long firstEventTimeUs = subtitle.getEventTime(startIndex). I will have a go at writing a test case to reproduce the stack trace you're seeing, and see if this change resolves it.

https://github.com/androidx/media/blob/023e9d1479d5a99251cf2163e84ce645732de5b8/libraries/extractor/src/main/java/androidx/media3/extractor/text/LegacySubtitleUtil.java#L40-L52

icbaker avatar Jul 01 '24 15:07 icbaker

I've added a test that creates the same stack trace, and sent a change the fixes the code to not crash in this case - I hope to include this in 1.4.0-rc01. Thanks for raising this, and providing stack trace details so we could work out the issue.

icbaker avatar Jul 01 '24 16:07 icbaker

This should be fixed by the commit above.

icbaker avatar Jul 02 '24 08:07 icbaker

Hello @icbaker

I'm testing this out in 1.4.0 and still see the same problem. You are correct in that this is the place the IllegalArgumentException is getting thrown:

 public long getEventTime(int index) { 
   Assertions.checkArgument(index >= 0); 
   Assertions.checkArgument(index < sortedCueTimesUs.length);  <-------------- HERE
   return sortedCueTimesUs[index]; 
 } 

and it is indeed because the nextEventTimeIndex is C.INDEX_UNSET, which causes the startIndex to be set to the length of the array

   private static int getStartIndex(Subtitle subtitle, OutputOptions outputOptions) {
    if (outputOptions.startTimeUs == C.TIME_UNSET) {
      return 0;
    }
    int nextEventTimeIndex = subtitle.getNextEventTimeIndex(outputOptions.startTimeUs);
    if (nextEventTimeIndex == C.INDEX_UNSET) {
      return subtitle.getEventTimeCount();        <---------- HERE
    }
    if (nextEventTimeIndex > 0
        && subtitle.getEventTime(nextEventTimeIndex - 1) == outputOptions.startTimeUs) {
      nextEventTimeIndex--;
    }
    return nextEventTimeIndex;
  }

I'm working on changing the setup to try the legacy way and will report on the results there

JonWatson avatar Jul 29 '24 16:07 JonWatson

The array of sortedCueTimesUs is like this from our VTT:

[0, 241000, 241000, 1409000, 1409000, 1442000, 1442000, 1475000, 1475000, 1509000, 1509000, 1576000, 1576000, 1642000, 1642000, 1676000, 1676000, 1742000, 1742000, 1776000, 1776000, 1809000, 1809000, 1843000, 1843000, 2009000, 2009000, 2043000, 2043000, 2076000, 2076000, 2109000, 2109000, 2210000, 2210000, 2243000, 2243000, 2276000, 2276000, 2310000, 2310000, 2610000, 2610000, 2677000, 2677000, 2810000, 2810000, 2844000, 2844000, 2877000, 2877000, 3211000, 3211000, 3244000, 3244000, 3277000, 3277000, 3511000, 3511000, 3544000, 3544000, 3578000, 3578000, 3644000, 3644000, 3678000, 3678000, 3711000, 3711000, 4004000]

But the value we are searching for in Util.binarySearchCeil() is 458500275832 so it seems like our third-party VTTs are not in the expected format somehow...

JonWatson avatar Jul 29 '24 16:07 JonWatson

I'd like to point out that ExoPlayer doesn't crash, everything is caught.

It's just that our playback is broken (we get a 3001) when trying to play with subtitles on

JonWatson avatar Jul 29 '24 17:07 JonWatson

Please can you give us a way to play a stream that reproduces this issue? Without that I'm afraid we're just guessing at the possible cause (and it seems that last time we guessed wrong).

icbaker avatar Jul 29 '24 17:07 icbaker

Please either upload it here or send it to [email protected] with the subject Issue #1516. Please also update this issue to indicate you’ve done this.

icbaker avatar Jul 29 '24 17:07 icbaker

Our videos are locked with DRM and our DRM tokens are only good for 10 minutes. I cannot share our source so I think we may be stuck here. It may be that I can provide you with a dynamic URL that will return DRM tokens you can use(along with our custom DRM implementation) but I need to consider this..

JonWatson avatar Jul 29 '24 17:07 JonWatson

Confirmed our subtitles show properly and no playback errors if we use:

MediaSource.Factory.experimentalParseSubtitlesDuringExtraction(false) TextRenderer.experimentalSetLegacyDecodingEnabled(true)

JonWatson avatar Jul 29 '24 19:07 JonWatson

@JonWatson Are you able to share the DASH manifest URL via email, without the DRM license/token info? That may be enough for us to reproduce the issue (since it only concerns subtitles, which presumably aren't encrypted).

icbaker avatar Jul 31 '24 15:07 icbaker

Yes I can do that! An example manifest URL has been sent to [email protected]

JonWatson avatar Jul 31 '24 18:07 JonWatson

Thanks for the test stream, it's been really helpful to look into this issue. I've fixed the IndexOutOfBoundsException with the commit linked above, which solves the playback error, but subtitles are still not shown on your stream.

Looking in your DASH manifest it seems that your subtitles are raw WebVTT (not encapsulated in ISOBMFF / MP4) but are segmented. I believe this combination is not supported by the DASH-IF IOP 5.2.10 (emphasis mine):

Some services store text adaptation sets in stand-alone IMSC1 or WebVTT files, without segmentation or [ISOBMFF] encapsulation. This document requires:

  • Timecodes in stand-alone text files SHALL be relative to the period start point.
  • @presentationTimeOffset SHALL NOT be present and SHALL be ignored by clients if present.

I appreciate that this stream works on 1.3.1, and not on 1.4.0, so even if this content isn't fully spec compliant it still seems like a regression in the library.

I'm still working on plumbing the right timestamps around to get these subtitles showing up again - I haven't quite got it working yet.

icbaker avatar Aug 08 '24 07:08 icbaker

@JonWatson Ah I think the stream stopped working today - would you be able to email a new link so I can continue debugging? Thanks!

icbaker avatar Aug 08 '24 13:08 icbaker

Done! Thank you

JonWatson avatar Aug 08 '24 15:08 JonWatson

@JonWatson I've made some hacky local changes which get subtitles showing for your stream, but I need to clean them up before being able to submit (because at the moment they will break other use-cases). Unfortunately I think the stream stopped working again today - would you be able to send a new one?

icbaker avatar Aug 16 '24 12:08 icbaker

Thanks @icbaker , new manifest has been emailed

JonWatson avatar Aug 16 '24 18:08 JonWatson