Search in sources :

Example 1 with ParsableByteArray

use of androidx.media3.common.util.ParsableByteArray in project media by androidx.

the class FlacExtractor method handlePendingSeek.

@RequiresNonNull("binarySearchSeeker")
private int handlePendingSeek(ExtractorInput input, PositionHolder seekPosition, ParsableByteArray outputBuffer, OutputFrameHolder outputFrameHolder, TrackOutput trackOutput) throws IOException {
    int seekResult = binarySearchSeeker.handlePendingSeek(input, seekPosition);
    ByteBuffer outputByteBuffer = outputFrameHolder.byteBuffer;
    if (seekResult == RESULT_CONTINUE && outputByteBuffer.limit() > 0) {
        outputSample(outputBuffer, outputByteBuffer.limit(), outputFrameHolder.timeUs, trackOutput);
    }
    return seekResult;
}
Also used : ByteBuffer(java.nio.ByteBuffer) SeekPoint(androidx.media3.extractor.SeekPoint) RequiresNonNull(org.checkerframework.checker.nullness.qual.RequiresNonNull)

Example 2 with ParsableByteArray

use of androidx.media3.common.util.ParsableByteArray in project media by androidx.

the class FrameworkMediaDrm method addLaUrlAttributeIfMissing.

/**
 * If the LA_URL tag is missing, injects a mock LA_URL value to avoid causing the CDM to throw
 * when creating the key request. The LA_URL attribute is optional but some Android PlayReady
 * implementations are known to require it. Does nothing it the provided {@code data} already
 * contains an LA_URL value.
 */
private static byte[] addLaUrlAttributeIfMissing(byte[] data) {
    ParsableByteArray byteArray = new ParsableByteArray(data);
    // See https://docs.microsoft.com/en-us/playready/specifications/specifications for more
    // information about the init data format.
    int length = byteArray.readLittleEndianInt();
    int objectRecordCount = byteArray.readLittleEndianShort();
    int recordType = byteArray.readLittleEndianShort();
    if (objectRecordCount != 1 || recordType != 1) {
        Log.i(TAG, "Unexpected record count or type. Skipping LA_URL workaround.");
        return data;
    }
    int recordLength = byteArray.readLittleEndianShort();
    String xml = byteArray.readString(recordLength, Charsets.UTF_16LE);
    if (xml.contains("<LA_URL>")) {
        // LA_URL already present. Do nothing.
        return data;
    }
    // This PlayReady object record does not include an LA_URL. We add a mock value for it.
    int endOfDataTagIndex = xml.indexOf("</DATA>");
    if (endOfDataTagIndex == -1) {
        Log.w(TAG, "Could not find the </DATA> tag. Skipping LA_URL workaround.");
    }
    String xmlWithMockLaUrl = xml.substring(/* beginIndex= */
    0, /* endIndex= */
    endOfDataTagIndex) + MOCK_LA_URL + xml.substring(/* beginIndex= */
    endOfDataTagIndex);
    int extraBytes = MOCK_LA_URL.length() * UTF_16_BYTES_PER_CHARACTER;
    ByteBuffer newData = ByteBuffer.allocate(length + extraBytes);
    newData.order(ByteOrder.LITTLE_ENDIAN);
    newData.putInt(length + extraBytes);
    newData.putShort((short) objectRecordCount);
    newData.putShort((short) recordType);
    newData.putShort((short) (xmlWithMockLaUrl.length() * UTF_16_BYTES_PER_CHARACTER));
    newData.put(xmlWithMockLaUrl.getBytes(Charsets.UTF_16LE));
    return newData.array();
}
Also used : ParsableByteArray(androidx.media3.common.util.ParsableByteArray) ByteBuffer(java.nio.ByteBuffer) SuppressLint(android.annotation.SuppressLint)

Example 3 with ParsableByteArray

use of androidx.media3.common.util.ParsableByteArray in project media by androidx.

the class IcyDataSource method readMetadata.

/**
 * Reads an ICY stream metadata block, passing it to {@link #listener} unless the block is empty.
 *
 * @return True if the block was extracted, including if its length byte indicated a length of
 *     zero. False if the end of the stream was reached.
 * @throws IOException If an error occurs reading from the wrapped {@link DataSource}.
 */
