Search in sources :

Example 1 with TimestampAdjuster

use of com.google.android.exoplayer2.util.TimestampAdjuster in project ExoPlayer by google.

the class FragmentedMp4Extractor method readSample.

/**
   * 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 Whether a sample was extracted.
   * @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 (parserState == STATE_READING_SAMPLE_START) {
        if (currentTrackBundle == null) {
            TrackBundle currentTrackBundle = getNextFragmentRun(trackBundles);
            if (currentTrackBundle == null) {
                // We've run out of samples in the current mdat. Discard any trailing data and prepare to
                // read the header of the next atom.
                int bytesToSkip = (int) (endOfMdatPosition - input.getPosition());
                if (bytesToSkip < 0) {
                    throw new ParserException("Offset to end of mdat was negative.");
                }
                input.skipFully(bytesToSkip);
                enterReadingAtomHeaderState();
                return false;
            }
            long nextDataPosition = currentTrackBundle.fragment.trunDataPosition[currentTrackBundle.currentTrackRunIndex];
            // We skip bytes preceding the next sample to read.
            int bytesToSkip = (int) (nextDataPosition - input.getPosition());
            if (bytesToSkip < 0) {
                // Assume the sample data must be contiguous in the mdat with no preceding data.
                Log.w(TAG, "Ignoring negative offset to sample data.");
                bytesToSkip = 0;
            }
            input.skipFully(bytesToSkip);
            this.currentTrackBundle = currentTrackBundle;
        }
        sampleSize = currentTrackBundle.fragment.sampleSizeTable[currentTrackBundle.currentSampleIndex];
        if (currentTrackBundle.fragment.definesEncryptionData) {
            sampleBytesWritten = appendSampleEncryptionData(currentTrackBundle);
            sampleSize += sampleBytesWritten;
        } else {
            sampleBytesWritten = 0;
        }
        if (currentTrackBundle.track.sampleTransformation == Track.TRANSFORMATION_CEA608_CDAT) {
            sampleSize -= Atom.HEADER_SIZE;
            input.skipFully(Atom.HEADER_SIZE);
        }
        parserState = STATE_READING_SAMPLE_CONTINUE;
        sampleCurrentNalBytesRemaining = 0;
    }
    TrackFragment fragment = currentTrackBundle.fragment;
    Track track = currentTrackBundle.track;
    TrackOutput output = currentTrackBundle.output;
    int sampleIndex = currentTrackBundle.currentSampleIndex;
    if (track.nalUnitLengthFieldLength != 0) {
        // Zero the top three bytes of the array that we'll use to decode nal unit lengths, in case
        // they're only 1 or 2 bytes long.
        byte[] nalPrefixData = nalPrefix.data;
        nalPrefixData[0] = 0;
        nalPrefixData[1] = 0;
        nalPrefixData[2] = 0;
        int nalUnitPrefixLength = track.nalUnitLengthFieldLength + 1;
        int nalUnitLengthFieldLengthDiff = 4 - track.nalUnitLengthFieldLength;
        // 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, and its type.
                input.readFully(nalPrefixData, nalUnitLengthFieldLengthDiff, nalUnitPrefixLength);
                nalPrefix.setPosition(0);
                sampleCurrentNalBytesRemaining = nalPrefix.readUnsignedIntToInt() - 1;
                // Write a start code for the current NAL unit.
                nalStartCode.setPosition(0);
                output.sampleData(nalStartCode, 4);
                // Write the NAL unit type byte.
                output.sampleData(nalPrefix, 1);
                processSeiNalUnitPayload = cea608TrackOutputs != null && NalUnitUtil.isNalUnitSei(track.format.sampleMimeType, nalPrefixData[4]);
                sampleBytesWritten += 5;
                sampleSize += nalUnitLengthFieldLengthDiff;
            } else {
                int writtenBytes;
                if (processSeiNalUnitPayload) {
                    // Read and write the payload of the SEI NAL unit.
                    nalBuffer.reset(sampleCurrentNalBytesRemaining);
                    input.readFully(nalBuffer.data, 0, sampleCurrentNalBytesRemaining);
                    output.sampleData(nalBuffer, sampleCurrentNalBytesRemaining);
                    writtenBytes = sampleCurrentNalBytesRemaining;
                    // Unescape and process the SEI NAL unit.
                    int unescapedLength = NalUnitUtil.unescapeStream(nalBuffer.data, nalBuffer.limit());
                    // If the format is H.265/HEVC the NAL unit header has two bytes so skip one more byte.
                    nalBuffer.setPosition(MimeTypes.VIDEO_H265.equals(track.format.sampleMimeType) ? 1 : 0);
                    nalBuffer.setLimit(unescapedLength);
                    CeaUtil.consume(fragment.getSamplePresentationTime(sampleIndex) * 1000L, nalBuffer, cea608TrackOutputs);
                } else {
                    // Write the payload of the NAL unit.
                    writtenBytes = output.sampleData(input, sampleCurrentNalBytesRemaining, false);
                }
                sampleBytesWritten += writtenBytes;
                sampleCurrentNalBytesRemaining -= writtenBytes;
            }
        }
    } else {
        while (sampleBytesWritten < sampleSize) {
            int writtenBytes = output.sampleData(input, sampleSize - sampleBytesWritten, false);
            sampleBytesWritten += writtenBytes;
        }
    }
    long sampleTimeUs = fragment.getSamplePresentationTime(sampleIndex) * 1000L;
    @C.BufferFlags int sampleFlags = (fragment.definesEncryptionData ? C.BUFFER_FLAG_ENCRYPTED : 0) | (fragment.sampleIsSyncFrameTable[sampleIndex] ? C.BUFFER_FLAG_KEY_FRAME : 0);
    int sampleDescriptionIndex = fragment.header.sampleDescriptionIndex;
    byte[] encryptionKey = null;
    if (fragment.definesEncryptionData) {
        encryptionKey = fragment.trackEncryptionBox != null ? fragment.trackEncryptionBox.keyId : track.sampleDescriptionEncryptionBoxes[sampleDescriptionIndex].keyId;
    }
    if (timestampAdjuster != null) {
        sampleTimeUs = timestampAdjuster.adjustSampleTimestamp(sampleTimeUs);
    }
    output.sampleMetadata(sampleTimeUs, sampleFlags, sampleSize, 0, encryptionKey);
    while (!pendingMetadataSampleInfos.isEmpty()) {
        MetadataSampleInfo sampleInfo = pendingMetadataSampleInfos.removeFirst();
        pendingMetadataSampleBytes -= sampleInfo.size;
        eventMessageTrackOutput.sampleMetadata(sampleTimeUs + sampleInfo.presentationTimeDeltaUs, C.BUFFER_FLAG_KEY_FRAME, sampleInfo.size, pendingMetadataSampleBytes, null);
    }
    currentTrackBundle.currentSampleIndex++;
    currentTrackBundle.currentSampleInTrackRun++;
    if (currentTrackBundle.currentSampleInTrackRun == fragment.trunLength[currentTrackBundle.currentTrackRunIndex]) {
        currentTrackBundle.currentTrackRunIndex++;
        currentTrackBundle.currentSampleInTrackRun = 0;
        currentTrackBundle = null;
    }
    parserState = STATE_READING_SAMPLE_START;
    return true;
}
Also used : ParserException(com.google.android.exoplayer2.ParserException) TrackOutput(com.google.android.exoplayer2.extractor.TrackOutput)

Example 2 with TimestampAdjuster

use of com.google.android.exoplayer2.util.TimestampAdjuster in project ExoPlayer by google.

the class SpliceInfoDecoder method decode.

@Override
public Metadata decode(MetadataInputBuffer inputBuffer) throws MetadataDecoderException {
    // Internal timestamps adjustment.
    if (timestampAdjuster == null || inputBuffer.subsampleOffsetUs != timestampAdjuster.getTimestampOffsetUs()) {
        timestampAdjuster = new TimestampAdjuster(inputBuffer.timeUs);
        timestampAdjuster.adjustSampleTimestamp(inputBuffer.timeUs - inputBuffer.subsampleOffsetUs);
    }
    ByteBuffer buffer = inputBuffer.data;
    byte[] data = buffer.array();
    int size = buffer.limit();
    sectionData.reset(data, size);
    sectionHeader.reset(data, size);
    // table_id(8), section_syntax_indicator(1), private_indicator(1), reserved(2),
    // section_length(12), protocol_version(8), encrypted_packet(1), encryption_algorithm(6).
    sectionHeader.skipBits(39);
    long ptsAdjustment = sectionHeader.readBits(1);
    ptsAdjustment = (ptsAdjustment << 32) | sectionHeader.readBits(32);
    // cw_index(8), tier(12).
    sectionHeader.skipBits(20);
    int spliceCommandLength = sectionHeader.readBits(12);
    int spliceCommandType = sectionHeader.readBits(8);
    SpliceCommand command = null;
    // Go to the start of the command by skipping all fields up to command_type.
    sectionData.skipBytes(14);
    switch(spliceCommandType) {
        case TYPE_SPLICE_NULL:
            command = new SpliceNullCommand();
            break;
        case TYPE_SPLICE_SCHEDULE:
            command = SpliceScheduleCommand.parseFromSection(sectionData);
            break;
        case TYPE_SPLICE_INSERT:
            command = SpliceInsertCommand.parseFromSection(sectionData, ptsAdjustment, timestampAdjuster);
            break;
        case TYPE_TIME_SIGNAL:
            command = TimeSignalCommand.parseFromSection(sectionData, ptsAdjustment, timestampAdjuster);
            break;
        case TYPE_PRIVATE_COMMAND:
            command = PrivateCommand.parseFromSection(sectionData, spliceCommandLength, ptsAdjustment);
            break;
        default:
            // Do nothing.
            break;
    }
    return command == null ? new Metadata() : new Metadata(command);
}
Also used : Metadata(com.google.android.exoplayer2.metadata.Metadata) TimestampAdjuster(com.google.android.exoplayer2.util.TimestampAdjuster) ByteBuffer(java.nio.ByteBuffer)

Example 3 with TimestampAdjuster

use of com.google.android.exoplayer2.util.TimestampAdjuster in project ExoPlayer by google.

the class PsExtractor method read.

@Override
public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException {
    // First peek and check what type of start code is next.
    if (!input.peekFully(psPacketBuffer.data, 0, 4, true)) {
        return RESULT_END_OF_INPUT;
    }
    psPacketBuffer.setPosition(0);
    int nextStartCode = psPacketBuffer.readInt();
    if (nextStartCode == MPEG_PROGRAM_END_CODE) {
        return RESULT_END_OF_INPUT;
    } else if (nextStartCode == PACK_START_CODE) {
        // Now peek the rest of the pack_header.
        input.peekFully(psPacketBuffer.data, 0, 10);
        // We only care about the pack_stuffing_length in here, skip the first 77 bits.
        psPacketBuffer.setPosition(9);
        // Last 3 bits is the length.
        int packStuffingLength = psPacketBuffer.readUnsignedByte() & 0x07;
        // Now skip the stuffing and the pack header.
        input.skipFully(packStuffingLength + 14);
        return RESULT_CONTINUE;
    } else if (nextStartCode == SYSTEM_HEADER_START_CODE) {
        // We just skip all this, but we need to get the length first.
        input.peekFully(psPacketBuffer.data, 0, 2);
        // Length is the next 2 bytes.
        psPacketBuffer.setPosition(0);
        int systemHeaderLength = psPacketBuffer.readUnsignedShort();
        input.skipFully(systemHeaderLength + 6);
        return RESULT_CONTINUE;
    } else if (((nextStartCode & 0xFFFFFF00) >> 8) != PACKET_START_CODE_PREFIX) {
        // Skip bytes until we see a valid start code again.
        input.skipFully(1);
        return RESULT_CONTINUE;
    }
    // We're at the start of a regular PES packet now.
    // Get the stream ID off the last byte of the start code.
    int streamId = nextStartCode & 0xFF;
    // Check to see if we have this one in our map yet, and if not, then add it.
    PesReader payloadReader = psPayloadReaders.get(streamId);
    if (!foundAllTracks) {
        if (payloadReader == null) {
            ElementaryStreamReader elementaryStreamReader = null;
            if (!foundAudioTrack && streamId == PRIVATE_STREAM_1) {
                // Private stream, used for AC3 audio.
                // NOTE: This may need further parsing to determine if its DTS, but that's likely only
                // valid for DVDs.
                elementaryStreamReader = new Ac3Reader();
                foundAudioTrack = true;
            } else if (!foundAudioTrack && (streamId & AUDIO_STREAM_MASK) == AUDIO_STREAM) {
                elementaryStreamReader = new MpegAudioReader();
                foundAudioTrack = true;
            } else if (!foundVideoTrack && (streamId & VIDEO_STREAM_MASK) == VIDEO_STREAM) {
                elementaryStreamReader = new H262Reader();
                foundVideoTrack = true;
            }
            if (elementaryStreamReader != null) {
                TrackIdGenerator idGenerator = new TrackIdGenerator(streamId, MAX_STREAM_ID_PLUS_ONE);
                elementaryStreamReader.createTracks(output, idGenerator);
                payloadReader = new PesReader(elementaryStreamReader, timestampAdjuster);
                psPayloadReaders.put(streamId, payloadReader);
            }
        }
        if ((foundAudioTrack && foundVideoTrack) || input.getPosition() > MAX_SEARCH_LENGTH) {
            foundAllTracks = true;
            output.endTracks();
        }
    }
    // The next 2 bytes are the length. Once we have that we can consume the complete packet.
    input.peekFully(psPacketBuffer.data, 0, 2);
    psPacketBuffer.setPosition(0);
    int payloadLength = psPacketBuffer.readUnsignedShort();
    int pesLength = payloadLength + 6;
    if (payloadReader == null) {
        // Just skip this data.
        input.skipFully(pesLength);
    } else {
        psPacketBuffer.reset(pesLength);
        // Read the whole packet and the header for consumption.
        input.readFully(psPacketBuffer.data, 0, pesLength);
        psPacketBuffer.setPosition(6);
        payloadReader.consume(psPacketBuffer);
        psPacketBuffer.setLimit(psPacketBuffer.capacity());
    }
    return RESULT_CONTINUE;
}
Also used : TrackIdGenerator(com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator)

Example 4 with TimestampAdjuster

use of com.google.android.exoplayer2.util.TimestampAdjuster in project ExoPlayer by google.

the class SectionReaderTest method setUp.

@Override
public void setUp() {
    packetPayload = new byte[512];
    Arrays.fill(packetPayload, (byte) 0xFF);
    payloadReader = new CustomSectionPayloadReader();
    reader = new SectionReader(payloadReader);
    reader.init(new TimestampAdjuster(0), new FakeExtractorOutput(), new TsPayloadReader.TrackIdGenerator(0, 1));
}
Also used : TimestampAdjuster(com.google.android.exoplayer2.util.TimestampAdjuster) FakeExtractorOutput(com.google.android.exoplayer2.testutil.FakeExtractorOutput)

Example 5 with TimestampAdjuster

use of com.google.android.exoplayer2.util.TimestampAdjuster in project ExoPlayer by google.

the class TsExtractorTest method testCustomInitialSectionReader.

public void testCustomInitialSectionReader() throws Exception {
    CustomTsPayloadReaderFactory factory = new CustomTsPayloadReaderFactory(false, true);
    TsExtractor tsExtractor = new TsExtractor(TsExtractor.MODE_NORMAL, new TimestampAdjuster(0), factory);
    FakeExtractorInput input = new FakeExtractorInput.Builder().setData(TestUtil.getByteArray(getInstrumentation(), "ts/sample_with_sdt.ts")).setSimulateIOErrors(false).setSimulateUnknownLength(false).setSimulatePartialReads(false).build();
    tsExtractor.init(new FakeExtractorOutput());
    PositionHolder seekPositionHolder = new PositionHolder();
    int readResult = Extractor.RESULT_CONTINUE;
    while (readResult != Extractor.RESULT_END_OF_INPUT) {
        readResult = tsExtractor.read(input, seekPositionHolder);
    }
    assertEquals(1, factory.sdtReader.consumedSdts);
}
Also used : FakeExtractorInput(com.google.android.exoplayer2.testutil.FakeExtractorInput) PositionHolder(com.google.android.exoplayer2.extractor.PositionHolder) TimestampAdjuster(com.google.android.exoplayer2.util.TimestampAdjuster) FakeExtractorOutput(com.google.android.exoplayer2.testutil.FakeExtractorOutput)

Aggregations

TimestampAdjuster (com.google.android.exoplayer2.util.TimestampAdjuster)6 FakeExtractorOutput (com.google.android.exoplayer2.testutil.FakeExtractorOutput)3 PositionHolder (com.google.android.exoplayer2.extractor.PositionHolder)2 TrackOutput (com.google.android.exoplayer2.extractor.TrackOutput)2 FakeExtractorInput (com.google.android.exoplayer2.testutil.FakeExtractorInput)2 Uri (android.net.Uri)1 ParserException (com.google.android.exoplayer2.ParserException)1 Extractor (com.google.android.exoplayer2.extractor.Extractor)1 Mp3Extractor (com.google.android.exoplayer2.extractor.mp3.Mp3Extractor)1 FragmentedMp4Extractor (com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor)1 Ac3Extractor (com.google.android.exoplayer2.extractor.ts.Ac3Extractor)1 AdtsExtractor (com.google.android.exoplayer2.extractor.ts.AdtsExtractor)1 DefaultTsPayloadReaderFactory (com.google.android.exoplayer2.extractor.ts.DefaultTsPayloadReaderFactory)1 TsExtractor (com.google.android.exoplayer2.extractor.ts.TsExtractor)1 TrackIdGenerator (com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator)1 Metadata (com.google.android.exoplayer2.metadata.Metadata)1 BehindLiveWindowException (com.google.android.exoplayer2.source.BehindLiveWindowException)1 HlsUrl (com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl)1 HlsMediaPlaylist (com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist)1 Segment (com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment)1