Java Code Examples for com.google.android.exoplayer2.util.Log#w()

The following examples show how to use com.google.android.exoplayer2.util.Log#w() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: WebvttCueParser.java    From Telegram-FOSS with GNU General Public License v2.0 6 votes vote down vote up
private static Alignment parseTextAlignment(String s) {
  switch (s) {
    case "start":
    case "left":
      return Alignment.ALIGN_NORMAL;
    case "center":
    case "middle":
      return Alignment.ALIGN_CENTER;
    case "end":
    case "right":
      return Alignment.ALIGN_OPPOSITE;
    default:
      Log.w(TAG, "Invalid alignment value: " + s);
      return null;
  }
}
 
Example 2
Source File: MetadataUtil.java    From Telegram with GNU General Public License v2.0 6 votes vote down vote up
@Nullable
private static ApicFrame parseCoverArt(ParsableByteArray data) {
  int atomSize = data.readInt();
  int atomType = data.readInt();
  if (atomType == Atom.TYPE_data) {
    int fullVersionInt = data.readInt();
    int flags = Atom.parseFullAtomFlags(fullVersionInt);
    String mimeType = flags == 13 ? "image/jpeg" : flags == 14 ? "image/png" : null;
    if (mimeType == null) {
      Log.w(TAG, "Unrecognized cover art flags: " + flags);
      return null;
    }
    data.skipBytes(4); // empty (4)
    byte[] pictureData = new byte[atomSize - 16];
    data.readBytes(pictureData, 0, pictureData.length);
    return new ApicFrame(
        mimeType,
        /* description= */ null,
        /* pictureType= */ PICTURE_TYPE_FRONT_COVER,
        pictureData);
  }
  Log.w(TAG, "Failed to parse cover art attribute");
  return null;
}
 
Example 3
Source File: TrackEncryptionBox.java    From Telegram-FOSS with GNU General Public License v2.0 6 votes vote down vote up
@C.CryptoMode
private static int schemeToCryptoMode(@Nullable String schemeType) {
  if (schemeType == null) {
    // If unknown, assume cenc.
    return C.CRYPTO_MODE_AES_CTR;
  }
  switch (schemeType) {
    case C.CENC_TYPE_cenc:
    case C.CENC_TYPE_cens:
      return C.CRYPTO_MODE_AES_CTR;
    case C.CENC_TYPE_cbc1:
    case C.CENC_TYPE_cbcs:
      return C.CRYPTO_MODE_AES_CBC;
    default:
      Log.w(TAG, "Unsupported protection scheme type '" + schemeType + "'. Assuming AES-CTR "
          + "crypto mode.");
      return C.CRYPTO_MODE_AES_CTR;
  }
}
 
Example 4
Source File: CacheDataSink.java    From Telegram with GNU General Public License v2.0 6 votes vote down vote up
/**
 * @param cache The cache into which data should be written.
 * @param fragmentSize For requests that should be fragmented into multiple cache files, this is
 *     the maximum size of a cache file in bytes. If set to {@link C#LENGTH_UNSET} then no
 *     fragmentation will occur. Using a small value allows for finer-grained cache eviction
 *     policies, at the cost of increased overhead both on the cache implementation and the file
 *     system. Values under {@code (2 * 1024 * 1024)} are not recommended.
 * @param bufferSize The buffer size in bytes for writing to a cache file. A zero or negative
 *     value disables buffering.
 */
public CacheDataSink(Cache cache, long fragmentSize, int bufferSize) {
  Assertions.checkState(
      fragmentSize > 0 || fragmentSize == C.LENGTH_UNSET,
      "fragmentSize must be positive or C.LENGTH_UNSET.");
  if (fragmentSize != C.LENGTH_UNSET && fragmentSize < MIN_RECOMMENDED_FRAGMENT_SIZE) {
    Log.w(
        TAG,
        "fragmentSize is below the minimum recommended value of "
            + MIN_RECOMMENDED_FRAGMENT_SIZE
            + ". This may cause poor cache performance.");
  }
  this.cache = Assertions.checkNotNull(cache);
  this.fragmentSize = fragmentSize == C.LENGTH_UNSET ? Long.MAX_VALUE : fragmentSize;
  this.bufferSize = bufferSize;
}
 
