mirror of
https://github.com/samsonjs/media.git
synced 2026-03-26 09:35:47 +00:00
Implement ID3 Metadata support for audio only HLS.
Issue: #862 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=111403855
This commit is contained in:
parent
1e4f2f6a1f
commit
69a42b60f8
7 changed files with 383 additions and 51 deletions
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.extractor.ts;
|
||||
|
||||
import com.google.android.exoplayer.C;
|
||||
import com.google.android.exoplayer.testutil.FakeTrackOutput;
|
||||
import com.google.android.exoplayer.testutil.TestUtil;
|
||||
import com.google.android.exoplayer.util.ParsableByteArray;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Test for {@link AdtsReader}.
|
||||
*/
|
||||
public class AdtsReaderTest extends TestCase {
|
||||
|
||||
public static final byte[] ID3_DATA_1 = TestUtil.createByteArray(
|
||||
0x49, 0x44, 0x33, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x54, 0x58,
|
||||
0x58, 0x58, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x03, 0x00, 0x20, 0x2a,
|
||||
0x2a, 0x2a, 0x20, 0x54, 0x48, 0x49, 0x53, 0x20, 0x49, 0x53, 0x20, 0x54,
|
||||
0x69, 0x6d, 0x65, 0x64, 0x20, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74,
|
||||
0x61, 0x20, 0x40, 0x20, 0x2d, 0x2d, 0x20, 0x30, 0x30, 0x3a, 0x30, 0x30,
|
||||
0x3a, 0x30, 0x30, 0x2e, 0x30, 0x20, 0x2a, 0x2a, 0x2a, 0x20, 0x00);
|
||||
|
||||
public static final byte[] ID3_DATA_2 = TestUtil.createByteArray(
|
||||
0x49,
|
||||
0x44, 0x33, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x50, 0x52, 0x49,
|
||||
0x56, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x63, 0x6f, 0x6d, 0x2e, 0x61,
|
||||
0x70, 0x70, 0x6c, 0x65, 0x2e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69,
|
||||
0x6e, 0x67, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
|
||||
0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
||||
0x61, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xbb, 0xa0);
|
||||
|
||||
public static final byte[] ADTS_HEADER = TestUtil.createByteArray(
|
||||
0xff, 0xf1, 0x50, 0x80, 0x01, 0xdf, 0xfc);
|
||||
|
||||
public static final byte[] ADTS_CONTENT = TestUtil.createByteArray(
|
||||
0x20, 0x00, 0x20, 0x00, 0x00, 0x80, 0x0e);
|
||||
|
||||
private static final byte TEST_DATA[] = TestUtil.joinByteArrays(
|
||||
ID3_DATA_1,
|
||||
ID3_DATA_2,
|
||||
ADTS_HEADER,
|
||||
ADTS_CONTENT);
|
||||
|
||||
private static final long ADTS_SAMPLE_DURATION = 23219L;
|
||||
|
||||
private ParsableByteArray data;
|
||||
|
||||
private AdtsReader adtsReader;
|
||||
private FakeTrackOutput adtsOutput;
|
||||
private FakeTrackOutput id3Output;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
adtsOutput = new FakeTrackOutput();
|
||||
id3Output = new FakeTrackOutput();
|
||||
adtsReader = new AdtsReader(adtsOutput, id3Output);
|
||||
data = new ParsableByteArray(TEST_DATA);
|
||||
}
|
||||
|
||||
public void testSkipToNextSample() throws Exception {
|
||||
for (int i = 1; i <= ID3_DATA_1.length + ID3_DATA_2.length; i++) {
|
||||
data.setPosition(i);
|
||||
feed();
|
||||
// Once the data position set to ID3_DATA_1.length, no more id3 samples are read
|
||||
int id3SampleCount = Math.min(i, ID3_DATA_1.length);
|
||||
assertSampleCounts(id3SampleCount, i);
|
||||
}
|
||||
}
|
||||
|
||||
public void testSkipToNextSampleResetsState() throws Exception {
|
||||
data = new ParsableByteArray(TestUtil.joinByteArrays(
|
||||
ADTS_HEADER,
|
||||
ADTS_CONTENT,
|
||||
// Adts sample missing the first sync byte
|
||||
Arrays.copyOfRange(ADTS_HEADER, 1, ADTS_HEADER.length),
|
||||
ADTS_CONTENT));
|
||||
feed();
|
||||
assertSampleCounts(0, 1);
|
||||
adtsOutput.assertSample(0, ADTS_CONTENT, 0, C.SAMPLE_FLAG_SYNC, null);
|
||||
}
|
||||
|
||||
public void testNoData() throws Exception {
|
||||
feedLimited(0);
|
||||
assertSampleCounts(0, 0);
|
||||
}
|
||||
|
||||
public void testNotEnoughDataForIdentifier() throws Exception {
|
||||
feedLimited(3 - 1);
|
||||
assertSampleCounts(0, 0);
|
||||
}
|
||||
|
||||
public void testNotEnoughDataForHeader() throws Exception {
|
||||
feedLimited(10 - 1);
|
||||
assertSampleCounts(0, 0);
|
||||
}
|
||||
|
||||
public void testNotEnoughDataForWholeId3Packet() throws Exception {
|
||||
feedLimited(ID3_DATA_1.length - 1);
|
||||
assertSampleCounts(0, 0);
|
||||
}
|
||||
|
||||
public void testConsumeWholeId3Packet() throws Exception {
|
||||
feedLimited(ID3_DATA_1.length);
|
||||
assertSampleCounts(1, 0);
|
||||
id3Output.assertSample(0, ID3_DATA_1, 0, C.SAMPLE_FLAG_SYNC, null);
|
||||
}
|
||||
|
||||
public void testMultiId3Packet() throws Exception {
|
||||
feedLimited(ID3_DATA_1.length + ID3_DATA_2.length - 1);
|
||||
assertSampleCounts(1, 0);
|
||||
id3Output.assertSample(0, ID3_DATA_1, 0, C.SAMPLE_FLAG_SYNC, null);
|
||||
}
|
||||
|
||||
public void testMultiId3PacketConsumed() throws Exception {
|
||||
feedLimited(ID3_DATA_1.length + ID3_DATA_2.length);
|
||||
assertSampleCounts(2, 0);
|
||||
id3Output.assertSample(0, ID3_DATA_1, 0, C.SAMPLE_FLAG_SYNC, null);
|
||||
id3Output.assertSample(1, ID3_DATA_2, 0, C.SAMPLE_FLAG_SYNC, null);
|
||||
}
|
||||
|
||||
public void testMultiPacketConsumed() throws Exception {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
data.setPosition(0);
|
||||
adtsReader.consume(data, 0, i == 0);
|
||||
|
||||
long timeUs = ADTS_SAMPLE_DURATION * i;
|
||||
int j = i * 2;
|
||||
assertSampleCounts(j + 2, i + 1);
|
||||
|
||||
id3Output.assertSample(j, ID3_DATA_1, timeUs, C.SAMPLE_FLAG_SYNC, null);
|
||||
id3Output.assertSample(j + 1, ID3_DATA_2, timeUs, C.SAMPLE_FLAG_SYNC, null);
|
||||
adtsOutput.assertSample(i, ADTS_CONTENT, timeUs, C.SAMPLE_FLAG_SYNC, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void testAdtsDataOnly() throws Exception {
|
||||
data.setPosition(ID3_DATA_1.length + ID3_DATA_2.length);
|
||||
feed();
|
||||
assertSampleCounts(0, 1);
|
||||
adtsOutput.assertSample(0, ADTS_CONTENT, 0, C.SAMPLE_FLAG_SYNC, null);
|
||||
}
|
||||
|
||||
private void feedLimited(int limit) {
|
||||
data.setLimit(limit);
|
||||
feed();
|
||||
}
|
||||
|
||||
private void feed() {
|
||||
adtsReader.consume(data, 0, true);
|
||||
}
|
||||
|
||||
private void assertSampleCounts(int id3SampleCount, int adtsSampleCount) {
|
||||
id3Output.assertSampleCount(id3SampleCount);
|
||||
adtsOutput.assertSampleCount(adtsSampleCount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
package com.google.android.exoplayer;
|
||||
|
||||
import com.google.android.exoplayer.util.Assertions;
|
||||
import com.google.android.exoplayer.util.MimeTypes;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
|
@ -180,6 +181,11 @@ public final class MediaFormat {
|
|||
NO_VALUE);
|
||||
}
|
||||
|
||||
public static MediaFormat createId3Format() {
|
||||
return createFormatForMimeType(null, MimeTypes.APPLICATION_ID3, MediaFormat.NO_VALUE,
|
||||
C.UNKNOWN_TIME_US);
|
||||
}
|
||||
|
||||
/* package */ MediaFormat(String trackId, String mimeType, int bitrate, int maxInputSize,
|
||||
long durationUs, int width, int height, int rotationDegrees, float pixelWidthHeightRatio,
|
||||
int channelCount, int sampleRate, String language, long subsampleOffsetUs,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.extractor;
|
||||
|
||||
import com.google.android.exoplayer.MediaFormat;
|
||||
import com.google.android.exoplayer.util.ParsableByteArray;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A dummy {@link TrackOutput} implementation.
|
||||
*/
|
||||
public class DummyTrackOutput implements TrackOutput {
|
||||
@Override
|
||||
public void format(MediaFormat format) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
|
||||
throws IOException, InterruptedException {
|
||||
return input.skip(length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sampleData(ParsableByteArray data, int length) {
|
||||
data.skipBytes(length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
|
@ -110,7 +110,7 @@ public final class AdtsExtractor implements Extractor {
|
|||
|
||||
@Override
|
||||
public void init(ExtractorOutput output) {
|
||||
adtsReader = new AdtsReader(output.track(0));
|
||||
adtsReader = new AdtsReader(output.track(0), output.track(1));
|
||||
output.endTracks();
|
||||
output.seekMap(SeekMap.UNSEEKABLE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import com.google.android.exoplayer.util.ParsableByteArray;
|
|||
|
||||
import android.util.Pair;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
|
|
@ -32,20 +33,34 @@ import java.util.Collections;
|
|||
*/
|
||||
/* package */ final class AdtsReader extends ElementaryStreamReader {
|
||||
|
||||
private static final int STATE_FINDING_SYNC = 0;
|
||||
private static final int STATE_READING_HEADER = 1;
|
||||
private static final int STATE_READING_SAMPLE = 2;
|
||||
private static final int STATE_FINDING_SAMPLE = 0;
|
||||
private static final int STATE_READING_ID3_HEADER = 1;
|
||||
private static final int STATE_READING_ADTS_HEADER = 2;
|
||||
private static final int STATE_READING_SAMPLE = 3;
|
||||
|
||||
private static final int HEADER_SIZE = 5;
|
||||
private static final int CRC_SIZE = 2;
|
||||
|
||||
// Match states used while looking for the next sample
|
||||
private static final int MATCH_STATE_VALUE_SHIFT = 8;
|
||||
private static final int MATCH_STATE_START = 1 << MATCH_STATE_VALUE_SHIFT;
|
||||
private static final int MATCH_STATE_FF = 2 << MATCH_STATE_VALUE_SHIFT;
|
||||
private static final int MATCH_STATE_I = 3 << MATCH_STATE_VALUE_SHIFT;
|
||||
private static final int MATCH_STATE_ID = 4 << MATCH_STATE_VALUE_SHIFT;
|
||||
|
||||
private static final int ID3_HEADER_SIZE = 10;
|
||||
private static final int ID3_SIZE_OFFSET = 6;
|
||||
private static final byte[] ID3_IDENTIFIER = {'I', 'D', '3'};
|
||||
|
||||
private final ParsableBitArray adtsScratch;
|
||||
private final ParsableByteArray id3HeaderBuffer;
|
||||
private final TrackOutput id3Output;
|
||||
|
||||
private int state;
|
||||
private int bytesRead;
|
||||
|
||||
// Used to find the header.
|
||||
private boolean lastByteWasFF;
|
||||
private int matchState;
|
||||
|
||||
private boolean hasCrc;
|
||||
|
||||
// Used when parsing the header.
|
||||
|
|
@ -56,17 +71,25 @@ import java.util.Collections;
|
|||
// Used when reading the samples.
|
||||
private long timeUs;
|
||||
|
||||
public AdtsReader(TrackOutput output) {
|
||||
private TrackOutput currentOutput;
|
||||
private long currentSampleDuration;
|
||||
|
||||
/**
|
||||
* @param output A {@link TrackOutput} to which AAC samples should be written.
|
||||
* @param id3Output A {@link TrackOutput} to which ID3 samples should be written.
|
||||
*/
|
||||
public AdtsReader(TrackOutput output, TrackOutput id3Output) {
|
||||
super(output);
|
||||
this.id3Output = id3Output;
|
||||
id3Output.format(MediaFormat.createId3Format());
|
||||
adtsScratch = new ParsableBitArray(new byte[HEADER_SIZE + CRC_SIZE]);
|
||||
state = STATE_FINDING_SYNC;
|
||||
id3HeaderBuffer = new ParsableByteArray(Arrays.copyOf(ID3_IDENTIFIER, ID3_HEADER_SIZE));
|
||||
setFindingSampleState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek() {
|
||||
state = STATE_FINDING_SYNC;
|
||||
bytesRead = 0;
|
||||
lastByteWasFF = false;
|
||||
setFindingSampleState();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -76,30 +99,22 @@ import java.util.Collections;
|
|||
}
|
||||
while (data.bytesLeft() > 0) {
|
||||
switch (state) {
|
||||
case STATE_FINDING_SYNC:
|
||||
if (skipToNextSync(data)) {
|
||||
bytesRead = 0;
|
||||
state = STATE_READING_HEADER;
|
||||
case STATE_FINDING_SAMPLE:
|
||||
findNextSample(data);
|
||||
break;
|
||||
case STATE_READING_ID3_HEADER:
|
||||
if (continueRead(data, id3HeaderBuffer.data, ID3_HEADER_SIZE)) {
|
||||
parseId3Header();
|
||||
}
|
||||
break;
|
||||
case STATE_READING_HEADER:
|
||||
case STATE_READING_ADTS_HEADER:
|
||||
int targetLength = hasCrc ? HEADER_SIZE + CRC_SIZE : HEADER_SIZE;
|
||||
if (continueRead(data, adtsScratch.data, targetLength)) {
|
||||
parseHeader();
|
||||
bytesRead = 0;
|
||||
state = STATE_READING_SAMPLE;
|
||||
parseAdtsHeader();
|
||||
}
|
||||
break;
|
||||
case STATE_READING_SAMPLE:
|
||||
int bytesToRead = Math.min(data.bytesLeft(), sampleSize - bytesRead);
|
||||
output.sampleData(data, bytesToRead);
|
||||
bytesRead += bytesToRead;
|
||||
if (bytesRead == sampleSize) {
|
||||
output.sampleMetadata(timeUs, C.SAMPLE_FLAG_SYNC, sampleSize, 0, null);
|
||||
timeUs += sampleDurationUs;
|
||||
bytesRead = 0;
|
||||
state = STATE_FINDING_SYNC;
|
||||
}
|
||||
readSample(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -127,36 +142,109 @@ import java.util.Collections;
|
|||
}
|
||||
|
||||
/**
|
||||
* Locates the next sync word, advancing the position to the byte that immediately follows it.
|
||||
* If a sync word was not located, the position is advanced to the limit.
|
||||
* Sets the state to STATE_FINDING_SAMPLE.
|
||||
*/
|
||||
private void setFindingSampleState() {
|
||||
state = STATE_FINDING_SAMPLE;
|
||||
bytesRead = 0;
|
||||
matchState = MATCH_STATE_START;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state to STATE_READING_ID3_HEADER and resets the fields required for
|
||||
* {@link #parseId3Header()}.
|
||||
*/
|
||||
private void setReadingId3HeaderState() {
|
||||
state = STATE_READING_ID3_HEADER;
|
||||
bytesRead = ID3_IDENTIFIER.length;
|
||||
sampleSize = 0;
|
||||
id3HeaderBuffer.setPosition(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state to STATE_READING_SAMPLE.
|
||||
*
|
||||
* @param outputToUse TrackOutput object to write the sample to
|
||||
* @param currentSampleDuration Duration of the sample to be read
|
||||
* @param priorReadBytes Size of prior read bytes
|
||||
* @param sampleSize Size of the sample
|
||||
*/
|
||||
private void setReadingSampleState(TrackOutput outputToUse, long currentSampleDuration,
|
||||
int priorReadBytes, int sampleSize) {
|
||||
state = STATE_READING_SAMPLE;
|
||||
bytesRead = priorReadBytes;
|
||||
this.currentOutput = outputToUse;
|
||||
this.currentSampleDuration = currentSampleDuration;
|
||||
this.sampleSize = sampleSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state to STATE_READING_ADTS_HEADER.
|
||||
*/
|
||||
private void setReadingAdtsHeaderState() {
|
||||
state = STATE_READING_ADTS_HEADER;
|
||||
bytesRead = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the next sample start, advancing the position to the byte that immediately follows
|
||||
* identifier. If a sample was not located, the position is advanced to the limit.
|
||||
*
|
||||
* @param pesBuffer The buffer whose position should be advanced.
|
||||
* @return True if a sync word position was found. False otherwise.
|
||||
*/
|
||||
private boolean skipToNextSync(ParsableByteArray pesBuffer) {
|
||||
private void findNextSample(ParsableByteArray pesBuffer) {
|
||||
byte[] adtsData = pesBuffer.data;
|
||||
int startOffset = pesBuffer.getPosition();
|
||||
int position = pesBuffer.getPosition();
|
||||
int endOffset = pesBuffer.limit();
|
||||
for (int i = startOffset; i < endOffset; i++) {
|
||||
boolean byteIsFF = (adtsData[i] & 0xFF) == 0xFF;
|
||||
boolean found = lastByteWasFF && !byteIsFF && (adtsData[i] & 0xF0) == 0xF0;
|
||||
lastByteWasFF = byteIsFF;
|
||||
if (found) {
|
||||
hasCrc = (adtsData[i] & 0x1) == 0;
|
||||
pesBuffer.setPosition(i + 1);
|
||||
// Reset lastByteWasFF for next time.
|
||||
lastByteWasFF = false;
|
||||
return true;
|
||||
while (position < endOffset) {
|
||||
int data = adtsData[position++] & 0xFF;
|
||||
if (matchState == MATCH_STATE_FF && data >= 0xF0 && data != 0xFF) {
|
||||
hasCrc = (data & 0x1) == 0;
|
||||
setReadingAdtsHeaderState();
|
||||
pesBuffer.setPosition(position);
|
||||
return;
|
||||
}
|
||||
switch (matchState | data) {
|
||||
case MATCH_STATE_START | 0xFF:
|
||||
matchState = MATCH_STATE_FF;
|
||||
break;
|
||||
case MATCH_STATE_START | 'I':
|
||||
matchState = MATCH_STATE_I;
|
||||
break;
|
||||
case MATCH_STATE_I | 'D':
|
||||
matchState = MATCH_STATE_ID;
|
||||
break;
|
||||
case MATCH_STATE_ID | '3':
|
||||
setReadingId3HeaderState();
|
||||
pesBuffer.setPosition(position);
|
||||
return;
|
||||
default:
|
||||
if (matchState != MATCH_STATE_START) {
|
||||
// If matching fails in a later state, revert to MATCH_STATE_START and
|
||||
// check this byte again
|
||||
matchState = MATCH_STATE_START;
|
||||
position--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
pesBuffer.setPosition(endOffset);
|
||||
return false;
|
||||
pesBuffer.setPosition(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the Id3 header.
|
||||
*/
|
||||
private void parseId3Header() {
|
||||
id3Output.sampleData(id3HeaderBuffer, ID3_HEADER_SIZE);
|
||||
id3HeaderBuffer.setPosition(ID3_SIZE_OFFSET);
|
||||
setReadingSampleState(id3Output, 0, ID3_HEADER_SIZE,
|
||||
id3HeaderBuffer.readSynchSafeInt() + ID3_HEADER_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the sample header.
|
||||
*/
|
||||
private void parseHeader() {
|
||||
private void parseAdtsHeader() {
|
||||
adtsScratch.setPosition(0);
|
||||
|
||||
if (!hasOutputFormat) {
|
||||
|
|
@ -183,10 +271,26 @@ import java.util.Collections;
|
|||
}
|
||||
|
||||
adtsScratch.skipBits(4);
|
||||
sampleSize = adtsScratch.readBits(13) - 2 /* the sync word */ - HEADER_SIZE;
|
||||
int sampleSize = adtsScratch.readBits(13) - 2 /* the sync word */ - HEADER_SIZE;
|
||||
if (hasCrc) {
|
||||
sampleSize -= CRC_SIZE;
|
||||
}
|
||||
|
||||
setReadingSampleState(output, sampleDurationUs, 0, sampleSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the rest of the sample
|
||||
*/
|
||||
private void readSample(ParsableByteArray data) {
|
||||
int bytesToRead = Math.min(data.bytesLeft(), sampleSize - bytesRead);
|
||||
currentOutput.sampleData(data, bytesToRead);
|
||||
bytesRead += bytesToRead;
|
||||
if (bytesRead == sampleSize) {
|
||||
currentOutput.sampleMetadata(timeUs, C.SAMPLE_FLAG_SYNC, sampleSize, 0, null);
|
||||
timeUs += currentSampleDuration;
|
||||
setFindingSampleState();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ package com.google.android.exoplayer.extractor.ts;
|
|||
import com.google.android.exoplayer.C;
|
||||
import com.google.android.exoplayer.MediaFormat;
|
||||
import com.google.android.exoplayer.extractor.TrackOutput;
|
||||
import com.google.android.exoplayer.util.MimeTypes;
|
||||
import com.google.android.exoplayer.util.ParsableByteArray;
|
||||
|
||||
/**
|
||||
|
|
@ -35,8 +34,7 @@ import com.google.android.exoplayer.util.ParsableByteArray;
|
|||
|
||||
public Id3Reader(TrackOutput output) {
|
||||
super(output);
|
||||
output.format(MediaFormat.createFormatForMimeType(null, MimeTypes.APPLICATION_ID3,
|
||||
MediaFormat.NO_VALUE, C.UNKNOWN_TIME_US));
|
||||
output.format(MediaFormat.createId3Format());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.google.android.exoplayer.extractor.ts;
|
||||
|
||||
import com.google.android.exoplayer.extractor.DummyTrackOutput;
|
||||
import com.google.android.exoplayer.extractor.Extractor;
|
||||
import com.google.android.exoplayer.extractor.ExtractorInput;
|
||||
import com.google.android.exoplayer.extractor.ExtractorOutput;
|
||||
|
|
@ -334,7 +335,8 @@ public final class TsExtractor implements Extractor {
|
|||
pesPayloadReader = new MpegAudioReader(output.track(TS_STREAM_TYPE_MPA_LSF));
|
||||
break;
|
||||
case TS_STREAM_TYPE_AAC:
|
||||
pesPayloadReader = new AdtsReader(output.track(TS_STREAM_TYPE_AAC));
|
||||
pesPayloadReader = new AdtsReader(output.track(TS_STREAM_TYPE_AAC),
|
||||
new DummyTrackOutput());
|
||||
break;
|
||||
case TS_STREAM_TYPE_AC3:
|
||||
pesPayloadReader = new Ac3Reader(output.track(TS_STREAM_TYPE_AC3), false);
|
||||
|
|
|
|||
Loading…
Reference in a new issue