Purely stylistic changes to FLV extractor
This commit is contained in:
parent
fb75b65a70
commit
950cc70003
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user