Example 5
Source File: ExoPlayerImplInternal.java    From MediaSDK with Apache License 2.0 5 votes vote down vote up
@Override
public synchronized void sendMessage(PlayerMessage message) {
  if (released || !internalPlaybackThread.isAlive()) {
    Log.w(TAG, "Ignoring messages sent after release.");
    message.markAsProcessed(/* isDelivered= */ false);
    return;
  }
  handler.obtainMessage(MSG_SEND_MESSAGE, message).sendToTarget();
}
 
Example 6
Source File: FfmpegLibrary.java    From Telegram-FOSS with GNU General Public License v2.0 5 votes vote down vote up
/**
 * Returns whether the underlying library supports the specified MIME type.
 *
 * @param mimeType The MIME type to check.
 * @param encoding The PCM encoding for raw audio.
 */
public static boolean supportsFormat(String mimeType, @C.PcmEncoding int encoding) {
  String codecName = getCodecName(mimeType, encoding);
  if (codecName == null) {
    return false;
  }
  if (!ffmpegHasDecoder(codecName)) {
    Log.w(TAG, "No " + codecName + " decoder available. Check the FFmpeg build configuration.");
    return false;
  }
  return true;
}
 
Example 7
Source File: WakeLockManager.java    From MediaSDK with Apache License 2.0 5 votes vote down vote up
/**
 * Sets whether to enable the acquiring and releasing of the {@link WakeLock}.
 *
 * <p>By default, wake lock handling is not enabled. Enabling this will acquire the wake lock if
 * necessary. Disabling this will release the wake lock if it is held.
 *
 * @param enabled True if the player should handle a {@link WakeLock}, false otherwise. Please
 *     note that enabling this requires the {@link android.Manifest.permission#WAKE_LOCK}
 *     permission.
 */
public void setEnabled(boolean enabled) {
  if (enabled) {
    if (wakeLock == null) {
      if (powerManager == null) {
        Log.w(TAG, "PowerManager was null, therefore the WakeLock was not created.");
        return;
      }
      wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK_TAG);
    }
  }

  this.enabled = enabled;
  updateWakeLock();
}
 
Example 8
Source File: MatroskaExtractor.java    From MediaSDK with Apache License 2.0 5 votes vote down vote up
/**
 * Builds initialization data for a {@link Format} from FourCC codec private data.
 *
 * @return The codec mime type and initialization data. If the compression type is not supported
 *     then the mime type is set to {@link MimeTypes#VIDEO_UNKNOWN} and the initialization data
 *     is {@code null}.
 * @throws ParserException If the initialization data could not be built.
 */