private boolean readMetadata() throws IOException {
    int bytesRead = upstream.read(metadataLengthByteHolder, 0, 1);
    if (bytesRead == C.RESULT_END_OF_INPUT) {
        return false;
    }
    int metadataLength = (metadataLengthByteHolder[0] & 0xFF) << 4;
    if (metadataLength == 0) {
        return true;
    }
    int offset = 0;
    int lengthRemaining = metadataLength;
    byte[] metadata = new byte[metadataLength];
    while (lengthRemaining > 0) {
        bytesRead = upstream.read(metadata, offset, lengthRemaining);
        if (bytesRead == C.RESULT_END_OF_INPUT) {
            return false;
        }
        offset += bytesRead;
        lengthRemaining -= bytesRead;
    }
    // Discard trailing zero bytes.
    while (metadataLength > 0 && metadata[metadataLength - 1] == 0) {
        metadataLength--;
    }
    if (metadataLength > 0) {
        listener.onIcyMetadata(new ParsableByteArray(metadata, metadataLength));
    }
    return true;
}
Also used : ParsableByteArray(androidx.media3.common.util.ParsableByteArray)

Example 4 with ParsableByteArray

use of androidx.media3.common.util.ParsableByteArray in project media by androidx.

the class HlsMediaChunk method createInstance.

/**
 * Creates a new instance.
 *
 * @param extractorFactory A {@link HlsExtractorFactory} from which the {@link
 *     HlsMediaChunkExtractor} is obtained.
 * @param dataSource The source from which the data should be loaded.
 * @param format The chunk format.
 * @param startOfPlaylistInPeriodUs The position of the playlist in the period in microseconds.
 * @param mediaPlaylist The media playlist from which this chunk was obtained.
 * @param segmentBaseHolder The segment holder.
 * @param playlistUrl The url of the playlist from which this chunk was obtained.
 * @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption
 *     information is available in the multivariant playlist.
 * @param trackSelectionReason See {@link #trackSelectionReason}.
 * @param trackSelectionData See {@link #trackSelectionData}.
 * @param isMasterTimestampSource True if the chunk can initialize the timestamp adjuster.
 * @param timestampAdjusterProvider The provider from which to obtain the {@link
 *     TimestampAdjuster}.
 * @param previousChunk The {@link HlsMediaChunk} that preceded this one. May be null.
 * @param mediaSegmentKey The media segment decryption key, if fully encrypted. Null otherwise.
 * @param initSegmentKey The initialization segment decryption key, if fully encrypted. Null
 *     otherwise.
 * @param shouldSpliceIn Whether samples for this chunk should be spliced into existing samples.
 */
