@SuppressWarnings("ReferenceEquality") public Format copyWithManifestFormatInfo(Format manifestFormat) { if (this == manifestFormat) { // No need to copy from ourselves. return this; } String id = manifestFormat.id; String codecs = this.codecs == null ? manifestFormat.codecs : this.codecs; int bitrate = this.bitrate == NO_VALUE ? manifestFormat.bitrate : this.bitrate; float frameRate = this.frameRate == NO_VALUE ? manifestFormat.frameRate : this.frameRate; @C.SelectionFlags int selectionFlags = this.selectionFlags | manifestFormat.selectionFlags; String language = this.language == null ? manifestFormat.language : this.language; DrmInitData drmInitData = manifestFormat.drmInitData != null ? manifestFormat.drmInitData : this.drmInitData; return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize, width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData, drmInitData, metadata); }
/** Returns DrmInitData from leaf atoms. */ private static DrmInitData getDrmInitDataFromAtoms(List<Atom.LeafAtom> leafChildren) { ArrayList<SchemeData> schemeDatas = null; int leafChildrenSize = leafChildren.size(); for (int i = 0; i < leafChildrenSize; i++) { LeafAtom child = leafChildren.get(i); if (child.type == Atom.TYPE_pssh) { if (schemeDatas == null) { schemeDatas = new ArrayList<>(); } byte[] psshData = child.data.data; UUID uuid = PsshAtomUtil.parseUuid(psshData); if (uuid == null) { Log.w(TAG, "Skipped pssh atom (failed to extract uuid)"); } else { schemeDatas.add(new SchemeData(uuid, MimeTypes.VIDEO_MP4, psshData)); } } } return schemeDatas == null ? null : new DrmInitData(schemeDatas); }
/** * Returns the E-AC-3 format given {@code data} containing the EC3SpecificBox according to * ETSI TS 102 366 Annex F. The reading position of {@code data} will be modified. * * @param data The EC3SpecificBox 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 E-AC-3 format parsed from data in the header. */ public static Format parseEAc3AnnexFFormat(ParsableByteArray data, String trackId, String language, DrmInitData drmInitData) { data.skipBytes(2); // data_rate, num_ind_sub // Read only the first substream. // TODO: Read later substreams? int fscod = (data.readUnsignedByte() & 0xC0) >> 6; int sampleRate = SAMPLE_RATE_BY_FSCOD[fscod]; int nextByte = data.readUnsignedByte(); int channelCount = CHANNEL_COUNT_BY_ACMOD[(nextByte & 0x0E) >> 1]; if ((nextByte & 0x01) != 0) { // lfeon channelCount++; } return Format.createAudioSampleFormat(trackId, MimeTypes.AUDIO_E_AC3, null, Format.NO_VALUE, Format.NO_VALUE, channelCount, sampleRate, null, drmInitData, 0, language); }
/** * 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); }
@SuppressWarnings("PMD.AvoidCatchingGenericException") // We are forced to catch Exception as ResourceBusyException is minSdk 19 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) @Override public DrmSession<FrameworkMediaCrypto> acquireSession(Looper playbackLooper, DrmInitData drmInitData) { DrmSession<FrameworkMediaCrypto> drmSession; try { SessionId sessionId = SessionId.of(mediaDrm.openSession()); FrameworkMediaCrypto mediaCrypto = mediaDrm.createMediaCrypto(sessionId.asBytes()); mediaDrm.restoreKeys(sessionId.asBytes(), keySetIdToRestore.asBytes()); drmSession = new LocalDrmSession(mediaCrypto, keySetIdToRestore, sessionId); } catch (Exception exception) { drmSession = new InvalidDrmSession(new DrmSession.DrmSessionException(exception)); notifyErrorListener(drmSession); } return drmSession; }
public Format copyWithManifestFormatInfo(Format manifestFormat) { if (this == manifestFormat) { // No need to copy from ourselves. return this; } String id = manifestFormat.id; String codecs = this.codecs == null ? manifestFormat.codecs : this.codecs; int bitrate = this.bitrate == NO_VALUE ? manifestFormat.bitrate : this.bitrate; float frameRate = this.frameRate == NO_VALUE ? manifestFormat.frameRate : this.frameRate; @C.SelectionFlags int selectionFlags = this.selectionFlags | manifestFormat.selectionFlags; String language = this.language == null ? manifestFormat.language : this.language; DrmInitData drmInitData = manifestFormat.drmInitData != null ? manifestFormat.drmInitData : this.drmInitData; return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize, width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData, drmInitData, metadata); }
@Override public Object build() { StreamElement[] streamElementArray = new StreamElement[streamElements.size()]; streamElements.toArray(streamElementArray); if (protectionElement != null) { DrmInitData drmInitData = new DrmInitData(new SchemeData(protectionElement.uuid, MimeTypes.VIDEO_MP4, protectionElement.data)); for (StreamElement streamElement : streamElementArray) { for (int i = 0; i < streamElement.formats.length; i++) { streamElement.formats[i] = streamElement.formats[i].copyWithDrmInitData(drmInitData); } } } return new SsManifest(majorVersion, minorVersion, timescale, duration, dvrWindowLength, lookAheadCount, isLive, protectionElement, streamElementArray); }
/** * 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); }
public void testParcelable() { DrmInitData.SchemeData DRM_DATA_1 = new DrmInitData.SchemeData(WIDEVINE_UUID, VIDEO_MP4, TestUtil.buildTestData(128, 1 /* data seed */)); DrmInitData.SchemeData DRM_DATA_2 = new DrmInitData.SchemeData(C.UUID_NIL, VIDEO_WEBM, TestUtil.buildTestData(128, 1 /* data seed */)); DrmInitData drmInitData = new DrmInitData(DRM_DATA_1, DRM_DATA_2); byte[] projectionData = new byte[] {1, 2, 3}; Format formatToParcel = new Format("id", MimeTypes.VIDEO_MP4, MimeTypes.VIDEO_H264, null, 1024, 2048, 1920, 1080, 24, 90, 2, projectionData, C.STEREO_MODE_TOP_BOTTOM, 6, 44100, C.ENCODING_PCM_24BIT, 1001, 1002, 0, "und", Format.OFFSET_SAMPLE_RELATIVE, INIT_DATA, drmInitData); Parcel parcel = Parcel.obtain(); formatToParcel.writeToParcel(parcel, 0); parcel.setDataPosition(0); Format formatFromParcel = Format.CREATOR.createFromParcel(parcel); assertEquals(formatToParcel, formatFromParcel); parcel.recycle(); }
protected Representation buildRepresentation(RepresentationInfo representationInfo, String contentId, String extraDrmSchemeType, ArrayList<SchemeData> extraDrmSchemeDatas, ArrayList<Descriptor> extraInbandEventStreams) { Format format = representationInfo.format; String drmSchemeType = representationInfo.drmSchemeType != null ? representationInfo.drmSchemeType : extraDrmSchemeType; ArrayList<SchemeData> drmSchemeDatas = representationInfo.drmSchemeDatas; drmSchemeDatas.addAll(extraDrmSchemeDatas); if (!drmSchemeDatas.isEmpty()) { filterRedundantIncompleteSchemeDatas(drmSchemeDatas); DrmInitData drmInitData = new DrmInitData(drmSchemeType, drmSchemeDatas); format = format.copyWithDrmInitData(drmInitData); } ArrayList<Descriptor> inbandEventStreams = representationInfo.inbandEventStreams; inbandEventStreams.addAll(extraInbandEventStreams); return Representation.newInstance(contentId, representationInfo.revisionId, format, representationInfo.baseUrl, representationInfo.segmentBase, inbandEventStreams); }
/** * Loads {@link DrmInitData} for a given period in a DASH manifest. * * @param dataSource The {@link HttpDataSource} from which data should be loaded. * @param period The {@link Period}. * @return The loaded {@link DrmInitData}, or null if none is defined. * @throws IOException Thrown when there is an error while loading. * @throws InterruptedException Thrown if the thread was interrupted. */ public static DrmInitData loadDrmInitData(DataSource dataSource, Period period) throws IOException, InterruptedException { int primaryTrackType = C.TRACK_TYPE_VIDEO; Representation representation = getFirstRepresentation(period, primaryTrackType); if (representation == null) { primaryTrackType = C.TRACK_TYPE_AUDIO; representation = getFirstRepresentation(period, primaryTrackType); if (representation == null) { return null; } } DrmInitData drmInitData = representation.format.drmInitData; if (drmInitData != null) { // Prefer drmInitData obtained from the manifest over drmInitData obtained from the stream, // as per DASH IF Interoperability Recommendations V3.0, 7.5.3. return drmInitData; } Format sampleFormat = DashUtil.loadSampleFormat(dataSource, primaryTrackType, representation); return sampleFormat == null ? null : sampleFormat.drmInitData; }
@SuppressWarnings("ReferenceEquality") public Format copyWithManifestFormatInfo(Format manifestFormat) { if (this == manifestFormat) { // No need to copy from ourselves. return this; } String id = manifestFormat.id; String codecs = this.codecs == null ? manifestFormat.codecs : this.codecs; int bitrate = this.bitrate == NO_VALUE ? manifestFormat.bitrate : this.bitrate; float frameRate = this.frameRate == NO_VALUE ? manifestFormat.frameRate : this.frameRate; @C.SelectionFlags int selectionFlags = this.selectionFlags | manifestFormat.selectionFlags; String language = this.language == null ? manifestFormat.language : this.language; DrmInitData drmInitData = manifestFormat.drmInitData != null ? getFilledManifestDrmData(manifestFormat.drmInitData) : this.drmInitData; return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize, width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData, drmInitData, metadata); }
/** * @param flags Flags that control the extractor's behavior. * @param timestampAdjuster Adjusts sample timestamps. May be null if no adjustment is needed. * @param sideloadedTrack Sideloaded track information, in the case that the extractor * will not receive a moov box in the input data. Null if a moov box is expected. * @param sideloadedDrmInitData The {@link DrmInitData} to use for encrypted tracks. If null, the * pssh boxes (if present) will be used. * @param closedCaptionFormats For tracks that contain SEI messages, the formats of the closed * caption channels to expose. */ public FragmentedMp4Extractor(@Flags int flags, TimestampAdjuster timestampAdjuster, Track sideloadedTrack, DrmInitData sideloadedDrmInitData, List<Format> closedCaptionFormats) { this.flags = flags | (sideloadedTrack != null ? FLAG_SIDELOADED : 0); this.timestampAdjuster = timestampAdjuster; this.sideloadedTrack = sideloadedTrack; this.sideloadedDrmInitData = sideloadedDrmInitData; this.closedCaptionFormats = Collections.unmodifiableList(closedCaptionFormats); atomHeader = new ParsableByteArray(Atom.LONG_HEADER_SIZE); nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE); nalPrefix = new ParsableByteArray(5); nalBuffer = new ParsableByteArray(); encryptionSignalByte = new ParsableByteArray(1); defaultInitializationVector = new ParsableByteArray(); extendedTypeScratch = new byte[16]; containerAtoms = new Stack<>(); pendingMetadataSampleInfos = new LinkedList<>(); trackBundles = new SparseArray<>(); durationUs = C.TIME_UNSET; segmentIndexEarliestPresentationTimeUs = C.TIME_UNSET; enterReadingAtomHeaderState(); }
public static Format createVideoSampleFormat(String id, String sampleMimeType, String codecs, int bitrate, int maxInputSize, int width, int height, float frameRate, List<byte[]> initializationData, int rotationDegrees, float pixelWidthHeightRatio, DrmInitData drmInitData) { return createVideoSampleFormat(id, sampleMimeType, codecs, bitrate, maxInputSize, width, height, frameRate, initializationData, rotationDegrees, pixelWidthHeightRatio, null, NO_VALUE, null, drmInitData); }
public static Format createVideoSampleFormat(String id, String sampleMimeType, String codecs, int bitrate, int maxInputSize, int width, int height, float frameRate, List<byte[]> initializationData, int rotationDegrees, float pixelWidthHeightRatio, byte[] projectionData, @C.StereoMode int stereoMode, ColorInfo colorInfo, DrmInitData drmInitData) { return new Format(id, null, sampleMimeType, codecs, bitrate, maxInputSize, width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, colorInfo, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, 0, null, NO_VALUE, OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData, null); }
public static Format createAudioSampleFormat(String id, String sampleMimeType, String codecs, int bitrate, int maxInputSize, int channelCount, int sampleRate, List<byte[]> initializationData, DrmInitData drmInitData, @C.SelectionFlags int selectionFlags, String language) { return createAudioSampleFormat(id, sampleMimeType, codecs, bitrate, maxInputSize, channelCount, sampleRate, NO_VALUE, initializationData, drmInitData, selectionFlags, language); }
public static Format createAudioSampleFormat(String id, String sampleMimeType, String codecs, int bitrate, int maxInputSize, int channelCount, int sampleRate, @C.PcmEncoding int pcmEncoding, List<byte[]> initializationData, DrmInitData drmInitData, @C.SelectionFlags int selectionFlags, String language) { return createAudioSampleFormat(id, sampleMimeType, codecs, bitrate, maxInputSize, channelCount, sampleRate, pcmEncoding, NO_VALUE, NO_VALUE, initializationData, drmInitData, selectionFlags, language, null); }
public static Format createAudioSampleFormat(String id, String sampleMimeType, String codecs, int bitrate, int maxInputSize, int channelCount, int sampleRate, @C.PcmEncoding int pcmEncoding, int encoderDelay, int encoderPadding, List<byte[]> initializationData, DrmInitData drmInitData, @C.SelectionFlags int selectionFlags, String language, Metadata metadata) { return new Format(id, null, sampleMimeType, codecs, bitrate, maxInputSize, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, selectionFlags, language, NO_VALUE, OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData, metadata); }
public static Format createTextSampleFormat(String id, String sampleMimeType, String codecs, int bitrate, @C.SelectionFlags int selectionFlags, String language, int accessibilityChannel, DrmInitData drmInitData, long subsampleOffsetUs, List<byte[]> initializationData) { return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData, drmInitData, null); }
public static Format createImageSampleFormat(String id, String sampleMimeType, String codecs, int bitrate, List<byte[]> initializationData, String language, DrmInitData drmInitData) { return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, 0, language, NO_VALUE, OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData, null); }
Format(String id, String containerMimeType, String sampleMimeType, String codecs, int bitrate, int maxInputSize, int width, int height, float frameRate, int rotationDegrees, float pixelWidthHeightRatio, byte[] projectionData, @C.StereoMode int stereoMode, ColorInfo colorInfo, int channelCount, int sampleRate, @C.PcmEncoding int pcmEncoding, int encoderDelay, int encoderPadding, @C.SelectionFlags int selectionFlags, String language, int accessibilityChannel, long subsampleOffsetUs, List<byte[]> initializationData, DrmInitData drmInitData, Metadata metadata) { this.id = id; this.containerMimeType = containerMimeType; this.sampleMimeType = sampleMimeType; this.codecs = codecs; this.bitrate = bitrate; this.maxInputSize = maxInputSize; this.width = width; this.height = height; this.frameRate = frameRate; this.rotationDegrees = rotationDegrees; this.pixelWidthHeightRatio = pixelWidthHeightRatio; this.projectionData = projectionData; this.stereoMode = stereoMode; this.colorInfo = colorInfo; this.channelCount = channelCount; this.sampleRate = sampleRate; this.pcmEncoding = pcmEncoding; this.encoderDelay = encoderDelay; this.encoderPadding = encoderPadding; this.selectionFlags = selectionFlags; this.language = language; this.accessibilityChannel = accessibilityChannel; this.subsampleOffsetUs = subsampleOffsetUs; this.initializationData = initializationData == null ? Collections.<byte[]>emptyList() : initializationData; this.drmInitData = drmInitData; this.metadata = metadata; }
@SuppressWarnings("ResourceType") /* package */ Format(Parcel in) { id = in.readString(); containerMimeType = in.readString(); sampleMimeType = in.readString(); codecs = in.readString(); bitrate = in.readInt(); maxInputSize = in.readInt(); width = in.readInt(); height = in.readInt(); frameRate = in.readFloat(); rotationDegrees = in.readInt(); pixelWidthHeightRatio = in.readFloat(); boolean hasProjectionData = in.readInt() != 0; projectionData = hasProjectionData ? in.createByteArray() : null; stereoMode = in.readInt(); colorInfo = in.readParcelable(ColorInfo.class.getClassLoader()); channelCount = in.readInt(); sampleRate = in.readInt(); pcmEncoding = in.readInt(); encoderDelay = in.readInt(); encoderPadding = in.readInt(); selectionFlags = in.readInt(); language = in.readString(); accessibilityChannel = in.readInt(); subsampleOffsetUs = in.readLong(); int initializationDataSize = in.readInt(); initializationData = new ArrayList<>(initializationDataSize); for (int i = 0; i < initializationDataSize; i++) { initializationData.add(in.createByteArray()); } drmInitData = in.readParcelable(DrmInitData.class.getClassLoader()); metadata = in.readParcelable(Metadata.class.getClassLoader()); }
public Format copyWithDrmInitData(DrmInitData drmInitData) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize, width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData, drmInitData, metadata); }
/** * Parses a trak atom (defined in 14496-12). * * @param trak Atom to decode. * @param mvhd Movie header atom, used to get the timescale. * @param duration The duration in units of the timescale declared in the mvhd atom, or * {@link C#TIME_UNSET} if the duration should be parsed from the tkhd atom. * @param drmInitData {@link DrmInitData} to be included in the format. * @param isQuickTime True for QuickTime media. False otherwise. * @return A {@link Track} instance, or {@code null} if the track's type isn't supported. */ public static Track parseTrak(Atom.ContainerAtom trak, Atom.LeafAtom mvhd, long duration, DrmInitData drmInitData, boolean isQuickTime) throws ParserException { Atom.ContainerAtom mdia = trak.getContainerAtomOfType(Atom.TYPE_mdia); int trackType = parseHdlr(mdia.getLeafAtomOfType(Atom.TYPE_hdlr).data); if (trackType == C.TRACK_TYPE_UNKNOWN) { return null; } TkhdData tkhdData = parseTkhd(trak.getLeafAtomOfType(Atom.TYPE_tkhd).data); if (duration == C.TIME_UNSET) { duration = tkhdData.duration; } long movieTimescale = parseMvhd(mvhd.data); long durationUs; if (duration == C.TIME_UNSET) { durationUs = C.TIME_UNSET; } else { durationUs = Util.scaleLargeTimestamp(duration, C.MICROS_PER_SECOND, movieTimescale); } Atom.ContainerAtom stbl = mdia.getContainerAtomOfType(Atom.TYPE_minf) .getContainerAtomOfType(Atom.TYPE_stbl); Pair<Long, String> mdhdData = parseMdhd(mdia.getLeafAtomOfType(Atom.TYPE_mdhd).data); StsdData stsdData = parseStsd(stbl.getLeafAtomOfType(Atom.TYPE_stsd).data, tkhdData.id, tkhdData.rotationDegrees, mdhdData.second, drmInitData, isQuickTime); Pair<long[], long[]> edtsData = parseEdts(trak.getContainerAtomOfType(Atom.TYPE_edts)); return stsdData.format == null ? null : new Track(tkhdData.id, trackType, mdhdData.first, movieTimescale, durationUs, stsdData.format, stsdData.requiredSampleTransformation, stsdData.trackEncryptionBoxes, stsdData.nalUnitLengthFieldLength, edtsData.first, edtsData.second); }
/** * Parses a stsd atom (defined in 14496-12). * * @param stsd The stsd atom to decode. * @param trackId The track's identifier in its container. * @param rotationDegrees The rotation of the track in degrees. * @param language The language of the track. * @param drmInitData {@link DrmInitData} to be included in the format. * @param isQuickTime True for QuickTime media. False otherwise. * @return An object containing the parsed data. */ private static StsdData parseStsd(ParsableByteArray stsd, int trackId, int rotationDegrees, String language, DrmInitData drmInitData, boolean isQuickTime) throws ParserException { stsd.setPosition(Atom.FULL_HEADER_SIZE); int numberOfEntries = stsd.readInt(); StsdData out = new StsdData(numberOfEntries); for (int i = 0; i < numberOfEntries; i++) { int childStartPosition = stsd.getPosition(); int childAtomSize = stsd.readInt(); Assertions.checkArgument(childAtomSize > 0, "childAtomSize should be positive"); int childAtomType = stsd.readInt(); if (childAtomType == Atom.TYPE_avc1 || childAtomType == Atom.TYPE_avc3 || childAtomType == Atom.TYPE_encv || childAtomType == Atom.TYPE_mp4v || childAtomType == Atom.TYPE_hvc1 || childAtomType == Atom.TYPE_hev1 || childAtomType == Atom.TYPE_s263 || childAtomType == Atom.TYPE_vp08 || childAtomType == Atom.TYPE_vp09) { parseVideoSampleEntry(stsd, childAtomType, childStartPosition, childAtomSize, trackId, rotationDegrees, drmInitData, out, i); } else if (childAtomType == Atom.TYPE_mp4a || childAtomType == Atom.TYPE_enca || childAtomType == Atom.TYPE_ac_3 || childAtomType == Atom.TYPE_ec_3 || childAtomType == Atom.TYPE_dtsc || childAtomType == Atom.TYPE_dtse || childAtomType == Atom.TYPE_dtsh || childAtomType == Atom.TYPE_dtsl || childAtomType == Atom.TYPE_samr || childAtomType == Atom.TYPE_sawb || childAtomType == Atom.TYPE_lpcm || childAtomType == Atom.TYPE_sowt || childAtomType == Atom.TYPE__mp3 || childAtomType == Atom.TYPE_alac) { parseAudioSampleEntry(stsd, childAtomType, childStartPosition, childAtomSize, trackId, language, isQuickTime, drmInitData, out, i); } else if (childAtomType == Atom.TYPE_TTML || childAtomType == Atom.TYPE_tx3g || childAtomType == Atom.TYPE_wvtt || childAtomType == Atom.TYPE_stpp || childAtomType == Atom.TYPE_c608) { parseTextSampleEntry(stsd, childAtomType, childStartPosition, childAtomSize, trackId, language, drmInitData, out); } else if (childAtomType == Atom.TYPE_camm) { out.format = Format.createSampleFormat(Integer.toString(trackId), MimeTypes.APPLICATION_CAMERA_MOTION, null, Format.NO_VALUE, drmInitData); } stsd.setPosition(childStartPosition + childAtomSize); } return out; }
private static void parseTextSampleEntry(ParsableByteArray parent, int atomType, int position, int atomSize, int trackId, String language, DrmInitData drmInitData, StsdData out) throws ParserException { parent.setPosition(position + Atom.HEADER_SIZE + StsdData.STSD_HEADER_SIZE); // Default values. List<byte[]> initializationData = null; long subsampleOffsetUs = Format.OFFSET_SAMPLE_RELATIVE; String mimeType; if (atomType == Atom.TYPE_TTML) { mimeType = MimeTypes.APPLICATION_TTML; } else if (atomType == Atom.TYPE_tx3g) { mimeType = MimeTypes.APPLICATION_TX3G; int sampleDescriptionLength = atomSize - Atom.HEADER_SIZE - 8; byte[] sampleDescriptionData = new byte[sampleDescriptionLength]; parent.readBytes(sampleDescriptionData, 0, sampleDescriptionLength); initializationData = Collections.singletonList(sampleDescriptionData); } else if (atomType == Atom.TYPE_wvtt) { mimeType = MimeTypes.APPLICATION_MP4VTT; } else if (atomType == Atom.TYPE_stpp) { mimeType = MimeTypes.APPLICATION_TTML; subsampleOffsetUs = 0; // Subsample timing is absolute. } else if (atomType == Atom.TYPE_c608) { // Defined by the QuickTime File Format specification. mimeType = MimeTypes.APPLICATION_MP4CEA608; out.requiredSampleTransformation = Track.TRANSFORMATION_CEA608_CDAT; } else { // Never happens. throw new IllegalStateException(); } out.format = Format.createTextSampleFormat(Integer.toString(trackId), mimeType, null, Format.NO_VALUE, 0, language, Format.NO_VALUE, drmInitData, subsampleOffsetUs, initializationData); }
private void onMoofContainerAtomRead(ContainerAtom moof) throws ParserException { parseMoof(moof, trackBundles, flags, extendedTypeScratch); DrmInitData drmInitData = getDrmInitDataFromAtoms(moof.leafChildren); if (drmInitData != null) { int trackCount = trackBundles.size(); for (int i = 0; i < trackCount; i++) { trackBundles.valueAt(i).updateDrmInitData(drmInitData); } } }