private static Pair<String, List<byte[]>> parseFourCcPrivate(ParsableByteArray buffer)
    throws ParserException {
  try {
    buffer.skipBytes(16); // size(4), width(4), height(4), planes(2), bitcount(2).
    long compression = buffer.readLittleEndianUnsignedInt();
    if (compression == FOURCC_COMPRESSION_DIVX) {
      return new Pair<>(MimeTypes.VIDEO_DIVX, null);
    } else if (compression == FOURCC_COMPRESSION_H263) {
      return new Pair<>(MimeTypes.VIDEO_H263, null);
    } else if (compression == FOURCC_COMPRESSION_VC1) {
      // Search for the initialization data from the end of the BITMAPINFOHEADER. The last 20
      // bytes of which are: sizeImage(4), xPel/m (4), yPel/m (4), clrUsed(4), clrImportant(4).
      int startOffset = buffer.getPosition() + 20;
      byte[] bufferData = buffer.data;
      for (int offset = startOffset; offset < bufferData.length - 4; offset++) {
        if (bufferData[offset] == 0x00
            && bufferData[offset + 1] == 0x00
            && bufferData[offset + 2] == 0x01
            && bufferData[offset + 3] == 0x0F) {
          // We've found the initialization data.
          byte[] initializationData = Arrays.copyOfRange(bufferData, offset, bufferData.length);
          return new Pair<>(MimeTypes.VIDEO_VC1, Collections.singletonList(initializationData));
        }
      }
      throw new ParserException("Failed to find FourCC VC1 initialization data");
    }
  } catch (ArrayIndexOutOfBoundsException e) {
    throw new ParserException("Error parsing FourCC private data");
  }

  Log.w(TAG, "Unknown FourCC. Setting mimeType to " + MimeTypes.VIDEO_UNKNOWN);
  return new Pair<>(MimeTypes.VIDEO_UNKNOWN, null);
}
 
Example 9
Source File: WavHeaderReader.java    From Telegram-FOSS with GNU General Public License v2.0 5 votes vote down vote up
/**
 * Skips to the data in the given WAV input stream. After calling, the input stream's position
 * will point to the start of sample data in the WAV, and the data bounds of the provided {@link
 * WavHeader} will have been set.
 *
 * <p>If an exception is thrown, the input position will be left pointing to a chunk header and
 * the bounds of the provided {@link WavHeader} will not have been set.
 *
 * @param input Input stream to skip to the data chunk in. Its peek position must be pointing to a
 *     valid chunk header.
 * @param wavHeader WAV header to populate with data bounds.
 * @throws ParserException If an error occurs parsing chunks.
 * @throws IOException If reading from the input fails.
 * @throws InterruptedException If interrupted while reading from input.
 */
public static void skipToData(ExtractorInput input, WavHeader wavHeader)
    throws IOException, InterruptedException {
  Assertions.checkNotNull(input);
  Assertions.checkNotNull(wavHeader);

  // Make sure the peek position is set to the read position before we peek the first header.
  input.resetPeekPosition();

  ParsableByteArray scratch = new ParsableByteArray(ChunkHeader.SIZE_IN_BYTES);
  // Skip all chunks until we hit the data header.
  ChunkHeader chunkHeader = ChunkHeader.peek(input, scratch);
  while (chunkHeader.id != WavUtil.DATA_FOURCC) {
    if (chunkHeader.id != WavUtil.RIFF_FOURCC && chunkHeader.id != WavUtil.FMT_FOURCC) {
      Log.w(TAG, "Ignoring unknown WAV chunk: " + chunkHeader.id);
    }
    long bytesToSkip = ChunkHeader.SIZE_IN_BYTES + chunkHeader.size;
    // Override size of RIFF chunk, since it describes its size as the entire file.
    if (chunkHeader.id == WavUtil.RIFF_FOURCC) {
      bytesToSkip = ChunkHeader.SIZE_IN_BYTES + 4;
    }
    if (bytesToSkip > Integer.MAX_VALUE) {
      throw new ParserException("Chunk is too large (~2GB+) to skip; id: " + chunkHeader.id);
    }
    input.skipFully((int) bytesToSkip);
    chunkHeader = ChunkHeader.peek(input, scratch);
  }
  // Skip past the "data" header.
  input.skipFully(ChunkHeader.SIZE_IN_BYTES);

  int dataStartPosition = (int) input.getPosition();
  long dataEndPosition = dataStartPosition + chunkHeader.size;
  long inputLength = input.getLength();
  if (inputLength != C.LENGTH_UNSET && dataEndPosition > inputLength) {
    Log.w(TAG, "Data exceeds input length: " + dataEndPosition + ", " + inputLength);
    dataEndPosition = inputLength;
  }
  wavHeader.setDataBounds(dataStartPosition, dataEndPosition);
}
 
