Commit bfa1de68 by Oliver Woodman

Move common MP4 parsing code to CommonMp4AtomParsers and Mp4Util.

Also add parseMp4vFromParent and return the track's duration in parseTrak.

This is in preparation for adding a non-fragmented MP4 extractor.
parent a968e553
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer.chunk.parser; package com.google.android.exoplayer.chunk.parser;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.SampleHolder;
...@@ -79,6 +80,11 @@ public interface Extractor { ...@@ -79,6 +80,11 @@ public interface Extractor {
public MediaFormat getFormat(); public MediaFormat getFormat();
/** /**
* Returns the duration of the stream in microseconds, or {@link C#UNKNOWN_TIME_US} if unknown.
*/
public long getDurationUs();
/**
* Returns the pssh information parsed from the stream. * Returns the pssh information parsed from the stream.
* *
* @return The pssh information. May be null if pssh data has yet to be parsed, or if the stream * @return The pssh information. May be null if pssh data has yet to be parsed, or if the stream
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer.chunk.parser.webm; package com.google.android.exoplayer.chunk.parser.webm;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.SampleHolder;
...@@ -185,6 +186,11 @@ public final class WebmExtractor implements Extractor { ...@@ -185,6 +186,11 @@ public final class WebmExtractor implements Extractor {
} }
@Override @Override
public long getDurationUs() {
return durationUs == UNKNOWN ? C.UNKNOWN_TIME_US : durationUs;
}
@Override
public Map<UUID, byte[]> getPsshInfo() { public Map<UUID, byte[]> getPsshInfo() {
// TODO: Parse pssh data from Webm streams. // TODO: Parse pssh data from Webm streams.
return null; return null;
......
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer.mp4;
import com.google.android.exoplayer.util.CodecSpecificDataUtil;
import com.google.android.exoplayer.util.ParsableByteArray;
import java.nio.ByteBuffer;
/**
* Utility methods and constants for parsing fragmented and unfragmented MP4 files.
*/
public final class Mp4Util {
/** Size of an atom header, in bytes. */
public static final int ATOM_HEADER_SIZE = 8;
/** Size of a long atom header, in bytes. */
public static final int LONG_ATOM_HEADER_SIZE = 16;
/** Size of a full atom header, in bytes. */
public static final int FULL_ATOM_HEADER_SIZE = 12;
/** Four initial bytes that must prefix H.264/AVC NAL units for decoding. */
private static final byte[] NAL_START_CODE = new byte[] {0, 0, 0, 1};
/** Parses the version number out of the additional integer component of a full atom. */
public static int parseFullAtomVersion(int fullAtomInt) {
return 0x000000FF & (fullAtomInt >> 24);
}
/** Parses the atom flags out of the additional integer component of a full atom. */
public static int parseFullAtomFlags(int fullAtomInt) {
return 0x00FFFFFF & fullAtomInt;
}
/**
* Reads an unsigned integer into an integer. This method is suitable for use when it can be
* assumed that the top bit will always be set to zero.
*
* @throws IllegalArgumentException If the top bit of the input data is set.
*/
public static int readUnsignedIntToInt(ByteBuffer data) {
int result = 0xFF & data.get();
for (int i = 1; i < 4; i++) {
result <<= 8;
result |= 0xFF & data.get();
}
if (result < 0) {
throw new IllegalArgumentException("Top bit not zero: " + result);
}
return result;
}
/**
* Replaces length prefixes of NAL units in {@code buffer} with start code prefixes, within the
* {@code size} bytes preceding the buffer's position.
*/
public static void replaceLengthPrefixesWithAvcStartCodes(ByteBuffer buffer, int size) {
int sampleOffset = buffer.position() - size;
int position = sampleOffset;
while (position < sampleOffset + size) {
buffer.position(position);
int length = readUnsignedIntToInt(buffer);
buffer.position(position);
buffer.put(NAL_START_CODE);
position += length + 4;
}
buffer.position(sampleOffset + size);
}
/** Constructs and returns a NAL unit with a start code followed by the data in {@code atom}. */
public static byte[] parseChildNalUnit(ParsableByteArray atom) {
int length = atom.readUnsignedShort();
int offset = atom.getPosition();
atom.skip(length);
return CodecSpecificDataUtil.buildNalUnit(atom.data, offset, length);
}
}
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer.mp4; package com.google.android.exoplayer.mp4;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.chunk.parser.mp4.TrackEncryptionBox; import com.google.android.exoplayer.chunk.parser.mp4.TrackEncryptionBox;
...@@ -43,6 +44,10 @@ public final class Track { ...@@ -43,6 +44,10 @@ public final class Track {
* Type of a meta track. * Type of a meta track.
*/ */
public static final int TYPE_META = 0x6D657461; public static final int TYPE_META = 0x6D657461;
/**
* Type of a time-code track.
*/
public static final int TYPE_TIME_CODE = 0x746D6364;
/** /**
* The track identifier. * The track identifier.
...@@ -50,7 +55,8 @@ public final class Track { ...@@ -50,7 +55,8 @@ public final class Track {
public final int id; public final int id;
/** /**
* One of {@link #TYPE_VIDEO}, {@link #TYPE_AUDIO}, {@link #TYPE_HINT} and {@link #TYPE_META}. * One of {@link #TYPE_VIDEO}, {@link #TYPE_AUDIO}, {@link #TYPE_HINT}, {@link #TYPE_META} and
* {@link #TYPE_TIME_CODE}.
*/ */
public final int type; public final int type;
...@@ -60,6 +66,11 @@ public final class Track { ...@@ -60,6 +66,11 @@ public final class Track {
public final long timescale; public final long timescale;
/** /**
* The duration of the track in microseconds, or {@link C#UNKNOWN_TIME_US} if unknown.
*/
public final long durationUs;
/**
* The format if {@link #type} is {@link #TYPE_VIDEO} or {@link #TYPE_AUDIO}. Null otherwise. * The format if {@link #type} is {@link #TYPE_VIDEO} or {@link #TYPE_AUDIO}. Null otherwise.
*/ */
public final MediaFormat mediaFormat; public final MediaFormat mediaFormat;
...@@ -69,11 +80,12 @@ public final class Track { ...@@ -69,11 +80,12 @@ public final class Track {
*/ */
public final TrackEncryptionBox[] sampleDescriptionEncryptionBoxes; public final TrackEncryptionBox[] sampleDescriptionEncryptionBoxes;
public Track(int id, int type, long timescale, MediaFormat mediaFormat, public Track(int id, int type, long timescale, long durationUs, MediaFormat mediaFormat,
TrackEncryptionBox[] sampleDescriptionEncryptionBoxes) { TrackEncryptionBox[] sampleDescriptionEncryptionBoxes) {
this.id = id; this.id = id;
this.type = type; this.type = type;
this.timescale = timescale; this.timescale = timescale;
this.durationUs = durationUs;
this.mediaFormat = mediaFormat; this.mediaFormat = mediaFormat;
this.sampleDescriptionEncryptionBoxes = sampleDescriptionEncryptionBoxes; this.sampleDescriptionEncryptionBoxes = sampleDescriptionEncryptionBoxes;
} }
......
...@@ -167,8 +167,8 @@ public class SmoothStreamingChunkSource implements ChunkSource { ...@@ -167,8 +167,8 @@ public class SmoothStreamingChunkSource implements ChunkSource {
: Track.TYPE_AUDIO; : Track.TYPE_AUDIO;
FragmentedMp4Extractor extractor = new FragmentedMp4Extractor( FragmentedMp4Extractor extractor = new FragmentedMp4Extractor(
FragmentedMp4Extractor.WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME); FragmentedMp4Extractor.WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME);
extractor.setTrack(new Track(trackIndex, trackType, streamElement.timescale, mediaFormat, extractor.setTrack(new Track(trackIndex, trackType, streamElement.timescale,
trackEncryptionBoxes)); initialManifest.durationUs, mediaFormat, trackEncryptionBoxes));
extractors.put(trackIndex, extractor); extractors.put(trackIndex, extractor);
} }
this.maxHeight = maxHeight; this.maxHeight = maxHeight;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment