Search in sources :

Example 11 with Subtitle

use of com.google.android.exoplayer2.text.Subtitle in project ExoPlayer by google.

the class HlsMediaPeriod method buildAndPrepareSampleStreamWrappers.

// Internal methods.
private void buildAndPrepareSampleStreamWrappers() {
    HlsMasterPlaylist masterPlaylist = playlistTracker.getMasterPlaylist();
    // Build the default stream wrapper.
    List<HlsUrl> selectedVariants = new ArrayList<>(masterPlaylist.variants);
    ArrayList<HlsUrl> definiteVideoVariants = new ArrayList<>();
    ArrayList<HlsUrl> definiteAudioOnlyVariants = new ArrayList<>();
    for (int i = 0; i < selectedVariants.size(); i++) {
        HlsUrl variant = selectedVariants.get(i);
        if (variant.format.height > 0 || variantHasExplicitCodecWithPrefix(variant, "avc")) {
            definiteVideoVariants.add(variant);
        } else if (variantHasExplicitCodecWithPrefix(variant, "mp4a")) {
            definiteAudioOnlyVariants.add(variant);
        }
    }
    if (!definiteVideoVariants.isEmpty()) {
        // We've identified some variants as definitely containing video. Assume variants within the
        // master playlist are marked consistently, and hence that we have the full set. Filter out
        // any other variants, which are likely to be audio only.
        selectedVariants = definiteVideoVariants;
    } else if (definiteAudioOnlyVariants.size() < selectedVariants.size()) {
        // We've identified some variants, but not all, as being audio only. Filter them out to leave
        // the remaining variants, which are likely to contain video.
        selectedVariants.removeAll(definiteAudioOnlyVariants);
    } else {
    // Leave the enabled variants unchanged. They're likely either all video or all audio.
    }
    List<HlsUrl> audioRenditions = masterPlaylist.audios;
    List<HlsUrl> subtitleRenditions = masterPlaylist.subtitles;
    sampleStreamWrappers = new HlsSampleStreamWrapper[1 + /* variants */
    audioRenditions.size() + subtitleRenditions.size()];
    int currentWrapperIndex = 0;
    pendingPrepareCount = sampleStreamWrappers.length;
    Assertions.checkArgument(!selectedVariants.isEmpty());
    HlsUrl[] variants = new HlsMasterPlaylist.HlsUrl[selectedVariants.size()];
    selectedVariants.toArray(variants);
    HlsSampleStreamWrapper sampleStreamWrapper = buildSampleStreamWrapper(C.TRACK_TYPE_DEFAULT, variants, masterPlaylist.muxedAudioFormat, masterPlaylist.muxedCaptionFormats);
    sampleStreamWrappers[currentWrapperIndex++] = sampleStreamWrapper;
    sampleStreamWrapper.setIsTimestampMaster(true);
    sampleStreamWrapper.continuePreparing();
    // Build audio stream wrappers.
    for (int i = 0; i < audioRenditions.size(); i++) {
        sampleStreamWrapper = buildSampleStreamWrapper(C.TRACK_TYPE_AUDIO, new HlsUrl[] { audioRenditions.get(i) }, null, Collections.<Format>emptyList());
        sampleStreamWrappers[currentWrapperIndex++] = sampleStreamWrapper;
        sampleStreamWrapper.continuePreparing();
    }
    // Build subtitle stream wrappers.
    for (int i = 0; i < subtitleRenditions.size(); i++) {
        HlsUrl url = subtitleRenditions.get(i);
        sampleStreamWrapper = buildSampleStreamWrapper(C.TRACK_TYPE_TEXT, new HlsUrl[] { url }, null, Collections.<Format>emptyList());
        sampleStreamWrapper.prepareSingleTrack(url.format);
        sampleStreamWrappers[currentWrapperIndex++] = sampleStreamWrapper;
    }
}
Also used : Format(com.google.android.exoplayer2.Format) HlsUrl(com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl) ArrayList(java.util.ArrayList) HlsMasterPlaylist(com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist)

