@Override public void consume(ParsableByteArray data) { if (writingSample) { if (bytesToCheck == 2 && !checkNextByte(data, 0x20)) { // Failed to check data_identifier return; } if (bytesToCheck == 1 && !checkNextByte(data, 0x00)) { // Check and discard the subtitle_stream_id return; } int dataPosition = data.getPosition(); int bytesAvailable = data.bytesLeft(); for (TrackOutput output : outputs) { data.setPosition(dataPosition); output.sampleData(data, bytesAvailable); } sampleBytesWritten += bytesAvailable; } }
public void testCustomPesReader() throws Exception { CustomEsReaderFactory factory = new CustomEsReaderFactory(); TsExtractor tsExtractor = new TsExtractor(new TimestampAdjuster(0), factory, false); FakeExtractorInput input = new FakeExtractorInput.Builder() .setData(TestUtil.getByteArray(getInstrumentation(), "ts/sample.ts")) .setSimulateIOErrors(false) .setSimulateUnknownLength(false) .setSimulatePartialReads(false).build(); FakeExtractorOutput output = new FakeExtractorOutput(); tsExtractor.init(output); tsExtractor.seek(input.getPosition()); PositionHolder seekPositionHolder = new PositionHolder(); int readResult = Extractor.RESULT_CONTINUE; while (readResult != Extractor.RESULT_END_OF_INPUT) { readResult = tsExtractor.read(input, seekPositionHolder); } CustomEsReader reader = factory.reader; assertEquals(2, reader.packetsRead); TrackOutput trackOutput = reader.getTrackOutput(); assertTrue(trackOutput == output.trackOutputs.get(257 /* PID of audio track. */)); assertEquals( Format.createTextSampleFormat("Overriding format", "mime", null, 0, 0, "und", null, 0), ((FakeTrackOutput) trackOutput).format); }
public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { for (int i = 0; i < outputs.length; i++) { idGenerator.generateNewId(); TrackOutput output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_TEXT); Format channelFormat = closedCaptionFormats.get(i); String channelMimeType = channelFormat.sampleMimeType; Assertions.checkArgument(MimeTypes.APPLICATION_CEA608.equals(channelMimeType) || MimeTypes.APPLICATION_CEA708.equals(channelMimeType), "Invalid closed caption mime type provided: " + channelMimeType); String formatId = channelFormat.id != null ? channelFormat.id : idGenerator.getFormatId(); output.format(Format.createTextSampleFormat(formatId, channelMimeType, null, Format.NO_VALUE, channelFormat.selectionFlags, channelFormat.language, channelFormat.accessibilityChannel, null)); outputs[i] = output; } }
@Override public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException { if (streamReader == null) { if (!sniffInternal(input)) { throw new ParserException("Failed to determine bitstream type"); } input.resetPeekPosition(); } if (!streamReaderInitialized) { TrackOutput trackOutput = output.track(0, C.TRACK_TYPE_AUDIO); output.endTracks(); streamReader.init(output, trackOutput); streamReaderInitialized = true; } return streamReader.read(input, seekPosition); }
@Override public TrackOutput track(int id, int type) { int trackCount = sampleQueues.length; for (int i = 0; i < trackCount; i++) { if (sampleQueueTrackIds[i] == id) { return sampleQueues[i]; } } SampleQueue trackOutput = new SampleQueue(allocator); trackOutput.setUpstreamFormatChangeListener(this); sampleQueueTrackIds = Arrays.copyOf(sampleQueueTrackIds, trackCount + 1); sampleQueueTrackIds[trackCount] = id; sampleQueues = Arrays.copyOf(sampleQueues, trackCount + 1); sampleQueues[trackCount] = trackOutput; return trackOutput; }
public void testCustomPesReader() throws Exception { CustomTsPayloadReaderFactory factory = new CustomTsPayloadReaderFactory(true, false); TsExtractor tsExtractor = new TsExtractor(TsExtractor.MODE_MULTI_PMT, new TimestampAdjuster(0), factory); FakeExtractorInput input = new FakeExtractorInput.Builder() .setData(TestUtil.getByteArray(getInstrumentation(), "ts/sample.ts")) .setSimulateIOErrors(false) .setSimulateUnknownLength(false) .setSimulatePartialReads(false).build(); FakeExtractorOutput output = new FakeExtractorOutput(); tsExtractor.init(output); PositionHolder seekPositionHolder = new PositionHolder(); int readResult = Extractor.RESULT_CONTINUE; while (readResult != Extractor.RESULT_END_OF_INPUT) { readResult = tsExtractor.read(input, seekPositionHolder); } CustomEsReader reader = factory.esReader; assertEquals(2, reader.packetsRead); TrackOutput trackOutput = reader.getTrackOutput(); assertTrue(trackOutput == output.trackOutputs.get(257 /* PID of audio track. */)); assertEquals( Format.createTextSampleFormat("1/257", "mime", null, 0, 0, "und", null, 0), ((FakeTrackOutput) trackOutput).format); }
private TrackOutput buildTrackOutput(long subsampleOffsetUs) { TrackOutput trackOutput = output.track(0, C.TRACK_TYPE_TEXT); trackOutput.format(Format.createTextSampleFormat(null, MimeTypes.TEXT_VTT, null, Format.NO_VALUE, 0, language, null, subsampleOffsetUs)); output.endTracks(); return trackOutput; }
public SampleReader(TrackOutput output, boolean allowNonIdrKeyframes, boolean detectAccessUnits) { this.output = output; this.allowNonIdrKeyframes = allowNonIdrKeyframes; this.detectAccessUnits = detectAccessUnits; sps = new SparseArray<>(); pps = new SparseArray<>(); previousSliceHeader = new SliceHeaderData(); sliceHeader = new SliceHeaderData(); buffer = new byte[DEFAULT_BUFFER_SIZE]; bitArray = new ParsableNalUnitBitArray(buffer, 0, 0); reset(); }
public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { for (int i = 0; i < outputs.length; i++) { idGenerator.generateNewId(); TrackOutput output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_TEXT); Format channelFormat = closedCaptionFormats.get(i); String channelMimeType = channelFormat.sampleMimeType; Assertions.checkArgument(MimeTypes.APPLICATION_CEA608.equals(channelMimeType) || MimeTypes.APPLICATION_CEA708.equals(channelMimeType), "Invalid closed caption mime type provided: " + channelMimeType); output.format(Format.createTextSampleFormat(idGenerator.getFormatId(), channelMimeType, null, Format.NO_VALUE, channelFormat.selectionFlags, channelFormat.language, channelFormat.accessibilityChannel, null)); outputs[i] = output; } }
/** * Sets the state to STATE_READING_SAMPLE. * * @param outputToUse TrackOutput object to write the sample to * @param currentSampleDuration Duration of the sample to be read * @param priorReadBytes Size of prior read bytes * @param sampleSize Size of the sample */ private void setReadingSampleState(TrackOutput outputToUse, long currentSampleDuration, int priorReadBytes, int sampleSize) { state = STATE_READING_SAMPLE; bytesRead = priorReadBytes; this.currentOutput = outputToUse; this.currentSampleDuration = currentSampleDuration; this.sampleSize = sampleSize; }
@Override public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { for (int i = 0; i < outputs.length; i++) { DvbSubtitleInfo subtitleInfo = subtitleInfos.get(i); idGenerator.generateNewId(); TrackOutput output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_TEXT); output.format(Format.createImageSampleFormat(idGenerator.getFormatId(), MimeTypes.APPLICATION_DVBSUBS, null, Format.NO_VALUE, Collections.singletonList(subtitleInfo.initializationData), subtitleInfo.language, null)); outputs[i] = output; } }
@Override public void packetFinished() { if (writingSample) { for (TrackOutput output : outputs) { output.sampleMetadata(sampleTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleBytesWritten, 0, null); } writingSample = false; } }
@Override public void init(ExtractorOutput output) { TrackOutput trackOutput = output.track(0, C.TRACK_TYPE_AUDIO); output.endTracks(); // TODO: fix the case if sniff() isn't called streamReader.init(output, trackOutput); }
void init(ExtractorOutput output, TrackOutput trackOutput) { this.extractorOutput = output; this.trackOutput = trackOutput; this.oggPacket = new OggPacket(); reset(true); }
private void maybeInitExtraTracks() { if ((flags & FLAG_ENABLE_EMSG_TRACK) != 0 && eventMessageTrackOutput == null) { eventMessageTrackOutput = extractorOutput.track(trackBundles.size(), C.TRACK_TYPE_METADATA); eventMessageTrackOutput.format(Format.createSampleFormat(null, MimeTypes.APPLICATION_EMSG, Format.OFFSET_SAMPLE_RELATIVE)); } if ((flags & FLAG_ENABLE_CEA608_TRACK) != 0 && cea608TrackOutputs == null) { TrackOutput cea608TrackOutput = extractorOutput.track(trackBundles.size() + 1, C.TRACK_TYPE_TEXT); cea608TrackOutput.format(Format.createTextSampleFormat(null, MimeTypes.APPLICATION_CEA608, null, Format.NO_VALUE, 0, null, null)); cea608TrackOutputs = new TrackOutput[] {cea608TrackOutput}; } }
/** * Appends the corresponding encryption data to the {@link TrackOutput} contained in the given * {@link TrackBundle}. * * @param trackBundle The {@link TrackBundle} that contains the {@link Track} for which the * Sample encryption data must be output. * @return The number of written bytes. */ private int appendSampleEncryptionData(TrackBundle trackBundle) { TrackFragment trackFragment = trackBundle.fragment; ParsableByteArray sampleEncryptionData = trackFragment.sampleEncryptionData; int sampleDescriptionIndex = trackFragment.header.sampleDescriptionIndex; TrackEncryptionBox encryptionBox = trackFragment.trackEncryptionBox != null ? trackFragment.trackEncryptionBox : trackBundle.track.sampleDescriptionEncryptionBoxes[sampleDescriptionIndex]; int vectorSize = encryptionBox.initializationVectorSize; boolean subsampleEncryption = trackFragment .sampleHasSubsampleEncryptionTable[trackBundle.currentSampleIndex]; // Write the signal byte, containing the vector size and the subsample encryption flag. encryptionSignalByte.data[0] = (byte) (vectorSize | (subsampleEncryption ? 0x80 : 0)); encryptionSignalByte.setPosition(0); TrackOutput output = trackBundle.output; output.sampleData(encryptionSignalByte, 1); // Write the vector. output.sampleData(sampleEncryptionData, vectorSize); // If we don't have subsample encryption data, we're done. if (!subsampleEncryption) { return 1 + vectorSize; } // Write the subsample encryption data. int subsampleCount = sampleEncryptionData.readUnsignedShort(); sampleEncryptionData.skipBytes(-2); int subsampleDataLength = 2 + 6 * subsampleCount; output.sampleData(sampleEncryptionData, subsampleDataLength); return 1 + vectorSize + subsampleDataLength; }
/** * Outputs up to {@code length} bytes of sample data to {@code output}, consisting of either * {@link #sampleStrippedBytes} or data read from {@code input}. */ private int readToOutput(ExtractorInput input, TrackOutput output, int length) throws IOException, InterruptedException { int bytesRead; int strippedBytesLeft = sampleStrippedBytes.bytesLeft(); if (strippedBytesLeft > 0) { bytesRead = Math.min(length, strippedBytesLeft); output.sampleData(sampleStrippedBytes, bytesRead); } else { bytesRead = output.sampleData(input, length, false); } sampleBytesRead += bytesRead; sampleBytesWritten += bytesRead; return bytesRead; }
/** * Consumes the unescaped content of an SEI NAL unit, writing the content of any CEA-608 messages * as samples to all of the provided outputs. * * @param presentationTimeUs The presentation time in microseconds for any samples. * @param seiBuffer The unescaped SEI NAL unit data, excluding the NAL unit start code and type. * @param outputs The outputs to which any samples should be written. */ public static void consume(long presentationTimeUs, ParsableByteArray seiBuffer, TrackOutput[] outputs) { while (seiBuffer.bytesLeft() > 1 /* last byte will be rbsp_trailing_bits */) { int payloadType = readNon255TerminatedValue(seiBuffer); int payloadSize = readNon255TerminatedValue(seiBuffer); // Process the payload. if (payloadSize == -1 || payloadSize > seiBuffer.bytesLeft()) { // This might occur if we're trying to read an encrypted SEI NAL unit. Log.w(TAG, "Skipping remainder of malformed SEI NAL unit."); seiBuffer.setPosition(seiBuffer.limit()); } else if (isSeiMessageCea608(payloadType, payloadSize, seiBuffer)) { // Ignore country_code (1) + provider_code (2) + user_identifier (4) // + user_data_type_code (1). seiBuffer.skipBytes(8); // Ignore first three bits: reserved (1) + process_cc_data_flag (1) + zero_bit (1). int ccCount = seiBuffer.readUnsignedByte() & 0x1F; // Ignore em_data (1) seiBuffer.skipBytes(1); // Each data packet consists of 24 bits: marker bits (5) + cc_valid (1) + cc_type (2) // + cc_data_1 (8) + cc_data_2 (8). int sampleLength = ccCount * 3; int sampleStartPosition = seiBuffer.getPosition(); for (TrackOutput output : outputs) { seiBuffer.setPosition(sampleStartPosition); output.sampleData(seiBuffer, sampleLength); output.sampleMetadata(presentationTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleLength, 0, null); } // Ignore trailing information in SEI, if any. seiBuffer.skipBytes(payloadSize - (10 + ccCount * 3)); } else { seiBuffer.skipBytes(payloadSize); } } }
@Override public TrackOutput track(int id, int type) { BindingTrackOutput bindingTrackOutput = bindingTrackOutputs.get(id); if (bindingTrackOutput == null) { // Assert that if we're seeing a new track we have not seen endTracks. Assertions.checkState(sampleFormats == null); bindingTrackOutput = new BindingTrackOutput(id, type, manifestFormat); bindingTrackOutput.bind(trackOutputProvider); bindingTrackOutputs.put(id, bindingTrackOutput); } return bindingTrackOutput; }
@Override public TrackOutput track(int id, int type) { for (int i = 0; i < trackTypes.length; i++) { if (type == trackTypes[i]) { return trackOutputs[i]; } } Log.e(TAG, "Unmatched track of type: " + type); return new DummyTrackOutput(); }
@SuppressWarnings("NonAtomicVolatileUpdate") @Override public void load() throws IOException, InterruptedException { DataSpec loadDataSpec = Util.getRemainderDataSpec(dataSpec, bytesLoaded); try { // Create and open the input. long length = dataSource.open(loadDataSpec); if (length != C.LENGTH_UNSET) { length += bytesLoaded; } ExtractorInput extractorInput = new DefaultExtractorInput(dataSource, bytesLoaded, length); BaseMediaChunkOutput output = getOutput(); output.setSampleOffsetUs(0); TrackOutput trackOutput = output.track(0, trackType); trackOutput.format(sampleFormat); // Load the sample data. int result = 0; while (result != C.RESULT_END_OF_INPUT) { bytesLoaded += result; result = trackOutput.sampleData(extractorInput, Integer.MAX_VALUE, true); } int sampleSize = bytesLoaded; trackOutput.sampleMetadata(startTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleSize, 0, null); } finally { Util.closeQuietly(dataSource); } loadCompleted = true; }
@Override public void init(ExtractorOutput output) { TrackOutput trackOutput = output.track(0); output.endTracks(); // TODO: fix the case if sniff() isn't called streamReader.init(output, trackOutput); }