Example 10
Source File: MetadataUtil.java    From MediaSDK with Apache License 2.0 5 votes vote down vote up
@Nullable
private static CommentFrame parseCommentAttribute(int type, ParsableByteArray data) {
  int atomSize = data.readInt();
  int atomType = data.readInt();
  if (atomType == Atom.TYPE_data) {
    data.skipBytes(8); // version (1), flags (3), empty (4)
    String value = data.readNullTerminatedString(atomSize - 16);
    return new CommentFrame(LANGUAGE_UNDEFINED, value, value);
  }
  Log.w(TAG, "Failed to parse comment attribute: " + Atom.getAtomTypeString(type));
  return null;
}
 
Example 11
Source File: MetadataUtil.java    From MediaSDK with Apache License 2.0 5 votes vote down vote up
@Nullable
private static TextInformationFrame parseTextAttribute(
    int type, String id, ParsableByteArray data) {
  int atomSize = data.readInt();
  int atomType = data.readInt();
  if (atomType == Atom.TYPE_data) {
    data.skipBytes(8); // version (1), flags (3), empty (4)
    String value = data.readNullTerminatedString(atomSize - 16);
    return new TextInformationFrame(id, /* description= */ null, value);
  }
  Log.w(TAG, "Failed to parse text attribute: " + Atom.getAtomTypeString(type));
  return null;
}
 
Example 12
Source File: TtmlDecoder.java    From Telegram-FOSS with GNU General Public License v2.0 5 votes vote down vote up
private static void parseFontSize(String expression, TtmlStyle out) throws
    SubtitleDecoderException {
  String[] expressions = Util.split(expression, "\\s+");
  Matcher matcher;
  if (expressions.length == 1) {
    matcher = FONT_SIZE.matcher(expression);
  } else if (expressions.length == 2){
    matcher = FONT_SIZE.matcher(expressions[1]);
    Log.w(TAG, "Multiple values in fontSize attribute. Picking the second value for vertical font"
        + " size and ignoring the first.");
  } else {
    throw new SubtitleDecoderException("Invalid number of entries for fontSize: "
        + expressions.length + ".");
  }

  if (matcher.matches()) {
    String unit = matcher.group(3);
    switch (unit) {
      case "px":
        out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_PIXEL);
        break;
      case "em":
        out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_EM);
        break;
      case "%":
        out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_PERCENT);
        break;
      default:
        throw new SubtitleDecoderException("Invalid unit for fontSize: '" + unit + "'.");
    }
    out.setFontSize(Float.valueOf(matcher.group(1)));
  } else {
    throw new SubtitleDecoderException("Invalid expression for fontSize: '" + expression + "'.");
  }
}
 
Example 13
Source File: MetadataUtil.java    From Telegram-FOSS with GNU General Public License v2.0 5 votes vote down vote up
private static int parseUint8AttributeValue(ParsableByteArray data) {
  data.skipBytes(4); // atomSize
  int atomType = data.readInt();
  if (atomType == Atom.TYPE_data) {
    data.skipBytes(8); // version (1), flags (3), empty (4)
    return data.readUnsignedByte();
  }
  Log.w(TAG, "Failed to parse uint8 attribute value");
  return -1;
}
 
Example 14
Source File: MediaCodecAudioRenderer.java    From Telegram with GNU General Public License v2.0 5 votes vote down vote up
@Override
protected void onStreamChanged(Format[] formats, long offsetUs) throws ExoPlaybackException {
  super.onStreamChanged(formats, offsetUs);
  if (lastInputTimeUs != C.TIME_UNSET) {
    if (pendingStreamChangeCount == pendingStreamChangeTimesUs.length) {
      Log.w(
          TAG,
          "Too many stream changes, so dropping change at "
              + pendingStreamChangeTimesUs[pendingStreamChangeCount - 1]);
    } else {
      pendingStreamChangeCount++;
    }
    pendingStreamChangeTimesUs[pendingStreamChangeCount - 1] = lastInputTimeUs;
  }
}
 
