Ejemplo n.º 1
0
 private void readEncryptionData(ExtractorInput input) throws IOException, InterruptedException {
   int bytesToSkip = (int) (fragmentRun.auxiliaryDataPosition - input.getPosition());
   checkState(bytesToSkip >= 0, "Offset to encryption data was negative.");
   input.skipFully(bytesToSkip);
   fragmentRun.fillEncryptionData(input);
   parserState = STATE_READING_SAMPLE_START;
 }
Ejemplo n.º 2
0
  /**
   * Reads an EBML variable-length integer (varint) from an {@link ExtractorInput} such that reading
   * can be resumed later if an error occurs having read only some of it.
   *
   * <p>If an value is successfully read, then the reader will automatically reset itself ready to
   * read another value.
   *
   * <p>If an {@link IOException} or {@link InterruptedException} is throw, the read can be resumed
   * later by calling this method again, passing an {@link ExtractorInput} providing data starting
   * where the previous one left off.
   *
   * @param input The {@link ExtractorInput} from which the integer should be read.
   * @param allowEndOfInput True if encountering the end of the input having read no data is
   *     allowed, and should result in {@link C#RESULT_END_OF_INPUT} being returned. False if it
   *     should be considered an error, causing an {@link EOFException} to be thrown.
   * @param removeLengthMask Removes the variable-length integer length mask from the value.
   * @param maximumAllowedLength Maximum allowed length of the variable integer to be read.
   * @return The read value, or {@link C#RESULT_END_OF_INPUT} if {@code allowEndOfStream} is true
   *     and the end of the input was encountered, or {@link C#RESULT_MAX_LENGTH_EXCEEDED} if the
   *     length of the varint exceeded maximumAllowedLength.
   * @throws IOException If an error occurs reading from the input.
   * @throws InterruptedException If the thread is interrupted.
   */
  public long readUnsignedVarint(
      ExtractorInput input,
      boolean allowEndOfInput,
      boolean removeLengthMask,
      int maximumAllowedLength)
      throws IOException, InterruptedException {
    if (state == STATE_BEGIN_READING) {
      // Read the first byte to establish the length.
      if (!input.readFully(scratch, 0, 1, allowEndOfInput)) {
        return C.RESULT_END_OF_INPUT;
      }
      int firstByte = scratch[0] & 0xFF;
      length = parseUnsignedVarintLength(firstByte);
      if (length == -1) {
        throw new IllegalStateException("No valid varint length mask found");
      }
      state = STATE_READ_CONTENTS;
    }

    if (length > maximumAllowedLength) {
      state = STATE_BEGIN_READING;
      return C.RESULT_MAX_LENGTH_EXCEEDED;
    }

    if (length != 1) {
      // Read the remaining bytes.
      input.readFully(scratch, 1, length - 1);
    }

    state = STATE_BEGIN_READING;
    return assembleVarint(scratch, length, removeLengthMask);
  }
Ejemplo n.º 3
0
  @Override
  public int read(ExtractorInput input, PositionHolder seekPosition)
      throws IOException, InterruptedException {

    if (vorbisSetup == null) {
      vorbisSetup = readSetupHeaders(input, scratch);
      ArrayList<byte[]> codecInitialisationData = new ArrayList<>();
      codecInitialisationData.clear();
      codecInitialisationData.add(vorbisSetup.idHeader.data);
      codecInitialisationData.add(vorbisSetup.setupHeaderData);

      long duration =
          input.getLength() == C.LENGTH_UNBOUNDED
              ? C.UNKNOWN_TIME_US
              : input.getLength() * 8000000 / vorbisSetup.idHeader.getApproximateBitrate();
      trackOutput.format(
          MediaFormat.createAudioFormat(
              null,
              MimeTypes.AUDIO_VORBIS,
              this.vorbisSetup.idHeader.bitrateNominal,
              OGG_MAX_SEGMENT_SIZE * 255,
              duration,
              this.vorbisSetup.idHeader.channels,
              (int) this.vorbisSetup.idHeader.sampleRate,
              codecInitialisationData,
              null));
    }
    if (oggReader.readPacket(input, scratch)) {
      // if this is an audio packet...
      if ((scratch.data[0] & 0x01) != 1) {
        // ... we need to decode the block size
        int packetBlockSize = decodeBlockSize(scratch.data[0], vorbisSetup);
        // a packet contains samples produced from overlapping the previous and current frame data
        // (https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-350001.3.2)
        int samplesInPacket =
            seenFirstAudioPacket ? (packetBlockSize + previousPacketBlockSize) / 4 : 0;
        // codec expects the number of samples appended to audio data
        appendNumberOfSamples(scratch, samplesInPacket);

        // calculate time and send audio data to codec
        long timeUs = elapsedSamples * C.MICROS_PER_SECOND / vorbisSetup.idHeader.sampleRate;
        trackOutput.sampleData(scratch, scratch.limit());
        trackOutput.sampleMetadata(timeUs, C.SAMPLE_FLAG_SYNC, scratch.limit(), 0, null);

        // update state in members for next iteration
        seenFirstAudioPacket = true;
        elapsedSamples += samplesInPacket;
        previousPacketBlockSize = packetBlockSize;
      }
      scratch.reset();
      return RESULT_CONTINUE;
    }
    return RESULT_END_OF_INPUT;
  }