public static HlsMediaChunk createInstance(HlsExtractorFactory extractorFactory, DataSource dataSource, Format format, long startOfPlaylistInPeriodUs, HlsMediaPlaylist mediaPlaylist, HlsChunkSource.SegmentBaseHolder segmentBaseHolder, Uri playlistUrl, @Nullable List<Format> muxedCaptionFormats, @C.SelectionReason int trackSelectionReason, @Nullable Object trackSelectionData, boolean isMasterTimestampSource, TimestampAdjusterProvider timestampAdjusterProvider, @Nullable HlsMediaChunk previousChunk, @Nullable byte[] mediaSegmentKey, @Nullable byte[] initSegmentKey, boolean shouldSpliceIn, PlayerId playerId) {
    // Media segment.
    HlsMediaPlaylist.SegmentBase mediaSegment = segmentBaseHolder.segmentBase;
    DataSpec dataSpec = new DataSpec.Builder().setUri(UriUtil.resolveToUri(mediaPlaylist.baseUri, mediaSegment.url)).setPosition(mediaSegment.byteRangeOffset).setLength(mediaSegment.byteRangeLength).setFlags(segmentBaseHolder.isPreload ? FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED : 0).build();
    boolean mediaSegmentEncrypted = mediaSegmentKey != null;
    @Nullable byte[] mediaSegmentIv = mediaSegmentEncrypted ? getEncryptionIvArray(Assertions.checkNotNull(mediaSegment.encryptionIV)) : null;
    DataSource mediaDataSource = buildDataSource(dataSource, mediaSegmentKey, mediaSegmentIv);
    // Init segment.
    HlsMediaPlaylist.Segment initSegment = mediaSegment.initializationSegment;
    DataSpec initDataSpec = null;
    boolean initSegmentEncrypted = false;
    @Nullable DataSource initDataSource = null;
    if (initSegment != null) {
        initSegmentEncrypted = initSegmentKey != null;
        @Nullable byte[] initSegmentIv = initSegmentEncrypted ? getEncryptionIvArray(Assertions.checkNotNull(initSegment.encryptionIV)) : null;
        Uri initSegmentUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, initSegment.url);
        initDataSpec = new DataSpec(initSegmentUri, initSegment.byteRangeOffset, initSegment.byteRangeLength);
        initDataSource = buildDataSource(dataSource, initSegmentKey, initSegmentIv);
    }
    long segmentStartTimeInPeriodUs = startOfPlaylistInPeriodUs + mediaSegment.relativeStartTimeUs;
    long segmentEndTimeInPeriodUs = segmentStartTimeInPeriodUs + mediaSegment.durationUs;
    int discontinuitySequenceNumber = mediaPlaylist.discontinuitySequence + mediaSegment.relativeDiscontinuitySequence;
    @Nullable HlsMediaChunkExtractor previousExtractor = null;
    Id3Decoder id3Decoder;
    ParsableByteArray scratchId3Data;
    if (previousChunk != null) {
        boolean isSameInitData = initDataSpec == previousChunk.initDataSpec || (initDataSpec != null && previousChunk.initDataSpec != null && initDataSpec.uri.equals(previousChunk.initDataSpec.uri) && initDataSpec.position == previousChunk.initDataSpec.position);
        boolean isFollowingChunk = playlistUrl.equals(previousChunk.playlistUrl) && previousChunk.loadCompleted;
        id3Decoder = previousChunk.id3Decoder;
        scratchId3Data = previousChunk.scratchId3Data;
        previousExtractor = isSameInitData && isFollowingChunk && !previousChunk.extractorInvalidated && previousChunk.discontinuitySequenceNumber == discontinuitySequenceNumber ? previousChunk.extractor : null;
    } else {
        id3Decoder = new Id3Decoder();
        scratchId3Data = new ParsableByteArray(Id3Decoder.ID3_HEADER_LENGTH);
    }
    return new HlsMediaChunk(extractorFactory, mediaDataSource, dataSpec, format, mediaSegmentEncrypted, initDataSource, initDataSpec, initSegmentEncrypted, playlistUrl, muxedCaptionFormats, trackSelectionReason, trackSelectionData, segmentStartTimeInPeriodUs, segmentEndTimeInPeriodUs, segmentBaseHolder.mediaSequence, segmentBaseHolder.partIndex, /* isPublished= */
    !segmentBaseHolder.isPreload, discontinuitySequenceNumber, mediaSegment.hasGapTag, isMasterTimestampSource, /* timestampAdjuster= */
    timestampAdjusterProvider.getAdjuster(discontinuitySequenceNumber), mediaSegment.drmInitData, previousExtractor, id3Decoder, scratchId3Data, shouldSpliceIn, playerId);
}
Also used : Uri(android.net.Uri) DataSource(androidx.media3.datasource.DataSource) ParsableByteArray(androidx.media3.common.util.ParsableByteArray) DataSpec(androidx.media3.datasource.DataSpec) HlsMediaPlaylist(androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist) Id3Decoder(androidx.media3.extractor.metadata.id3.Id3Decoder) Nullable(androidx.annotation.Nullable)

Example 5 with ParsableByteArray

use of androidx.media3.common.util.ParsableByteArray in project media by androidx.

the class WebvttExtractor method processSample.