Example 12 with Subtitle

use of com.google.android.exoplayer2.text.Subtitle in project ExoPlayer by google.

the class CeaDecoder method dequeueOutputBuffer.

@Override
public SubtitleOutputBuffer dequeueOutputBuffer() throws SubtitleDecoderException {
    if (availableOutputBuffers.isEmpty()) {
        return null;
    }
    // be deferred until they would be applicable
    while (!queuedInputBuffers.isEmpty() && queuedInputBuffers.first().timeUs <= playbackPositionUs) {
        SubtitleInputBuffer inputBuffer = queuedInputBuffers.pollFirst();
        // return immediately with an output buffer propagating that
        if (inputBuffer.isEndOfStream()) {
            SubtitleOutputBuffer outputBuffer = availableOutputBuffers.pollFirst();
            outputBuffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
            releaseInputBuffer(inputBuffer);
            return outputBuffer;
        }
        decode(inputBuffer);
        // check if we have any caption updates to report
        if (isNewSubtitleDataAvailable()) {
            // Even if the subtitle is decode-only; we need to generate it to consume the data so it
            // isn't accidentally prepended to the next subtitle
            Subtitle subtitle = createSubtitle();
            if (!inputBuffer.isDecodeOnly()) {
                SubtitleOutputBuffer outputBuffer = availableOutputBuffers.pollFirst();
                outputBuffer.setContent(inputBuffer.timeUs, subtitle, Format.OFFSET_SAMPLE_RELATIVE);
                releaseInputBuffer(inputBuffer);
                return outputBuffer;
            }
        }
        releaseInputBuffer(inputBuffer);
    }
    return null;
}
Also used : Subtitle(com.google.android.exoplayer2.text.Subtitle) SubtitleInputBuffer(com.google.android.exoplayer2.text.SubtitleInputBuffer) SubtitleOutputBuffer(com.google.android.exoplayer2.text.SubtitleOutputBuffer)

Example 13 with Subtitle

use of com.google.android.exoplayer2.text.Subtitle in project ExoPlayer by google.

the class MatroskaExtractor method writeSampleData.