Ejemplo n.º 4
0
 private void readAtomPayload(ExtractorInput input) throws IOException, InterruptedException {
   int atomPayloadSize = (int) atomSize - atomHeaderBytesRead;
   if (atomData != null) {
     input.readFully(atomData.data, Atom.HEADER_SIZE, atomPayloadSize);
     onLeafAtomRead(new LeafAtom(atomType, atomData), input.getPosition());
   } else {
     input.skipFully(atomPayloadSize);
   }
   long currentPosition = input.getPosition();
   while (!containerAtoms.isEmpty() && containerAtoms.peek().endPosition == currentPosition) {
     onContainerAtomRead(containerAtoms.pop());
   }
   enterReadingAtomHeaderState();
 }
Ejemplo n.º 5
0
 @Override
 public boolean sniff(ExtractorInput input) throws IOException, InterruptedException {
   try {
     OggReader.PageHeader header = new OggReader.PageHeader();
     OggReader.populatePageHeader(input, header, scratch, true);
     if ((header.type & 0x02) != 0x02) {
       throw new ParserException("expected page to be first page of a logical stream");
     }
     input.resetPeekPosition();
   } catch (ParserException e) {
     Log.e(TAG, e.getMessage());
     return false;
   }
   return true;
 }
Ejemplo n.º 6
0
  /**
   * Attempts to extract the next sample in the current mdat atom.
   *
   * <p>If there are no more samples in the current mdat atom then the parser state is transitioned
   * to {@link #STATE_READING_ATOM_HEADER} and {@code false} is returned.
   *
   * <p>It is possible for a sample to be extracted in part in the case that an exception is thrown.
   * In this case the method can be called again to extract the remainder of the sample.
   *
   * @param input The {@link ExtractorInput} from which to read data.
   * @return True if a sample was extracted. False otherwise.
   * @throws IOException If an error occurs reading from the input.
   * @throws InterruptedException If the thread is interrupted.
   */
  private boolean readSample(ExtractorInput input) throws IOException, InterruptedException {
    if (sampleIndex == 0) {
      int bytesToSkip = (int) (fragmentRun.dataPosition - input.getPosition());
      checkState(bytesToSkip >= 0, "Offset to sample data was negative.");
      input.skipFully(bytesToSkip);
    }

    if (sampleIndex >= fragmentRun.length) {
      int bytesToSkip = (int) (endOfMdatPosition - input.getPosition());
      checkState(bytesToSkip >= 0, "Offset to end of mdat was negative.");
      input.skipFully(bytesToSkip);
      // We've run out of samples in the current mdat atom.
      enterReadingAtomHeaderState();
      return false;
    }

    if (parserState == STATE_READING_SAMPLE_START) {
      sampleSize = fragmentRun.sampleSizeTable[sampleIndex];
      if (fragmentRun.definesEncryptionData) {
        sampleBytesWritten = appendSampleEncryptionData(fragmentRun.sampleEncryptionData);
        sampleSize += sampleBytesWritten;
      } else {
        sampleBytesWritten = 0;
      }
      sampleCurrentNalBytesRemaining = 0;
      parserState = STATE_READING_SAMPLE_CONTINUE;
    }

    if (track.nalUnitLengthFieldLength != -1) {
      // Zero the top three bytes of the array that we'll use to parse nal unit lengths, in case
      // they're only 1 or 2 bytes long.
      byte[] nalLengthData = nalLength.data;
      nalLengthData[0] = 0;
      nalLengthData[1] = 0;
      nalLengthData[2] = 0;
      int nalUnitLengthFieldLength = track.nalUnitLengthFieldLength;
      int nalUnitLengthFieldLengthDiff = 4 - track.nalUnitLengthFieldLength;
      // NAL units are length delimited, but the decoder requires start code delimited units.
      // Loop until we've written the sample to the track output, replacing length delimiters with
      // start codes as we encounter them.
      while (sampleBytesWritten < sampleSize) {
        if (sampleCurrentNalBytesRemaining == 0) {
          // Read the NAL length so that we know where we find the next one.
          input.readFully(nalLength.data, nalUnitLengthFieldLengthDiff, nalUnitLengthFieldLength);
          nalLength.setPosition(0);
          sampleCurrentNalBytesRemaining = nalLength.readUnsignedIntToInt();
          // Write a start code for the current NAL unit.
          nalStartCode.setPosition(0);
          trackOutput.sampleData(nalStartCode, 4);
          sampleBytesWritten += 4;
          sampleSize += nalUnitLengthFieldLengthDiff;
        } else {
          // Write the payload of the NAL unit.
          int writtenBytes = trackOutput.sampleData(input, sampleCurrentNalBytesRemaining, false);
          sampleBytesWritten += writtenBytes;
          sampleCurrentNalBytesRemaining -= writtenBytes;
        }
      }
    } else {
      while (sampleBytesWritten < sampleSize) {
        int writtenBytes = trackOutput.sampleData(input, sampleSize - sampleBytesWritten, false);
        sampleBytesWritten += writtenBytes;
      }
    }

    long sampleTimeUs = fragmentRun.getSamplePresentationTime(sampleIndex) * 1000L;
    int sampleFlags =
        (fragmentRun.definesEncryptionData ? C.SAMPLE_FLAG_ENCRYPTED : 0)
            | (fragmentRun.sampleIsSyncFrameTable[sampleIndex] ? C.SAMPLE_FLAG_SYNC : 0);
    int sampleDescriptionIndex = fragmentRun.header.sampleDescriptionIndex;
    byte[] encryptionKey =
        fragmentRun.definesEncryptionData
            ? track.sampleDescriptionEncryptionBoxes[sampleDescriptionIndex].keyId
            : null;
    trackOutput.sampleMetadata(sampleTimeUs, sampleFlags, sampleSize, 0, encryptionKey);

    sampleIndex++;
    parserState = STATE_READING_SAMPLE_START;
    return true;
  }
