Purely stylistic changes to FLV extractor

This commit is contained in:
Oliver Woodman 2015-10-26 10:44:19 +00:00
parent fb75b65a70
commit 950cc70003
5 changed files with 75 additions and 74 deletions

View File

@ -30,8 +30,9 @@ import java.util.Collections;
/** /**
* Parses audio tags of from an FLV stream and extracts AAC frames. * Parses audio tags of from an FLV stream and extracts AAC frames.
*/ */
final class AudioTagPayloadReader extends TagPayloadReader { /* package */ final class AudioTagPayloadReader extends TagPayloadReader {
// Sound format
// Audio format
private static final int AUDIO_FORMAT_AAC = 10; private static final int AUDIO_FORMAT_AAC = 10;
// AAC PACKET TYPE // AAC PACKET TYPE
@ -47,48 +48,40 @@ final class AudioTagPayloadReader extends TagPayloadReader {
private boolean hasParsedAudioDataHeader; private boolean hasParsedAudioDataHeader;
private boolean hasOutputFormat; private boolean hasOutputFormat;
public AudioTagPayloadReader(TrackOutput output) { public AudioTagPayloadReader(TrackOutput output) {
super(output); super(output);
} }
@Override @Override
public void seek() { public void seek() {
// Do nothing.
} }
@Override @Override
protected boolean parseHeader(ParsableByteArray data) throws UnsupportedTrack { protected boolean parseHeader(ParsableByteArray data) throws UnsupportedFormatException {
// Parse audio data header, if it was not done, to extract information // Parse audio data header, if it was not done, to extract information about the audio codec
// about the audio codec and audio configuration. // and audio configuration.
if (!hasParsedAudioDataHeader) { if (!hasParsedAudioDataHeader) {
int header = data.readUnsignedByte(); int header = data.readUnsignedByte();
int soundFormat = (header >> 4) & 0x0F; int audioFormat = (header >> 4) & 0x0F;
int sampleRateIndex = (header >> 2) & 0x03; int sampleRateIndex = (header >> 2) & 0x03;
int bitsPerSample = (header & 0x02) == 0x02 ? 16 : 8;
int channels = (header & 0x01) + 1;
if (sampleRateIndex < 0 || sampleRateIndex >= AUDIO_SAMPLING_RATE_TABLE.length) { if (sampleRateIndex < 0 || sampleRateIndex >= AUDIO_SAMPLING_RATE_TABLE.length) {
throw new UnsupportedTrack("Invalid sample rate for the audio track"); throw new UnsupportedFormatException("Invalid sample rate for the audio track");
} }
if (audioFormat != AUDIO_FORMAT_AAC) {
if (!hasOutputFormat) {
// TODO: Adds support for MP3 and PCM // TODO: Adds support for MP3 and PCM
if (soundFormat != AUDIO_FORMAT_AAC) { if (audioFormat != AUDIO_FORMAT_AAC) {
throw new UnsupportedTrack("Audio track not supported. Format: " + soundFormat + throw new UnsupportedFormatException("Audio format not supported: " + audioFormat);
", Sample rate: " + sampleRateIndex + ", bps: " + bitsPerSample + ", channels: " +
channels);
} }
} }
hasParsedAudioDataHeader = true; hasParsedAudioDataHeader = true;
} else { } else {
// Skip header if it was parsed previously. // Skip header if it was parsed previously.
data.skipBytes(1); data.skipBytes(1);
} }
// In all the cases we will be managing AAC format (otherwise an exception would be // In all the cases we will be managing AAC format (otherwise an exception would be fired so we
// fired so we can just always return true // can just always return true.
return true; return true;
} }
@ -110,10 +103,9 @@ final class AudioTagPayloadReader extends TagPayloadReader {
audioSpecificConfig); audioSpecificConfig);
MediaFormat mediaFormat = MediaFormat.createAudioFormat(MediaFormat.NO_VALUE, MediaFormat mediaFormat = MediaFormat.createAudioFormat(MediaFormat.NO_VALUE,
MimeTypes.AUDIO_AAC, MediaFormat.NO_VALUE, MediaFormat.NO_VALUE, durationUs, MimeTypes.AUDIO_AAC, MediaFormat.NO_VALUE, MediaFormat.NO_VALUE, getDurationUs(),
audioParams.second, audioParams.first, Collections.singletonList(audioSpecificConfig), audioParams.second, audioParams.first, Collections.singletonList(audioSpecificConfig),
null); null);
output.format(mediaFormat); output.format(mediaFormat);
hasOutputFormat = true; hasOutputFormat = true;
} else if (packetType == AAC_PACKET_TYPE_AAC_RAW) { } else if (packetType == AAC_PACKET_TYPE_AAC_RAW) {

View File

@ -129,7 +129,7 @@ public final class FlvExtractor implements Extractor, SeekMap {
return readSample(input); return readSample(input);
} }
} }
} catch (AudioTagPayloadReader.UnsupportedTrack unsupportedTrack) { } catch (AudioTagPayloadReader.UnsupportedFormatException unsupportedTrack) {
unsupportedTrack.printStackTrace(); unsupportedTrack.printStackTrace();
return RESULT_END_OF_INPUT; return RESULT_END_OF_INPUT;
} }
@ -186,11 +186,11 @@ public final class FlvExtractor implements Extractor, SeekMap {
* @return True if tag header was read successfully. Otherwise, false. * @return True if tag header was read successfully. Otherwise, false.
* @throws IOException If an error occurred reading from the source. * @throws IOException If an error occurred reading from the source.
* @throws InterruptedException If the thread was interrupted. * @throws InterruptedException If the thread was interrupted.
* @throws TagPayloadReader.UnsupportedTrack If payload of the tag is using a codec non * @throws TagPayloadReader.UnsupportedFormatException If payload of the tag is using a codec non
* supported codec. * supported codec.
*/ */
private boolean readTagHeader(ExtractorInput input) throws IOException, InterruptedException, private boolean readTagHeader(ExtractorInput input) throws IOException, InterruptedException,
TagPayloadReader.UnsupportedTrack { TagPayloadReader.UnsupportedFormatException {
try { try {
// skipping previous tag size field // skipping previous tag size field
input.skipFully(4); input.skipFully(4);
@ -235,11 +235,11 @@ public final class FlvExtractor implements Extractor, SeekMap {
* @return One of {@link Extractor#RESULT_CONTINUE} and {@link Extractor#RESULT_END_OF_INPUT}. * @return One of {@link Extractor#RESULT_CONTINUE} and {@link Extractor#RESULT_END_OF_INPUT}.
* @throws IOException If an error occurred reading from the source. * @throws IOException If an error occurred reading from the source.
* @throws InterruptedException If the thread was interrupted. * @throws InterruptedException If the thread was interrupted.
* @throws TagPayloadReader.UnsupportedTrack If payload of the tag is using a codec non * @throws TagPayloadReader.UnsupportedFormatException If payload of the tag is using a codec non
* supported codec. * supported codec.
*/ */
private int readSample(ExtractorInput input) throws IOException, private int readSample(ExtractorInput input) throws IOException,
InterruptedException, AudioTagPayloadReader.UnsupportedTrack { InterruptedException, AudioTagPayloadReader.UnsupportedFormatException {
if (tagData != null) { if (tagData != null) {
if (!input.readFully(tagData.data, 0, currentTagHeader.dataSize, true)) { if (!input.readFully(tagData.data, 0, currentTagHeader.dataSize, true)) {
return RESULT_END_OF_INPUT; return RESULT_END_OF_INPUT;

View File

@ -28,7 +28,7 @@ import java.util.Map;
/** /**
* Parses Script Data tags from an FLV stream and extracts metadata information. * Parses Script Data tags from an FLV stream and extracts metadata information.
*/ */
final class ScriptTagPayloadReader extends TagPayloadReader { /* package */ final class ScriptTagPayloadReader extends TagPayloadReader {
// AMF object types // AMF object types
private static final int AMF_TYPE_UNKNOWN = -1; private static final int AMF_TYPE_UNKNOWN = -1;
@ -50,19 +50,20 @@ final class ScriptTagPayloadReader extends TagPayloadReader {
@Override @Override
public void seek() { public void seek() {
// Do nothing.
} }
@Override @Override
protected boolean parseHeader(ParsableByteArray data) throws UnsupportedTrack { protected boolean parseHeader(ParsableByteArray data) throws UnsupportedFormatException {
return true; return true;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
protected void parsePayload(ParsableByteArray data, long timeUs) { protected void parsePayload(ParsableByteArray data, long timeUs) {
// Read message name (don't storing it as we are not going to give it any use) // Read message name (don't store it because we don't yet have a use for it).
readAMFData(data, AMF_TYPE_UNKNOWN); readAMFData(data, AMF_TYPE_UNKNOWN);
// Read message data.
Object obj = readAMFData(data, AMF_TYPE_UNKNOWN); Object obj = readAMFData(data, AMF_TYPE_UNKNOWN);
if (obj instanceof Map) { if (obj instanceof Map) {
@ -74,7 +75,7 @@ final class ScriptTagPayloadReader extends TagPayloadReader {
switch (entry.getKey()) { switch (entry.getKey()) {
case "duration": case "duration":
this.durationUs = (long)(C.MICROS_PER_SECOND * (Double)(entry.getValue())); setDurationUs((long) (C.MICROS_PER_SECOND * (Double)(entry.getValue())));
break; break;
default: default:
@ -198,4 +199,5 @@ final class ScriptTagPayloadReader extends TagPayloadReader {
data.readUnsignedShort(); data.readUnsignedShort();
return date; return date;
} }
} }

View File

@ -24,10 +24,20 @@ import com.google.android.exoplayer.util.ParsableByteArray;
*/ */
/* package */ abstract class TagPayloadReader { /* package */ abstract class TagPayloadReader {
/**
* Thrown when the format is not supported.
*/
public static final class UnsupportedFormatException extends Exception {
public UnsupportedFormatException(String msg) {
super(msg);
}
}
protected final TrackOutput output; protected final TrackOutput output;
// Duration of the track private long durationUs;
protected long durationUs;
/** /**
* @param output A {@link TrackOutput} to which samples should be written. * @param output A {@link TrackOutput} to which samples should be written.
@ -37,6 +47,24 @@ import com.google.android.exoplayer.util.ParsableByteArray;
this.durationUs = C.UNKNOWN_TIME_US; this.durationUs = C.UNKNOWN_TIME_US;
} }
/**
* Sets duration in microseconds.
*
* @param durationUs duration in microseconds.
*/
public final void setDurationUs(long durationUs) {
this.durationUs = durationUs;
}
/**
* Gets the duration in microseconds.
*
* @return The duration in microseconds.
*/
public final long getDurationUs() {
return durationUs;
}
/** /**
* Notifies the reader that a seek has occurred. * Notifies the reader that a seek has occurred.
* <p> * <p>
@ -46,53 +74,34 @@ import com.google.android.exoplayer.util.ParsableByteArray;
*/ */
public abstract void seek(); public abstract void seek();
/**
* Parses tag header
* @param data Buffer where the tag header is stored
* @return True if header was parsed successfully and then payload should be read;
* Otherwise, false
* @throws UnsupportedTrack
*/
protected abstract boolean parseHeader(ParsableByteArray data) throws UnsupportedTrack;
/**
* Parses tag payload
* @param data Buffer where tag payload is stored
* @param timeUs Time position of the frame
*/
protected abstract void parsePayload(ParsableByteArray data, long timeUs);
/** /**
* Consumes payload data. * Consumes payload data.
* *
* @param data The payload data to consume. * @param data The payload data to consume.
* @param timeUs The timestamp associated with the payload. * @param timeUs The timestamp associated with the payload.
*/ */
public void consume(ParsableByteArray data, long timeUs) throws UnsupportedTrack { public final void consume(ParsableByteArray data, long timeUs) throws UnsupportedFormatException {
if (parseHeader(data)) { if (parseHeader(data)) {
parsePayload(data, timeUs); parsePayload(data, timeUs);
} }
} }
/** /**
* Sets duration in microseconds * Parses tag header.
* @param durationUs duration in microseconds *
* @param data Buffer where the tag header is stored.
* @return True if the header was parsed successfully and the payload should be read. False
* otherwise.
* @throws UnsupportedFormatException
*/ */
public void setDurationUs(long durationUs) { protected abstract boolean parseHeader(ParsableByteArray data) throws UnsupportedFormatException;
this.durationUs = durationUs;
}
public long getDurationUs() {
return durationUs;
}
/** /**
* Thrown when format described in the AudioTrack is not supported * Parses tag payload.
*
* @param data Buffer where tag payload is stored
* @param timeUs Time position of the frame
*/ */
public static final class UnsupportedTrack extends Exception { protected abstract void parsePayload(ParsableByteArray data, long timeUs);
public UnsupportedTrack(String msg) {
super(msg);
}
}
} }

View File

@ -34,7 +34,7 @@ import java.util.List;
/** /**
* Parses video tags from an FLV stream and extracts H.264 nal units. * Parses video tags from an FLV stream and extracts H.264 nal units.
*/ */
final class VideoTagPayloadReader extends TagPayloadReader { /* package */ final class VideoTagPayloadReader extends TagPayloadReader {
private static final String TAG = "VideoTagPayloadReader"; private static final String TAG = "VideoTagPayloadReader";
// Video codec // Video codec
@ -63,25 +63,23 @@ final class VideoTagPayloadReader extends TagPayloadReader {
*/ */
public VideoTagPayloadReader(TrackOutput output) { public VideoTagPayloadReader(TrackOutput output) {
super(output); super(output);
nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE); nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE);
nalLength = new ParsableByteArray(4); nalLength = new ParsableByteArray(4);
} }
@Override @Override
public void seek() { public void seek() {
// Do nothing.
} }
@Override @Override
protected boolean parseHeader(ParsableByteArray data) throws UnsupportedTrack { protected boolean parseHeader(ParsableByteArray data) throws UnsupportedFormatException {
int header = data.readUnsignedByte(); int header = data.readUnsignedByte();
int frameType = (header >> 4) & 0x0F; int frameType = (header >> 4) & 0x0F;
int videoCodec = (header & 0x0F); int videoCodec = (header & 0x0F);
// Support just H.264 encoded content. // Support just H.264 encoded content.
if (videoCodec != VIDEO_CODEC_AVC) { if (videoCodec != VIDEO_CODEC_AVC) {
throw new UnsupportedTrack("Video codec not supported. Codec: " + videoCodec); throw new UnsupportedFormatException("Video format not supported: " + videoCodec);
} }
this.frameType = frameType; this.frameType = frameType;
return (frameType != VIDEO_FRAME_VIDEO_INFO); return (frameType != VIDEO_FRAME_VIDEO_INFO);
@ -113,7 +111,7 @@ final class VideoTagPayloadReader extends TagPayloadReader {
// Construct and output the format. // Construct and output the format.
MediaFormat mediaFormat = MediaFormat.createVideoFormat(MediaFormat.NO_VALUE, MediaFormat mediaFormat = MediaFormat.createVideoFormat(MediaFormat.NO_VALUE,
MimeTypes.VIDEO_H264, MediaFormat.NO_VALUE, MediaFormat.NO_VALUE, durationUs, MimeTypes.VIDEO_H264, MediaFormat.NO_VALUE, MediaFormat.NO_VALUE, getDurationUs(),
avcData.width, avcData.height, avcData.initializationData, MediaFormat.NO_VALUE, avcData.width, avcData.height, avcData.initializationData, MediaFormat.NO_VALUE,
avcData.pixelWidthAspectRatio); avcData.pixelWidthAspectRatio);
output.format(mediaFormat); output.format(mediaFormat);