private void writeSampleData(ExtractorInput input, Track track, int size) throws IOException, InterruptedException {
    if (CODEC_ID_SUBRIP.equals(track.codecId)) {
        int sizeWithPrefix = SUBRIP_PREFIX.length + size;
        if (subripSample.capacity() < sizeWithPrefix) {
            // Initialize subripSample to contain the required prefix and have space to hold a subtitle
            // twice as long as this one.
            subripSample.data = Arrays.copyOf(SUBRIP_PREFIX, sizeWithPrefix + size);
        }
        input.readFully(subripSample.data, SUBRIP_PREFIX.length, size);
        subripSample.setPosition(0);
        subripSample.setLimit(sizeWithPrefix);
        // the correct end timecode, which we might not have yet.
        return;
    }
    TrackOutput output = track.output;
    if (!sampleEncodingHandled) {
        if (track.hasContentEncryption) {
            // If the sample is encrypted, read its encryption signal byte and set the IV size.
            // Clear the encrypted flag.
            blockFlags &= ~C.BUFFER_FLAG_ENCRYPTED;
            if (!sampleSignalByteRead) {
                input.readFully(scratch.data, 0, 1);
                sampleBytesRead++;
                if ((scratch.data[0] & 0x80) == 0x80) {
                    throw new ParserException("Extension bit is set in signal byte");
                }
                sampleSignalByte = scratch.data[0];
                sampleSignalByteRead = true;
            }
            boolean isEncrypted = (sampleSignalByte & 0x01) == 0x01;
            if (isEncrypted) {
                boolean hasSubsampleEncryption = (sampleSignalByte & 0x02) == 0x02;
                blockFlags |= C.BUFFER_FLAG_ENCRYPTED;
                if (!sampleInitializationVectorRead) {
                    input.readFully(encryptionInitializationVector.data, 0, ENCRYPTION_IV_SIZE);
                    sampleBytesRead += ENCRYPTION_IV_SIZE;
                    sampleInitializationVectorRead = true;
                    // Write the signal byte, containing the IV size and the subsample encryption flag.
                    scratch.data[0] = (byte) (ENCRYPTION_IV_SIZE | (hasSubsampleEncryption ? 0x80 : 0x00));
                    scratch.setPosition(0);
                    output.sampleData(scratch, 1);
                    sampleBytesWritten++;
                    // Write the IV.
                    encryptionInitializationVector.setPosition(0);
                    output.sampleData(encryptionInitializationVector, ENCRYPTION_IV_SIZE);
                    sampleBytesWritten += ENCRYPTION_IV_SIZE;
                }
                if (hasSubsampleEncryption) {
                    if (!samplePartitionCountRead) {
                        input.readFully(scratch.data, 0, 1);
                        sampleBytesRead++;
                        scratch.setPosition(0);
                        samplePartitionCount = scratch.readUnsignedByte();
                        samplePartitionCountRead = true;
                    }
                    int samplePartitionDataSize = samplePartitionCount * 4;
                    scratch.reset(samplePartitionDataSize);
                    input.readFully(scratch.data, 0, samplePartitionDataSize);
                    sampleBytesRead += samplePartitionDataSize;
                    short subsampleCount = (short) (1 + (samplePartitionCount / 2));
                    int subsampleDataSize = 2 + 6 * subsampleCount;
                    if (encryptionSubsampleDataBuffer == null || encryptionSubsampleDataBuffer.capacity() < subsampleDataSize) {
                        encryptionSubsampleDataBuffer = ByteBuffer.allocate(subsampleDataSize);
                    }
                    encryptionSubsampleDataBuffer.position(0);
                    encryptionSubsampleDataBuffer.putShort(subsampleCount);
                    // Loop through the partition offsets and write out the data in the way ExoPlayer
                    // wants it (ISO 23001-7 Part 7):
                    //   2 bytes - sub sample count.
                    //   for each sub sample:
                    //     2 bytes - clear data size.
                    //     4 bytes - encrypted data size.
                    int partitionOffset = 0;
                    for (int i = 0; i < samplePartitionCount; i++) {
                        int previousPartitionOffset = partitionOffset;
                        partitionOffset = scratch.readUnsignedIntToInt();
                        if ((i % 2) == 0) {
                            encryptionSubsampleDataBuffer.putShort((short) (partitionOffset - previousPartitionOffset));
                        } else {
                            encryptionSubsampleDataBuffer.putInt(partitionOffset - previousPartitionOffset);
                        }
                    }
                    int finalPartitionSize = size - sampleBytesRead - partitionOffset;
                    if ((samplePartitionCount % 2) == 1) {
                        encryptionSubsampleDataBuffer.putInt(finalPartitionSize);
                    } else {
                        encryptionSubsampleDataBuffer.putShort((short) finalPartitionSize);
                        encryptionSubsampleDataBuffer.putInt(0);
                    }
                    encryptionSubsampleData.reset(encryptionSubsampleDataBuffer.array(), subsampleDataSize);
                    output.sampleData(encryptionSubsampleData, subsampleDataSize);
                    sampleBytesWritten += subsampleDataSize;
                }
            }
        } else if (track.sampleStrippedBytes != null) {
            // If the sample has header stripping, prepare to read/output the stripped bytes first.
            sampleStrippedBytes.reset(track.sampleStrippedBytes, track.sampleStrippedBytes.length);
        }
        sampleEncodingHandled = true;
    }
    size += sampleStrippedBytes.limit();
    if (CODEC_ID_H264.equals(track.codecId) || CODEC_ID_H265.equals(track.codecId)) {
        // TODO: Deduplicate with Mp4Extractor.
        // 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[] nalLengthData = nalLength.data;
        nalLengthData[0] = 0;
        nalLengthData[1] = 0;
        nalLengthData[2] = 0;
        int nalUnitLengthFieldLength = track.nalUnitLengthFieldLength;
        int nalUnitLengthFieldLengthDiff = 4 - track.nalUnitLengthFieldLength;
        // start codes as we encounter them.
        while (sampleBytesRead < size) {
            if (sampleCurrentNalBytesRemaining == 0) {
                // Read the NAL length so that we know where we find the next one.
                readToTarget(input, nalLengthData, nalUnitLengthFieldLengthDiff, nalUnitLengthFieldLength);
                nalLength.setPosition(0);
                sampleCurrentNalBytesRemaining = nalLength.readUnsignedIntToInt();
                // Write a start code for the current NAL unit.
                nalStartCode.setPosition(0);
                output.sampleData(nalStartCode, 4);
                sampleBytesWritten += 4;
            } else {
                // Write the payload of the NAL unit.
                sampleCurrentNalBytesRemaining -= readToOutput(input, output, sampleCurrentNalBytesRemaining);
            }
        }
    } else {
        while (sampleBytesRead < size) {
            readToOutput(input, output, size - sampleBytesRead);
        }
    }
    if (CODEC_ID_VORBIS.equals(track.codecId)) {
        // Vorbis decoder in android MediaCodec [1] expects the last 4 bytes of the sample to be the
        // number of samples in the current page. This definition holds good only for Ogg and
        // irrelevant for Matroska. So we always set this to -1 (the decoder will ignore this value if
        // we set it to -1). The android platform media extractor [2] does the same.
        // [1] https://android.googlesource.com/platform/frameworks/av/+/lollipop-release/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp#314
        // [2] https://android.googlesource.com/platform/frameworks/av/+/lollipop-release/media/libstagefright/NuMediaExtractor.cpp#474
        vorbisNumPageSamples.setPosition(0);
        output.sampleData(vorbisNumPageSamples, 4);
        sampleBytesWritten += 4;
    }
}
Also used : ParserException(com.google.android.exoplayer2.ParserException) TrackOutput(com.google.android.exoplayer2.extractor.TrackOutput)

