/** * @param mode Mode for the extractor. One of {@link #MODE_MULTI_PMT}, {@link #MODE_SINGLE_PMT} * and {@link #MODE_HLS}. * @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps. * @param payloadReaderFactory Factory for injecting a custom set of payload readers. */ public TsExtractor(@Mode int mode, TimestampAdjuster timestampAdjuster, TsPayloadReader.Factory payloadReaderFactory) { this.payloadReaderFactory = Assertions.checkNotNull(payloadReaderFactory); this.mode = mode; if (mode == MODE_SINGLE_PMT || mode == MODE_HLS) { timestampAdjusters = Collections.singletonList(timestampAdjuster); } else { timestampAdjusters = new ArrayList<>(); timestampAdjusters.add(timestampAdjuster); } tsPacketBuffer = new ParsableByteArray(BUFFER_SIZE); tsScratch = new ParsableBitArray(new byte[3]); trackIds = new SparseBooleanArray(); tsPayloadReaders = new SparseArray<>(); continuityCounters = new SparseIntArray(); resetPayloadReaders(); }
/** * Parses a page composition segment, as defined by ETSI EN 300 743 7.2.2. */ private static PageComposition parsePageComposition(ParsableBitArray data, int length) { int timeoutSecs = data.readBits(8); int version = data.readBits(4); int state = data.readBits(2); data.skipBits(2); int remainingLength = length - 2; SparseArray<PageRegion> regions = new SparseArray<>(); while (remainingLength > 0) { int regionId = data.readBits(8); data.skipBits(8); // Skip reserved. int regionHorizontalAddress = data.readBits(16); int regionVerticalAddress = data.readBits(16); remainingLength -= 6; regions.put(regionId, new PageRegion(regionHorizontalAddress, regionVerticalAddress)); } return new PageComposition(timeoutSecs, version, state, regions); }
/** * Returns the DTS format given {@code data} containing the DTS frame according to ETSI TS 102 114 * subsections 5.3/5.4. * * @param frame The DTS frame to parse. * @param trackId The track identifier to set on the format, or null. * @param language The language to set on the format. * @param drmInitData {@link DrmInitData} to be included in the format. * @return The DTS format parsed from data in the header. */ public static Format parseDtsFormat(byte[] frame, String trackId, String language, DrmInitData drmInitData) { ParsableBitArray frameBits = new ParsableBitArray(frame); frameBits.skipBits(4 * 8 + 1 + 5 + 1 + 7 + 14); // SYNC, FTYPE, SHORT, CPF, NBLKS, FSIZE int amode = frameBits.readBits(6); int channelCount = CHANNELS_BY_AMODE[amode]; int sfreq = frameBits.readBits(4); int sampleRate = SAMPLE_RATE_BY_SFREQ[sfreq]; int rate = frameBits.readBits(5); int bitrate = rate >= TWICE_BITRATE_KBPS_BY_RATE.length ? Format.NO_VALUE : TWICE_BITRATE_KBPS_BY_RATE[rate] * 1000 / 2; frameBits.skipBits(10); // MIX, DYNF, TIMEF, AUXF, HDCD, EXT_AUDIO_ID, EXT_AUDIO, ASPF channelCount += frameBits.readBits(2) > 0 ? 1 : 0; // LFF return Format.createAudioSampleFormat(trackId, MimeTypes.AUDIO_DTS, null, bitrate, Format.NO_VALUE, channelCount, sampleRate, null, drmInitData, 0, language); }
/** * @param mode Mode for the extractor. One of {@link #MODE_NORMAL}, {@link #MODE_SINGLE_PMT} * and {@link #MODE_HLS}. * @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps. * @param payloadReaderFactory Factory for injecting a custom set of payload readers. */ public TsExtractor(@Mode int mode, TimestampAdjuster timestampAdjuster, TsPayloadReader.Factory payloadReaderFactory) { this.payloadReaderFactory = Assertions.checkNotNull(payloadReaderFactory); this.mode = mode; if (mode == MODE_SINGLE_PMT || mode == MODE_HLS) { timestampAdjusters = Collections.singletonList(timestampAdjuster); } else { timestampAdjusters = new ArrayList<>(); timestampAdjusters.add(timestampAdjuster); } tsPacketBuffer = new ParsableByteArray(BUFFER_SIZE); tsScratch = new ParsableBitArray(new byte[3]); trackIds = new SparseBooleanArray(); tsPayloadReaders = new SparseArray<>(); continuityCounters = new SparseIntArray(); resetPayloadReaders(); }
/** * Returns the AC-3 format given {@code data} containing a syncframe. The reading position of * {@code data} will be modified. * * @param data The data to parse, positioned at the start of the syncframe. * @param trackId The track identifier to set on the format, or null. * @param language The language to set on the format. * @param drmInitData {@link DrmInitData} to be included in the format. * @return The AC-3 format parsed from data in the header. */ public static Format parseAc3SyncframeFormat(ParsableBitArray data, String trackId, String language, DrmInitData drmInitData) { data.skipBits(16 + 16); // syncword, crc1 int fscod = data.readBits(2); data.skipBits(6 + 5 + 3); // frmsizecod, bsid, bsmod int acmod = data.readBits(3); if ((acmod & 0x01) != 0 && acmod != 1) { data.skipBits(2); // cmixlev } if ((acmod & 0x04) != 0) { data.skipBits(2); // surmixlev } if (acmod == 2) { data.skipBits(2); // dsurmod } boolean lfeon = data.readBit(); return Format.createAudioSampleFormat(trackId, MimeTypes.AUDIO_AC3, null, Format.NO_VALUE, Format.NO_VALUE, CHANNEL_COUNT_BY_ACMOD[acmod] + (lfeon ? 1 : 0), SAMPLE_RATE_BY_FSCOD[fscod], null, drmInitData, 0, language); }
/** * Returns the E-AC-3 format given {@code data} containing a syncframe. The reading position of * {@code data} will be modified. * * @param data The data to parse, positioned at the start of the syncframe. * @param trackId The track identifier to set on the format, or null. * @param language The language to set on the format. * @param drmInitData {@link DrmInitData} to be included in the format. * @return The E-AC-3 format parsed from data in the header. */ public static Format parseEac3SyncframeFormat(ParsableBitArray data, String trackId, String language, DrmInitData drmInitData) { data.skipBits(16 + 2 + 3 + 11); // syncword, strmtype, substreamid, frmsiz int sampleRate; int fscod = data.readBits(2); if (fscod == 3) { sampleRate = SAMPLE_RATE_BY_FSCOD2[data.readBits(2)]; } else { data.skipBits(2); // numblkscod sampleRate = SAMPLE_RATE_BY_FSCOD[fscod]; } int acmod = data.readBits(3); boolean lfeon = data.readBit(); return Format.createAudioSampleFormat(trackId, MimeTypes.AUDIO_E_AC3, null, Format.NO_VALUE, Format.NO_VALUE, CHANNEL_COUNT_BY_ACMOD[acmod] + (lfeon ? 1 : 0), sampleRate, null, drmInitData, 0, language); }
/** * Parses an AudioMuxElement as defined in 14496-3:2009, Section 1.7.3.1, Table 1.41. * * @param data A {@link ParsableBitArray} containing the AudioMuxElement's bytes. */ private void parseAudioMuxElement(ParsableBitArray data) throws ParserException { boolean useSameStreamMux = data.readBit(); if (!useSameStreamMux) { streamMuxRead = true; parseStreamMuxConfig(data); } else if (!streamMuxRead) { return; // Parsing cannot continue without StreamMuxConfig information. } if (audioMuxVersionA == 0) { if (numSubframes != 0) { throw new ParserException(); } int muxSlotLengthBytes = parsePayloadLengthInfo(data); parsePayloadMux(data, muxSlotLengthBytes); if (otherDataPresent) { data.skipBits((int) otherDataLenBits); } } else { throw new ParserException(); // Not defined by ISO/IEC 14496-3:2009. } }
private void parseFrameLength(ParsableBitArray data) { frameLengthType = data.readBits(3); switch (frameLengthType) { case 0: data.skipBits(8); // latmBufferFullness. break; case 1: data.skipBits(9); // frameLength. break; case 3: case 4: case 5: data.skipBits(6); // CELPframeLengthTableIndex. break; case 6: case 7: data.skipBits(1); // HVXCframeLengthTableIndex. break; } }
private void parsePayloadMux(ParsableBitArray data, int muxLengthBytes) { // The start of sample data in int bitPosition = data.getPosition(); if ((bitPosition & 0x07) == 0) { // Sample data is byte-aligned. We can output it directly. sampleDataBuffer.setPosition(bitPosition >> 3); } else { // Sample data is not byte-aligned and we need align it ourselves before outputting. // Byte alignment is needed because LATM framing is not supported by MediaCodec. data.readBits(sampleDataBuffer.data, 0, muxLengthBytes * 8); sampleDataBuffer.setPosition(0); } output.sampleData(sampleDataBuffer, muxLengthBytes); output.sampleMetadata(timeUs, C.BUFFER_FLAG_KEY_FRAME, muxLengthBytes, 0, null); timeUs += sampleDurationUs; }
/** * Constructs a new reader for (E-)AC-3 elementary streams. * * @param language Track language. */ public Ac3Reader(String language) { headerScratchBits = new ParsableBitArray(new byte[HEADER_SIZE]); headerScratchBytes = new ParsableByteArray(headerScratchBits.data); state = STATE_FINDING_SYNC; this.language = language; }
/** * @param exposeId3 True if the reader should expose ID3 information. * @param language Track language. */ public AdtsReader(boolean exposeId3, String language) { adtsScratch = new ParsableBitArray(new byte[HEADER_SIZE + CRC_SIZE]); id3HeaderBuffer = new ParsableByteArray(Arrays.copyOf(ID3_IDENTIFIER, ID3_HEADER_SIZE)); setFindingSampleState(); this.exposeId3 = exposeId3; this.language = language; }
public Cea708Decoder(int accessibilityChannel) { ccData = new ParsableByteArray(); serviceBlockPacket = new ParsableBitArray(); selectedServiceNumber = (accessibilityChannel == Format.NO_VALUE) ? 1 : accessibilityChannel; cueBuilders = new CueBuilder[NUM_WINDOWS]; for (int i = 0; i < NUM_WINDOWS; i++) { cueBuilders[i] = new CueBuilder(); } currentCueBuilder = cueBuilders[0]; resetCueBuilders(); }
/** * Parses a display definition segment, as defined by ETSI EN 300 743 7.2.1. */ private static DisplayDefinition parseDisplayDefinition(ParsableBitArray data) { data.skipBits(4); // dds_version_number (4). boolean displayWindowFlag = data.readBit(); data.skipBits(3); // Skip reserved. int width = data.readBits(16); int height = data.readBits(16); int horizontalPositionMinimum; int horizontalPositionMaximum; int verticalPositionMinimum; int verticalPositionMaximum; if (displayWindowFlag) { horizontalPositionMinimum = data.readBits(16); horizontalPositionMaximum = data.readBits(16); verticalPositionMinimum = data.readBits(16); verticalPositionMaximum = data.readBits(16); } else { horizontalPositionMinimum = 0; horizontalPositionMaximum = width; verticalPositionMinimum = 0; verticalPositionMaximum = height; } return new DisplayDefinition(width, height, horizontalPositionMinimum, horizontalPositionMaximum, verticalPositionMinimum, verticalPositionMaximum); }
/** * Parses an object data segment, as defined by ETSI EN 300 743 7.2.5. * * @return The parsed object data. */ private static ObjectData parseObjectData(ParsableBitArray data) { int objectId = data.readBits(16); data.skipBits(4); // Skip object_version_number int objectCodingMethod = data.readBits(2); boolean nonModifyingColorFlag = data.readBit(); data.skipBits(1); // Skip reserved. byte[] topFieldData = null; byte[] bottomFieldData = null; if (objectCodingMethod == OBJECT_CODING_STRING) { int numberOfCodes = data.readBits(8); // TODO: Parse and use character_codes. data.skipBits(numberOfCodes * 16); // Skip character_codes. } else if (objectCodingMethod == OBJECT_CODING_PIXELS) { int topFieldDataLength = data.readBits(16); int bottomFieldDataLength = data.readBits(16); if (topFieldDataLength > 0) { topFieldData = new byte[topFieldDataLength]; data.readBytes(topFieldData, 0, topFieldDataLength); } if (bottomFieldDataLength > 0) { bottomFieldData = new byte[bottomFieldDataLength]; data.readBytes(bottomFieldData, 0, bottomFieldDataLength); } else { bottomFieldData = topFieldData; } } return new ObjectData(objectId, nonModifyingColorFlag, topFieldData, bottomFieldData); }
/** * Paint an 8-bit/pixel code string, as defined by ETSI EN 300 743 7.2.5.2, to a canvas. */ private static int paint8BitPixelCodeString(ParsableBitArray data, int[] clutEntries, byte[] clutMapTable, int column, int line, Paint paint, Canvas canvas) { boolean endOfPixelCodeString = false; do { int runLength = 0; int clutIndex = 0; int peek = data.readBits(8); if (peek != 0x00) { runLength = 1; clutIndex = peek; } else { if (!data.readBit()) { peek = data.readBits(7); if (peek != 0x00) { runLength = peek; clutIndex = 0x00; } else { endOfPixelCodeString = true; } } else { runLength = data.readBits(7); clutIndex = data.readBits(8); } } if (runLength != 0 && paint != null) { paint.setColor(clutEntries[clutMapTable != null ? clutMapTable[clutIndex] : clutIndex]); canvas.drawRect(column, line, column + runLength, line + 1, paint); } column += runLength; } while (!endOfPixelCodeString); return column; }
private static byte[] buildClutMapTable(int length, int bitsPerEntry, ParsableBitArray data) { byte[] clutMapTable = new byte[length]; for (int i = 0; i < length; i++) { clutMapTable[i] = (byte) data.readBits(bitsPerEntry); } return clutMapTable; }
/** * @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps. * @param customReaderFactory Factory for injecting a custom set of elementary stream readers. * @param mapByType True if {@link TrackOutput}s should be mapped by their type, false to map them * by their PID. */ public TsExtractor(TimestampAdjuster timestampAdjuster, ElementaryStreamReader.Factory customReaderFactory, boolean mapByType) { this.timestampAdjuster = timestampAdjuster; this.streamReaderFactory = Assertions.checkNotNull(customReaderFactory); this.mapByType = mapByType; tsPacketBuffer = new ParsableByteArray(BUFFER_SIZE); tsScratch = new ParsableBitArray(new byte[3]); trackIds = new SparseBooleanArray(); tsPayloadReaders = new SparseArray<>(); continuityCounters = new SparseIntArray(); resetPayloadReaders(); }
public PesReader(ElementaryStreamReader pesPayloadReader, TimestampAdjuster timestampAdjuster) { this.pesPayloadReader = pesPayloadReader; this.timestampAdjuster = timestampAdjuster; pesScratch = new ParsableBitArray(new byte[PES_SCRATCH_SIZE]); state = STATE_FINDING_HEADER; }
private int parseAudioSpecificConfig(ParsableBitArray data) throws ParserException { int bitsLeft = data.bitsLeft(); Pair<Integer, Integer> config = CodecSpecificDataUtil.parseAacAudioSpecificConfig(data, true); sampleRateHz = config.first; channelCount = config.second; return bitsLeft - data.bitsLeft(); }
private int parsePayloadLengthInfo(ParsableBitArray data) throws ParserException { int muxSlotLengthBytes = 0; // Assuming single program and single layer. if (frameLengthType == 0) { int tmp; do { tmp = data.readBits(8); muxSlotLengthBytes += tmp; } while (tmp == 255); return muxSlotLengthBytes; } else { throw new ParserException(); } }
@Override public boolean sniff(ExtractorInput input) throws IOException, InterruptedException { // Skip any ID3 headers. ParsableByteArray scratch = new ParsableByteArray(10); ParsableBitArray scratchBits = new ParsableBitArray(scratch.data); int startPosition = 0; while (true) { input.peekFully(scratch.data, 0, 10); scratch.setPosition(0); if (scratch.readUnsignedInt24() != ID3_TAG) { break; } scratch.skipBytes(3); int length = scratch.readSynchSafeInt(); startPosition += 10 + length; input.advancePeekPosition(length); } input.resetPeekPosition(); input.advancePeekPosition(startPosition); // Try to find four or more consecutive AAC audio frames, exceeding the MPEG TS packet size. int headerPosition = startPosition; int validFramesSize = 0; int validFramesCount = 0; while (true) { input.peekFully(scratch.data, 0, 2); scratch.setPosition(0); int syncBytes = scratch.readUnsignedShort(); if ((syncBytes & 0xFFF6) != 0xFFF0) { validFramesCount = 0; validFramesSize = 0; input.resetPeekPosition(); if (++headerPosition - startPosition >= MAX_SNIFF_BYTES) { return false; } input.advancePeekPosition(headerPosition); } else { if (++validFramesCount >= 4 && validFramesSize > 188) { return true; } // Skip the frame. input.peekFully(scratch.data, 0, 4); scratchBits.setPosition(14); int frameSize = scratchBits.readBits(13); // Either the stream is malformed OR we're not parsing an ADTS stream. if (frameSize <= 6) { return false; } input.advancePeekPosition(frameSize - 6); validFramesSize += frameSize; } } }
public PatReader() { patScratch = new ParsableBitArray(new byte[4]); }