public class

LegacySubtitleUtil

extends java.lang.Object

 java.lang.Object

↳androidx.media3.extractor.text.LegacySubtitleUtil

Gradle dependencies

compile group: 'androidx.media3', name: 'media3-extractor', version: '1.5.0-alpha01'

  • groupId: androidx.media3
  • artifactId: media3-extractor
  • version: 1.5.0-alpha01

Artifact androidx.media3:media3-extractor:1.5.0-alpha01 it located at Google repository (https://maven.google.com/)

Overview

Utility methods for working with legacy Subtitle objects.

Summary

Methods
public static voidtoCuesWithTiming(Subtitle subtitle, SubtitleParser.OutputOptions outputOptions, Consumer<CuesWithTiming> output)

Converts a Subtitle to a list of CuesWithTiming representing it, emitted to output.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Methods

public static void toCuesWithTiming(Subtitle subtitle, SubtitleParser.OutputOptions outputOptions, Consumer<CuesWithTiming> output)

Converts a Subtitle to a list of CuesWithTiming representing it, emitted to output.

This may only be called with empty Subtitle instances, or those where the first event is non-empty and the last event is an empty cue list.

Source

/*
 * Copyright 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.media3.extractor.text;

import androidx.media3.common.C;
import androidx.media3.common.text.Cue;
import androidx.media3.common.util.Consumer;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.extractor.text.SubtitleParser.OutputOptions;
import java.util.List;

/** Utility methods for working with legacy {@link Subtitle} objects. */
@UnstableApi
public class LegacySubtitleUtil {

  private LegacySubtitleUtil() {}

  /**
   * Converts a {@link Subtitle} to a list of {@link CuesWithTiming} representing it, emitted to
   * {@code output}.
   *
   * <p>This may only be called with empty {@link Subtitle} instances, or those where the first
   * event is non-empty and the last event is an empty cue list.
   */
  public static void toCuesWithTiming(
      Subtitle subtitle, OutputOptions outputOptions, Consumer<CuesWithTiming> output) {
    int startIndex = getStartIndex(subtitle, outputOptions.startTimeUs);
    boolean startedInMiddleOfCue = false;
    if (outputOptions.startTimeUs != C.TIME_UNSET && startIndex < subtitle.getEventTimeCount()) {
      List<Cue> cuesAtStartTime = subtitle.getCues(outputOptions.startTimeUs);
      long firstEventTimeUs = subtitle.getEventTime(startIndex);
      if (!cuesAtStartTime.isEmpty() && outputOptions.startTimeUs < firstEventTimeUs) {
        output.accept(
            new CuesWithTiming(
                cuesAtStartTime,
                outputOptions.startTimeUs,
                firstEventTimeUs - outputOptions.startTimeUs));
        startedInMiddleOfCue = true;
      }
    }
    for (int i = startIndex; i < subtitle.getEventTimeCount(); i++) {
      outputSubtitleEvent(subtitle, i, output);
    }
    if (outputOptions.outputAllCues) {
      int endIndex = startedInMiddleOfCue ? startIndex - 1 : startIndex;
      for (int i = 0; i < endIndex; i++) {
        outputSubtitleEvent(subtitle, i, output);
      }
      if (startedInMiddleOfCue) {
        output.accept(
            new CuesWithTiming(
                subtitle.getCues(outputOptions.startTimeUs),
                subtitle.getEventTime(endIndex),
                outputOptions.startTimeUs - subtitle.getEventTime(endIndex)));
      }
    }
  }

  /**
   * Returns the event index from {@code subtitle} that is equal to or after {@code startTimeUs}, or
   * zero if {@code startTimeUs == C.TIME_UNSET}, or {@code subtitle.getEventTimeCount()} if {@code
   * startTimeUs} is after all events in {@code subtitle}.
   */
  private static int getStartIndex(Subtitle subtitle, long startTimeUs) {
    if (startTimeUs == C.TIME_UNSET) {
      return 0;
    }
    int nextEventTimeIndex = subtitle.getNextEventTimeIndex(startTimeUs);
    if (nextEventTimeIndex == C.INDEX_UNSET) {
      nextEventTimeIndex = subtitle.getEventTimeCount();
    }
    if (nextEventTimeIndex > 0 && subtitle.getEventTime(nextEventTimeIndex - 1) == startTimeUs) {
      nextEventTimeIndex--;
    }
    return nextEventTimeIndex;
  }

  private static void outputSubtitleEvent(
      Subtitle subtitle, int eventIndex, Consumer<CuesWithTiming> output) {
    long startTimeUs = subtitle.getEventTime(eventIndex);
    List<Cue> cuesForThisStartTime = subtitle.getCues(startTimeUs);
    if (cuesForThisStartTime.isEmpty()) {
      // An empty cue list has already been implicitly encoded in the duration of the previous
      // sample.
      return;
    } else if (eventIndex == subtitle.getEventTimeCount() - 1) {
      // The last cue list must be empty
      throw new IllegalStateException();
    }
    // It's safe to inspect element i+1, because we already exited the loop above if
    // i == getEventTimeCount() - 1.
    long durationUs = subtitle.getEventTime(eventIndex + 1) - subtitle.getEventTime(eventIndex);
    if (durationUs > 0) {
      output.accept(new CuesWithTiming(cuesForThisStartTime, startTimeUs, durationUs));
    }
  }
}