Example 15
Source File: SimpleExoPlayer.java    From MediaSDK with Apache License 2.0 5 votes vote down vote up
private void removeSurfaceCallbacks() {
  if (textureView != null) {
    if (textureView.getSurfaceTextureListener() != componentListener) {
      Log.w(TAG, "SurfaceTextureListener already unset or replaced.");
    } else {
      textureView.setSurfaceTextureListener(null);
    }
    textureView = null;
  }
  if (surfaceHolder != null) {
    surfaceHolder.removeCallback(componentListener);
    surfaceHolder = null;
  }
}
 
Example 16
Source File: XingSeeker.java    From Telegram-FOSS with GNU General Public License v2.0 5 votes vote down vote up
/**
 * Returns a {@link XingSeeker} for seeking in the stream, if required information is present.
 * Returns {@code null} if not. On returning, {@code frame}'s position is not specified so the
 * caller should reset it.
 *
 * @param inputLength The length of the stream in bytes, or {@link C#LENGTH_UNSET} if unknown.
 * @param position The position of the start of this frame in the stream.
 * @param mpegAudioHeader The MPEG audio header associated with the frame.
 * @param frame The data in this audio frame, with its position set to immediately after the
 *     'Xing' or 'Info' tag.
 * @return A {@link XingSeeker} for seeking in the stream, or {@code null} if the required
 *     information is not present.
 */
public static @Nullable XingSeeker create(
    long inputLength, long position, MpegAudioHeader mpegAudioHeader, ParsableByteArray frame) {
  int samplesPerFrame = mpegAudioHeader.samplesPerFrame;
  int sampleRate = mpegAudioHeader.sampleRate;

  int flags = frame.readInt();
  int frameCount;
  if ((flags & 0x01) != 0x01 || (frameCount = frame.readUnsignedIntToInt()) == 0) {
    // If the frame count is missing/invalid, the header can't be used to determine the duration.
    return null;
  }
  long durationUs = Util.scaleLargeTimestamp(frameCount, samplesPerFrame * C.MICROS_PER_SECOND,
      sampleRate);
  if ((flags & 0x06) != 0x06) {
    // If the size in bytes or table of contents is missing, the stream is not seekable.
    return new XingSeeker(position, mpegAudioHeader.frameSize, durationUs);
  }

  long dataSize = frame.readUnsignedIntToInt();
  long[] tableOfContents = new long[100];
  for (int i = 0; i < 100; i++) {
    tableOfContents[i] = frame.readUnsignedByte();
  }

  // TODO: Handle encoder delay and padding in 3 bytes offset by xingBase + 213 bytes:
  // delay = (frame.readUnsignedByte() << 4) + (frame.readUnsignedByte() >> 4);
  // padding = ((frame.readUnsignedByte() & 0x0F) << 8) + frame.readUnsignedByte();

  if (inputLength != C.LENGTH_UNSET && inputLength != position + dataSize) {
    Log.w(TAG, "XING data size mismatch: " + inputLength + ", " + (position + dataSize));
  }
  return new XingSeeker(
      position, mpegAudioHeader.frameSize, durationUs, dataSize, tableOfContents);
}
 
Example 17
Source File: AudioDecoder.java    From DanDanPlayForAndroid with MIT License 5 votes vote down vote up
public static boolean supportsFormat(String mimeType, @C.PcmEncoding int encoding) {
  String codecName = getCodecName(mimeType, encoding);
  if (codecName == null) {
    return false;
  }
  if (!ffmpegHasDecoder(codecName)) {
    Log.w(TAG, "No " + codecName + " decoder available. Check the FFmpeg build configuration.");
    return false;
  }
  return true;
}
 