Ejemplo n.º 7
0
  private boolean readAtomHeader(ExtractorInput input) throws IOException, InterruptedException {
    if (atomHeaderBytesRead == 0) {
      // Read the standard length atom header.
      if (!input.readFully(atomHeader.data, 0, Atom.HEADER_SIZE, true)) {
        return false;
      }
      atomHeaderBytesRead = Atom.HEADER_SIZE;
      atomHeader.setPosition(0);
      atomSize = atomHeader.readUnsignedInt();
      atomType = atomHeader.readInt();
    }

    if (atomSize == Atom.LONG_SIZE_PREFIX) {
      // Read the extended atom size.
      int headerBytesRemaining = Atom.LONG_HEADER_SIZE - Atom.HEADER_SIZE;
      input.readFully(atomHeader.data, Atom.HEADER_SIZE, headerBytesRemaining);
      atomHeaderBytesRead += headerBytesRemaining;
      atomSize = atomHeader.readUnsignedLongToLong();
    }

    long atomPosition = input.getPosition() - atomHeaderBytesRead;
    if (atomType == Atom.TYPE_moof) {
      // The data positions may be updated when parsing the tfhd/trun.
      fragmentRun.auxiliaryDataPosition = atomPosition;
      fragmentRun.dataPosition = atomPosition;
    }

    if (atomType == Atom.TYPE_mdat) {
      endOfMdatPosition = atomPosition + atomSize;
      if (!haveOutputSeekMap) {
        extractorOutput.seekMap(SeekMap.UNSEEKABLE);
        haveOutputSeekMap = true;
      }
      if (fragmentRun.sampleEncryptionDataNeedsFill) {
        parserState = STATE_READING_ENCRYPTION_DATA;
      } else {
        parserState = STATE_READING_SAMPLE_START;
      }
      return true;
    }

    if (shouldParseContainerAtom(atomType)) {
      long endPosition = input.getPosition() + atomSize - Atom.HEADER_SIZE;
      containerAtoms.add(new ContainerAtom(atomType, endPosition));
      enterReadingAtomHeaderState();
    } else if (shouldParseLeafAtom(atomType)) {
      // We don't support parsing of leaf atoms that define extended atom sizes, or that have
      // lengths greater than Integer.MAX_VALUE.
      checkState(atomHeaderBytesRead == Atom.HEADER_SIZE);
      checkState(atomSize <= Integer.MAX_VALUE);
      atomData = new ParsableByteArray((int) atomSize);
      System.arraycopy(atomHeader.data, 0, atomData.data, 0, Atom.HEADER_SIZE);
      parserState = STATE_READING_ATOM_PAYLOAD;
    } else {
      // We don't support skipping of atoms that have lengths greater than Integer.MAX_VALUE.
      checkState(atomSize <= Integer.MAX_VALUE);
      atomData = null;
      parserState = STATE_READING_ATOM_PAYLOAD;
    }

    return true;
  }