Example 14 with Subtitle

use of com.google.android.exoplayer2.text.Subtitle in project ExoPlayer by google.

the class WebvttDecoderTest method assertCue.

private static void assertCue(WebvttSubtitle subtitle, int eventTimeIndex, long startTimeUs, int endTimeUs, String text, Alignment textAlignment, float line, int lineType, int lineAnchor, float position, int positionAnchor, float size) {
    assertEquals(startTimeUs, subtitle.getEventTime(eventTimeIndex));
    assertEquals(endTimeUs, subtitle.getEventTime(eventTimeIndex + 1));
    List<Cue> cues = subtitle.getCues(subtitle.getEventTime(eventTimeIndex));
    assertEquals(1, cues.size());
    // Assert cue properties.
    Cue cue = cues.get(0);
    assertEquals(text, cue.text.toString());
    assertEquals(textAlignment, cue.textAlignment);
    assertEquals(line, cue.line);
    assertEquals(lineType, cue.lineType);
    assertEquals(lineAnchor, cue.lineAnchor);
    assertEquals(position, cue.position);
    assertEquals(positionAnchor, cue.positionAnchor);
    assertEquals(size, cue.size);
}
Also used : Cue(com.google.android.exoplayer2.text.Cue)

Aggregations

Cue (com.google.android.exoplayer2.text.Cue)10 SpannableStringBuilder (android.text.SpannableStringBuilder)5 Subtitle (com.google.android.exoplayer2.text.Subtitle)4 Format (com.google.android.exoplayer2.Format)1 ParserException (com.google.android.exoplayer2.ParserException)1 TrackOutput (com.google.android.exoplayer2.extractor.TrackOutput)1 HlsMasterPlaylist (com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist)1 HlsUrl (com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl)1 SubtitleInputBuffer (com.google.android.exoplayer2.text.SubtitleInputBuffer)1 SubtitleOutputBuffer (com.google.android.exoplayer2.text.SubtitleOutputBuffer)1 ArrayList (java.util.ArrayList)1