Example 18
Source File: VbriSeeker.java    From Telegram with GNU General Public License v2.0 4 votes vote down vote up
/**
 * Returns a {@link VbriSeeker} for seeking in the stream, if required information is present.
 * Returns {@code null} if not. On returning, {@code frame}'s position is not specified so the
 * caller should reset it.
 *
 * @param inputLength The length of the stream in bytes, or {@link C#LENGTH_UNSET} if unknown.
 * @param position The position of the start of this frame in the stream.
 * @param mpegAudioHeader The MPEG audio header associated with the frame.
 * @param frame The data in this audio frame, with its position set to immediately after the
 *     'VBRI' tag.
 * @return A {@link VbriSeeker} for seeking in the stream, or {@code null} if the required
 *     information is not present.
 */
public static @Nullable VbriSeeker create(
    long inputLength, long position, MpegAudioHeader mpegAudioHeader, ParsableByteArray frame) {
  frame.skipBytes(10);
  int numFrames = frame.readInt();
  if (numFrames <= 0) {
    return null;
  }
  int sampleRate = mpegAudioHeader.sampleRate;
  long durationUs = Util.scaleLargeTimestamp(numFrames,
      C.MICROS_PER_SECOND * (sampleRate >= 32000 ? 1152 : 576), sampleRate);
  int entryCount = frame.readUnsignedShort();
  int scale = frame.readUnsignedShort();
  int entrySize = frame.readUnsignedShort();
  frame.skipBytes(2);

  long minPosition = position + mpegAudioHeader.frameSize;
  // Read table of contents entries.
  long[] timesUs = new long[entryCount];
  long[] positions = new long[entryCount];
  for (int index = 0; index < entryCount; index++) {
    timesUs[index] = (index * durationUs) / entryCount;
    // Ensure positions do not fall within the frame containing the VBRI header. This constraint
    // will normally only apply to the first entry in the table.
    positions[index] = Math.max(position, minPosition);
    int segmentSize;
    switch (entrySize) {
      case 1:
        segmentSize = frame.readUnsignedByte();
        break;
      case 2:
        segmentSize = frame.readUnsignedShort();
        break;
      case 3:
        segmentSize = frame.readUnsignedInt24();
        break;
      case 4:
        segmentSize = frame.readUnsignedIntToInt();
        break;
      default:
        return null;
    }
    position += segmentSize * scale;
  }
  if (inputLength != C.LENGTH_UNSET && inputLength != position) {
    Log.w(TAG, "VBRI data size mismatch: " + inputLength + ", " + position);
  }
  return new VbriSeeker(timesUs, positions, durationUs, /* dataEndPosition= */ position);
}
 
Example 19
Source File: MediaCodecVideoRenderer.java    From Telegram-FOSS with GNU General Public License v2.0 4 votes vote down vote up
/**
 * Returns {@link CodecMaxValues} suitable for configuring a codec for {@code format} in a way
 * that will allow possible adaptation to other compatible formats in {@code streamFormats}.
 *
 * @param codecInfo Information about the {@link MediaCodec} being configured.
 * @param format The format for which the codec is being configured.
 * @param streamFormats The possible stream formats.
 * @return Suitable {@link CodecMaxValues}.
 */
