Add an extractor flag for ignoring edit lists

Issue: #3358

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=172464053
This commit is contained in:
andrewlewis 2017-10-17 07:33:19 -07:00 committed by Oliver Woodman
parent 2c10e6f1c3
commit f9249d23ea
5 changed files with 64 additions and 11 deletions

View File

@ -67,6 +67,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
} }
private @MatroskaExtractor.Flags int matroskaFlags; private @MatroskaExtractor.Flags int matroskaFlags;
private @Mp4Extractor.Flags int mp4Flags;
private @FragmentedMp4Extractor.Flags int fragmentedMp4Flags; private @FragmentedMp4Extractor.Flags int fragmentedMp4Flags;
private @Mp3Extractor.Flags int mp3Flags; private @Mp3Extractor.Flags int mp3Flags;
private @TsExtractor.Mode int tsMode; private @TsExtractor.Mode int tsMode;
@ -89,6 +90,18 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
return this; return this;
} }
/**
* Sets flags for {@link Mp4Extractor} instances created by the factory.
*
* @see Mp4Extractor#Mp4Extractor(int)
* @param flags The flags to use.
* @return The factory, for convenience.
*/
public synchronized DefaultExtractorsFactory setMp4ExtractorFlags(@Mp4Extractor.Flags int flags) {
this.mp4Flags = flags;
return this;
}
/** /**
* Sets flags for {@link FragmentedMp4Extractor} instances created by the factory. * Sets flags for {@link FragmentedMp4Extractor} instances created by the factory.
* *
@ -145,7 +158,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
Extractor[] extractors = new Extractor[FLAC_EXTRACTOR_CONSTRUCTOR == null ? 11 : 12]; Extractor[] extractors = new Extractor[FLAC_EXTRACTOR_CONSTRUCTOR == null ? 11 : 12];
extractors[0] = new MatroskaExtractor(matroskaFlags); extractors[0] = new MatroskaExtractor(matroskaFlags);
extractors[1] = new FragmentedMp4Extractor(fragmentedMp4Flags); extractors[1] = new FragmentedMp4Extractor(fragmentedMp4Flags);
extractors[2] = new Mp4Extractor(); extractors[2] = new Mp4Extractor(mp4Flags);
extractors[3] = new Mp3Extractor(mp3Flags); extractors[3] = new Mp3Extractor(mp3Flags);
extractors[4] = new AdtsExtractor(); extractors[4] = new AdtsExtractor();
extractors[5] = new Ac3Extractor(); extractors[5] = new Ac3Extractor();

View File

@ -60,11 +60,13 @@ import java.util.List;
* @param duration The duration in units of the timescale declared in the mvhd atom, or * @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. * {@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 drmInitData {@link DrmInitData} to be included in the format.
* @param ignoreEditLists Whether to ignore any edit lists in the trak box.
* @param isQuickTime True for QuickTime media. False otherwise. * @param isQuickTime True for QuickTime media. False otherwise.
* @return A {@link Track} instance, or {@code null} if the track's type isn't supported. * @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, public static Track parseTrak(Atom.ContainerAtom trak, Atom.LeafAtom mvhd, long duration,
DrmInitData drmInitData, boolean isQuickTime) throws ParserException { DrmInitData drmInitData, boolean ignoreEditLists, boolean isQuickTime)
throws ParserException {
Atom.ContainerAtom mdia = trak.getContainerAtomOfType(Atom.TYPE_mdia); Atom.ContainerAtom mdia = trak.getContainerAtomOfType(Atom.TYPE_mdia);
int trackType = parseHdlr(mdia.getLeafAtomOfType(Atom.TYPE_hdlr).data); int trackType = parseHdlr(mdia.getLeafAtomOfType(Atom.TYPE_hdlr).data);
if (trackType == C.TRACK_TYPE_UNKNOWN) { if (trackType == C.TRACK_TYPE_UNKNOWN) {
@ -88,11 +90,17 @@ import java.util.List;
Pair<Long, String> mdhdData = parseMdhd(mdia.getLeafAtomOfType(Atom.TYPE_mdhd).data); Pair<Long, String> mdhdData = parseMdhd(mdia.getLeafAtomOfType(Atom.TYPE_mdhd).data);
StsdData stsdData = parseStsd(stbl.getLeafAtomOfType(Atom.TYPE_stsd).data, tkhdData.id, StsdData stsdData = parseStsd(stbl.getLeafAtomOfType(Atom.TYPE_stsd).data, tkhdData.id,
tkhdData.rotationDegrees, mdhdData.second, drmInitData, isQuickTime); tkhdData.rotationDegrees, mdhdData.second, drmInitData, isQuickTime);
Pair<long[], long[]> edtsData = parseEdts(trak.getContainerAtomOfType(Atom.TYPE_edts)); long[] editListDurations = null;
long[] editListMediaTimes = null;
if (!ignoreEditLists) {
Pair<long[], long[]> edtsData = parseEdts(trak.getContainerAtomOfType(Atom.TYPE_edts));
editListDurations = edtsData.first;
editListMediaTimes = edtsData.second;
}
return stsdData.format == null ? null return stsdData.format == null ? null
: new Track(tkhdData.id, trackType, mdhdData.first, movieTimescale, durationUs, : new Track(tkhdData.id, trackType, mdhdData.first, movieTimescale, durationUs,
stsdData.format, stsdData.requiredSampleTransformation, stsdData.trackEncryptionBoxes, stsdData.format, stsdData.requiredSampleTransformation, stsdData.trackEncryptionBoxes,
stsdData.nalUnitLengthFieldLength, edtsData.first, edtsData.second); stsdData.nalUnitLengthFieldLength, editListDurations, editListMediaTimes);
} }
/** /**
@ -783,7 +791,7 @@ import java.util.List;
* *
* @param edtsAtom edts (edit box) atom to decode. * @param edtsAtom edts (edit box) atom to decode.
* @return Pair of edit list durations and edit list media times, or a pair of nulls if they are * @return Pair of edit list durations and edit list media times, or a pair of nulls if they are
* not present. * not present.
*/ */
private static Pair<long[], long[]> parseEdts(Atom.ContainerAtom edtsAtom) { private static Pair<long[], long[]> parseEdts(Atom.ContainerAtom edtsAtom) {
Atom.LeafAtom elst; Atom.LeafAtom elst;

View File

@ -74,7 +74,7 @@ public final class FragmentedMp4Extractor implements Extractor {
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, value = {FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME, @IntDef(flag = true, value = {FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME,
FLAG_WORKAROUND_IGNORE_TFDT_BOX, FLAG_ENABLE_EMSG_TRACK, FLAG_ENABLE_CEA608_TRACK, FLAG_WORKAROUND_IGNORE_TFDT_BOX, FLAG_ENABLE_EMSG_TRACK, FLAG_ENABLE_CEA608_TRACK,
FLAG_SIDELOADED}) FLAG_SIDELOADED, FLAG_WORKAROUND_IGNORE_EDIT_LISTS})
public @interface Flags {} public @interface Flags {}
/** /**
* Flag to work around an issue in some video streams where every frame is marked as a sync frame. * Flag to work around an issue in some video streams where every frame is marked as a sync frame.
@ -103,6 +103,10 @@ public final class FragmentedMp4Extractor implements Extractor {
* container. * container.
*/ */
private static final int FLAG_SIDELOADED = 16; private static final int FLAG_SIDELOADED = 16;
/**
* Flag to ignore any edit lists in the stream.
*/
public static final int FLAG_WORKAROUND_IGNORE_EDIT_LISTS = 32;
private static final String TAG = "FragmentedMp4Extractor"; private static final String TAG = "FragmentedMp4Extractor";
private static final int SAMPLE_GROUP_TYPE_seig = Util.getIntegerCodeForString("seig"); private static final int SAMPLE_GROUP_TYPE_seig = Util.getIntegerCodeForString("seig");
@ -432,7 +436,7 @@ public final class FragmentedMp4Extractor implements Extractor {
Atom.ContainerAtom atom = moov.containerChildren.get(i); Atom.ContainerAtom atom = moov.containerChildren.get(i);
if (atom.type == Atom.TYPE_trak) { if (atom.type == Atom.TYPE_trak) {
Track track = AtomParsers.parseTrak(atom, moov.getLeafAtomOfType(Atom.TYPE_mvhd), duration, Track track = AtomParsers.parseTrak(atom, moov.getLeafAtomOfType(Atom.TYPE_mvhd), duration,
drmInitData, false); drmInitData, (flags & FLAG_WORKAROUND_IGNORE_EDIT_LISTS) != 0, false);
if (track != null) { if (track != null) {
tracks.put(track.id, track); tracks.put(track.id, track);
} }

View File

@ -28,6 +28,7 @@ import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom; import com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom;
import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor.Flags;
import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.NalUnitUtil; import com.google.android.exoplayer2.util.NalUnitUtil;
@ -57,6 +58,17 @@ public final class Mp4Extractor implements Extractor, SeekMap {
}; };
/**
* Flags controlling the behavior of the extractor.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, value = {FLAG_WORKAROUND_IGNORE_EDIT_LISTS})
public @interface Flags {}
/**
* Flag to ignore any edit lists in the stream.
*/
public static final int FLAG_WORKAROUND_IGNORE_EDIT_LISTS = 1;
/** /**
* Parser states. * Parser states.
*/ */
@ -76,6 +88,8 @@ public final class Mp4Extractor implements Extractor, SeekMap {
*/ */
private static final long RELOAD_MINIMUM_SEEK_DISTANCE = 256 * 1024; private static final long RELOAD_MINIMUM_SEEK_DISTANCE = 256 * 1024;
private final @Flags int flags;
// Temporary arrays. // Temporary arrays.
private final ParsableByteArray nalStartCode; private final ParsableByteArray nalStartCode;
private final ParsableByteArray nalLength; private final ParsableByteArray nalLength;
@ -98,7 +112,21 @@ public final class Mp4Extractor implements Extractor, SeekMap {
private long durationUs; private long durationUs;
private boolean isQuickTime; private boolean isQuickTime;
/**
* Creates a new extractor for unfragmented MP4 streams.
*/
public Mp4Extractor() { public Mp4Extractor() {
this(0);
}
/**
* Creates a new extractor for unfragmented MP4 streams, using the specified flags to control the
* extractor's behavior.
*
* @param flags Flags that control the extractor's behavior.
*/
public Mp4Extractor(@Flags int flags) {
this.flags = flags;
atomHeader = new ParsableByteArray(Atom.LONG_HEADER_SIZE); atomHeader = new ParsableByteArray(Atom.LONG_HEADER_SIZE);
containerAtoms = new Stack<>(); containerAtoms = new Stack<>();
nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE); nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE);
@ -345,7 +373,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
} }
Track track = AtomParsers.parseTrak(atom, moov.getLeafAtomOfType(Atom.TYPE_mvhd), Track track = AtomParsers.parseTrak(atom, moov.getLeafAtomOfType(Atom.TYPE_mvhd),
C.TIME_UNSET, null, isQuickTime); C.TIME_UNSET, null, (flags & FLAG_WORKAROUND_IGNORE_EDIT_LISTS) != 0, isQuickTime);
if (track == null) { if (track == null) {
continue; continue;
} }

View File

@ -81,12 +81,12 @@ public final class Track {
/** /**
* Durations of edit list segments in the movie timescale. Null if there is no edit list. * Durations of edit list segments in the movie timescale. Null if there is no edit list.
*/ */
public final long[] editListDurations; @Nullable public final long[] editListDurations;
/** /**
* Media times for edit list segments in the track timescale. Null if there is no edit list. * Media times for edit list segments in the track timescale. Null if there is no edit list.
*/ */
public final long[] editListMediaTimes; @Nullable public final long[] editListMediaTimes;
/** /**
* For H264 video tracks, the length in bytes of the NALUnitLength field in each sample. 0 for * For H264 video tracks, the length in bytes of the NALUnitLength field in each sample. 0 for
@ -99,7 +99,7 @@ public final class Track {
public Track(int id, int type, long timescale, long movieTimescale, long durationUs, public Track(int id, int type, long timescale, long movieTimescale, long durationUs,
Format format, @Transformation int sampleTransformation, Format format, @Transformation int sampleTransformation,
@Nullable TrackEncryptionBox[] sampleDescriptionEncryptionBoxes, int nalUnitLengthFieldLength, @Nullable TrackEncryptionBox[] sampleDescriptionEncryptionBoxes, int nalUnitLengthFieldLength,
long[] editListDurations, long[] editListMediaTimes) { @Nullable long[] editListDurations, @Nullable long[] editListMediaTimes) {
this.id = id; this.id = id;
this.type = type; this.type = type;
this.timescale = timescale; this.timescale = timescale;