@RequiresNonNull("output")
private void processSample() throws ParserException {
    ParsableByteArray webvttData = new ParsableByteArray(sampleData);
    // Validate the first line of the header.
    WebvttParserUtil.validateWebvttHeaderLine(webvttData);
    // Defaults to use if the header doesn't contain an X-TIMESTAMP-MAP header.
    long vttTimestampUs = 0;
    long tsTimestampUs = 0;
    // Parse the remainder of the header looking for X-TIMESTAMP-MAP.
    for (String line = webvttData.readLine(); !TextUtils.isEmpty(line); line = webvttData.readLine()) {
        if (line.startsWith("X-TIMESTAMP-MAP")) {
            Matcher localTimestampMatcher = LOCAL_TIMESTAMP.matcher(line);
            if (!localTimestampMatcher.find()) {
                throw ParserException.createForMalformedContainer("X-TIMESTAMP-MAP doesn't contain local timestamp: " + line, /* cause= */
                null);
            }
            Matcher mediaTimestampMatcher = MEDIA_TIMESTAMP.matcher(line);
            if (!mediaTimestampMatcher.find()) {
                throw ParserException.createForMalformedContainer("X-TIMESTAMP-MAP doesn't contain media timestamp: " + line, /* cause= */
                null);
            }
            vttTimestampUs = WebvttParserUtil.parseTimestampUs(Assertions.checkNotNull(localTimestampMatcher.group(1)));
            tsTimestampUs = TimestampAdjuster.ptsToUs(Long.parseLong(Assertions.checkNotNull(mediaTimestampMatcher.group(1))));
        }
    }
    // Find the first cue header and parse the start time.
    Matcher cueHeaderMatcher = WebvttParserUtil.findNextCueHeader(webvttData);
    if (cueHeaderMatcher == null) {
        // No cues found. Don't output a sample, but still output a corresponding track.
        buildTrackOutput(0);
        return;
    }
    long firstCueTimeUs = WebvttParserUtil.parseTimestampUs(Assertions.checkNotNull(cueHeaderMatcher.group(1)));
    long sampleTimeUs = timestampAdjuster.adjustTsTimestamp(TimestampAdjuster.usToWrappedPts(firstCueTimeUs + tsTimestampUs - vttTimestampUs));
    long subsampleOffsetUs = sampleTimeUs - firstCueTimeUs;
    // Output the track.
    TrackOutput trackOutput = buildTrackOutput(subsampleOffsetUs);
    // Output the sample.
    sampleDataWrapper.reset(sampleData, sampleSize);
    trackOutput.sampleData(sampleDataWrapper, sampleSize);
    trackOutput.sampleMetadata(sampleTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleSize, 0, null);
}
Also used : ParsableByteArray(androidx.media3.common.util.ParsableByteArray) Matcher(java.util.regex.Matcher) TrackOutput(androidx.media3.extractor.TrackOutput) RequiresNonNull(org.checkerframework.checker.nullness.qual.RequiresNonNull)

Aggregations

ParsableByteArray (androidx.media3.common.util.ParsableByteArray)96 Test (org.junit.Test)38 Nullable (androidx.annotation.Nullable)34 Format (androidx.media3.common.Format)9 ArrayList (java.util.ArrayList)9 Metadata (androidx.media3.common.Metadata)7 FakeExtractorInput (androidx.media3.test.utils.FakeExtractorInput)7 SeekPoint (androidx.media3.extractor.SeekPoint)5 Cue (androidx.media3.common.text.Cue)4 SampleNumberHolder (androidx.media3.extractor.FlacFrameReader.SampleNumberHolder)4 FlacStreamMetadataHolder (androidx.media3.extractor.FlacMetadataReader.FlacStreamMetadataHolder)4 ByteBuffer (java.nio.ByteBuffer)4 RequiresNonNull (org.checkerframework.checker.nullness.qual.RequiresNonNull)4 ParsableBitArray (androidx.media3.common.util.ParsableBitArray)3 Mesh (androidx.media3.exoplayer.video.spherical.Projection.Mesh)3 SubMesh (androidx.media3.exoplayer.video.spherical.Projection.SubMesh)3 TrackOutput (androidx.media3.extractor.TrackOutput)3 SmtaMetadataEntry (androidx.media3.extractor.metadata.mp4.SmtaMetadataEntry)3 LeafAtom (androidx.media3.extractor.mp4.Atom.LeafAtom)3 EnsuresNonNullIf (org.checkerframework.checker.nullness.qual.EnsuresNonNullIf)3