protected CodecMaxValues getCodecMaxValues(
    MediaCodecInfo codecInfo, Format format, Format[] streamFormats) {
  int maxWidth = format.width;
  int maxHeight = format.height;
  int maxInputSize = getMaxInputSize(codecInfo, format);
  if (streamFormats.length == 1) {
    // The single entry in streamFormats must correspond to the format for which the codec is
    // being configured.
    if (maxInputSize != Format.NO_VALUE) {
      int codecMaxInputSize =
          getCodecMaxInputSize(codecInfo, format.sampleMimeType, format.width, format.height);
      if (codecMaxInputSize != Format.NO_VALUE) {
        // Scale up the initial video decoder maximum input size so playlist item transitions with
        // small increases in maximum sample size don't require reinitialization. This only makes
        // a difference if the exact maximum sample sizes are known from the container.
        int scaledMaxInputSize =
            (int) (maxInputSize * INITIAL_FORMAT_MAX_INPUT_SIZE_SCALE_FACTOR);
        // Avoid exceeding the maximum expected for the codec.
        maxInputSize = Math.min(scaledMaxInputSize, codecMaxInputSize);
      }
    }
    return new CodecMaxValues(maxWidth, maxHeight, maxInputSize);
  }
  boolean haveUnknownDimensions = false;
  for (Format streamFormat : streamFormats) {
    if (codecInfo.isSeamlessAdaptationSupported(
        format, streamFormat, /* isNewFormatComplete= */ false)) {
      haveUnknownDimensions |=
          (streamFormat.width == Format.NO_VALUE || streamFormat.height == Format.NO_VALUE);
      maxWidth = Math.max(maxWidth, streamFormat.width);
      maxHeight = Math.max(maxHeight, streamFormat.height);
      maxInputSize = Math.max(maxInputSize, getMaxInputSize(codecInfo, streamFormat));
    }
  }
  if (haveUnknownDimensions) {
    Log.w(TAG, "Resolutions unknown. Codec max resolution: " + maxWidth + "x" + maxHeight);
    Point codecMaxSize = getCodecMaxSize(codecInfo, format);
    if (codecMaxSize != null) {
      maxWidth = Math.max(maxWidth, codecMaxSize.x);
      maxHeight = Math.max(maxHeight, codecMaxSize.y);
      maxInputSize =
          Math.max(
              maxInputSize,
              getCodecMaxInputSize(codecInfo, format.sampleMimeType, maxWidth, maxHeight));
      Log.w(TAG, "Codec max resolution adjusted to: " + maxWidth + "x" + maxHeight);
    }
  }
  return new CodecMaxValues(maxWidth, maxHeight, maxInputSize);
}
 
Example 20
Source File: SsaDecoder.java    From Telegram with GNU General Public License v2.0 4 votes vote down vote up
/**
 * Parses a dialogue line.
 *
 * @param dialogueLine The line to parse.
 * @param cues A list to which parsed cues will be added.
 * @param cueTimesUs An array to which parsed cue timestamps will be added.
 */
private void parseDialogueLine(String dialogueLine, List<Cue> cues, LongArray cueTimesUs) {
  if (formatKeyCount == 0) {
    Log.w(TAG, "Skipping dialogue line before complete format: " + dialogueLine);
    return;
  }

  String[] lineValues = dialogueLine.substring(DIALOGUE_LINE_PREFIX.length())
      .split(",", formatKeyCount);
  if (lineValues.length != formatKeyCount) {
    Log.w(TAG, "Skipping dialogue line with fewer columns than format: " + dialogueLine);
    return;
  }

  long startTimeUs = SsaDecoder.parseTimecodeUs(lineValues[formatStartIndex]);
  if (startTimeUs == C.TIME_UNSET) {
    Log.w(TAG, "Skipping invalid timing: " + dialogueLine);
    return;
  }

  long endTimeUs = C.TIME_UNSET;
  String endTimeString = lineValues[formatEndIndex];
  if (!endTimeString.trim().isEmpty()) {
    endTimeUs = SsaDecoder.parseTimecodeUs(endTimeString);
    if (endTimeUs == C.TIME_UNSET) {
      Log.w(TAG, "Skipping invalid timing: " + dialogueLine);
      return;
    }
  }

  String text = lineValues[formatTextIndex]
      .replaceAll("\\{.*?\\}", "")
      .replaceAll("\\\\N", "\n")
      .replaceAll("\\\\n", "\n");
  cues.add(new Cue(text));
  cueTimesUs.add(startTimeUs);
  if (endTimeUs != C.TIME_UNSET) {
    cues.add(Cue.EMPTY);
    cueTimesUs.add(endTimeUs);
  }
}