parent
fb97fca04e
commit
c4eee71fa2
@ -43,10 +43,11 @@ public class DemoUtil {
|
||||
|
||||
public static final int TYPE_DASH = 0;
|
||||
public static final int TYPE_SS = 1;
|
||||
public static final int TYPE_OTHER = 2;
|
||||
public static final int TYPE_HLS = 3;
|
||||
public static final int TYPE_MP4 = 4;
|
||||
public static final int TYPE_MP3 = 5;
|
||||
public static final int TYPE_HLS = 2;
|
||||
public static final int TYPE_MP4 = 3;
|
||||
public static final int TYPE_MP3 = 4;
|
||||
public static final int TYPE_AAC = 5;
|
||||
public static final int TYPE_OTHER = 6;
|
||||
|
||||
private static final CookieManager defaultCookieManager;
|
||||
|
||||
|
@ -29,6 +29,7 @@ import com.google.android.exoplayer.demo.player.SmoothStreamingRendererBuilder;
|
||||
import com.google.android.exoplayer.demo.player.UnsupportedDrmException;
|
||||
import com.google.android.exoplayer.extractor.mp3.Mp3Extractor;
|
||||
import com.google.android.exoplayer.extractor.mp4.Mp4Extractor;
|
||||
import com.google.android.exoplayer.extractor.ts.AdtsExtractor;
|
||||
import com.google.android.exoplayer.metadata.GeobMetadata;
|
||||
import com.google.android.exoplayer.metadata.PrivMetadata;
|
||||
import com.google.android.exoplayer.metadata.TxxxMetadata;
|
||||
@ -234,6 +235,9 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
|
||||
case DemoUtil.TYPE_MP3:
|
||||
return new ExtractorRendererBuilder(userAgent, contentUri, debugTextView,
|
||||
new Mp3Extractor());
|
||||
case DemoUtil.TYPE_AAC:
|
||||
return new ExtractorRendererBuilder(userAgent, contentUri, debugTextView,
|
||||
new AdtsExtractor());
|
||||
default:
|
||||
return new DefaultRendererBuilder(this, contentUri, debugTextView);
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ import java.util.Locale;
|
||||
DemoUtil.TYPE_OTHER),
|
||||
new Sample("Apple AAC 10s", "https://devimages.apple.com.edgekey.net/"
|
||||
+ "streaming/examples/bipbop_4x3/gear0/fileSequence0.aac",
|
||||
DemoUtil.TYPE_OTHER),
|
||||
DemoUtil.TYPE_AAC),
|
||||
new Sample("Big Buck Bunny (MP4 Video)",
|
||||
"http://redirector.c.youtube.com/videoplayback?id=604ed5ce52eda7ee&itag=22&source=youtube"
|
||||
+ "&sparams=ip,ipbits,expire&ip=0.0.0.0&ipbits=0&expire=19000000000&signature="
|
||||
|
@ -71,6 +71,13 @@ public final class ChunkIndex implements SeekMap {
|
||||
return Util.binarySearchFloor(timesUs, timeUs, true, true);
|
||||
}
|
||||
|
||||
// SeekMap implementation.
|
||||
|
||||
@Override
|
||||
public boolean isSeekable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPosition(long timeUs) {
|
||||
return offsets[getChunkIndex(timeUs)];
|
||||
|
@ -20,13 +20,23 @@ package com.google.android.exoplayer.extractor;
|
||||
*/
|
||||
public interface SeekMap {
|
||||
|
||||
/**
|
||||
* Whether or not the seeking is supported.
|
||||
* <p>
|
||||
* If seeking is not supported then the only valid seek position is the start of the file, and so
|
||||
* {@link #getPosition(long)} will return 0 for all input values.
|
||||
*
|
||||
* @return True if seeking is supported. False otherwise.
|
||||
*/
|
||||
boolean isSeekable();
|
||||
|
||||
/**
|
||||
* Maps a seek position in microseconds to a corresponding position (byte offset) in the stream
|
||||
* from which data can be provided to the extractor.
|
||||
*
|
||||
* @param timeUs A seek position in microseconds.
|
||||
* @return The corresponding position (byte offset) in the stream from which data can be provided
|
||||
* to the extractor.
|
||||
* to the extractor, or 0 if {@code #isSeekable()} returns false.
|
||||
*/
|
||||
long getPosition(long timeUs);
|
||||
|
||||
|
@ -20,7 +20,7 @@ import com.google.android.exoplayer.C;
|
||||
/**
|
||||
* MP3 seeker that doesn't rely on metadata and seeks assuming the source has a constant bitrate.
|
||||
*/
|
||||
/* package */ final class ConstantBitrateSeeker implements Mp3Extractor.Seeker {
|
||||
/* package */ final class ConstantBitrateSeeker extends Mp3Extractor.Seeker {
|
||||
|
||||
private static final int MICROSECONDS_PER_SECOND = 1000000;
|
||||
private static final int BITS_PER_BYTE = 8;
|
||||
|
@ -37,25 +37,6 @@ import java.util.Collections;
|
||||
*/
|
||||
public final class Mp3Extractor implements Extractor {
|
||||
|
||||
/**
|
||||
* {@link SeekMap} that also allows mapping from position (byte offset) back to time, which can be
|
||||
* used to work out the new sample basis timestamp after seeking and resynchronization.
|
||||
*/
|
||||
/* package */ interface Seeker extends SeekMap {
|
||||
|
||||
/**
|
||||
* Maps a position (byte offset) to a corresponding sample timestamp.
|
||||
*
|
||||
* @param position A seek position (byte offset) relative to the start of the stream.
|
||||
* @return The corresponding timestamp of the next sample to be read, in microseconds.
|
||||
*/
|
||||
long getTimeUs(long position);
|
||||
|
||||
/** Returns the duration of the source, in microseconds. */
|
||||
long getDurationUs();
|
||||
|
||||
}
|
||||
|
||||
/** The maximum number of bytes to search when synchronizing, before giving up. */
|
||||
private static final int MAX_BYTES_TO_SEARCH = 128 * 1024;
|
||||
|
||||
@ -288,4 +269,28 @@ public final class Mp3Extractor implements Extractor {
|
||||
return extractorInput.getPosition() - bufferingInput.getAvailableByteCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link SeekMap} that also allows mapping from position (byte offset) back to time, which can be
|
||||
* used to work out the new sample basis timestamp after seeking and resynchronization.
|
||||
*/
|
||||
/* package */ abstract static class Seeker implements SeekMap {
|
||||
|
||||
@Override
|
||||
public final boolean isSeekable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a position (byte offset) to a corresponding sample timestamp.
|
||||
*
|
||||
* @param position A seek position (byte offset) relative to the start of the stream.
|
||||
* @return The corresponding timestamp of the next sample to be read, in microseconds.
|
||||
*/
|
||||
abstract long getTimeUs(long position);
|
||||
|
||||
/** Returns the duration of the source, in microseconds. */
|
||||
abstract long getDurationUs();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ import com.google.android.exoplayer.util.Util;
|
||||
/**
|
||||
* MP3 seeker that uses metadata from a VBRI header.
|
||||
*/
|
||||
/* package */ final class VbriSeeker implements Mp3Extractor.Seeker {
|
||||
/* package */ final class VbriSeeker extends Mp3Extractor.Seeker {
|
||||
|
||||
private static final int VBRI_HEADER = Util.getIntegerCodeForString("VBRI");
|
||||
|
||||
|
@ -22,7 +22,7 @@ import com.google.android.exoplayer.util.Util;
|
||||
/**
|
||||
* MP3 seeker that uses metadata from a XING header.
|
||||
*/
|
||||
/* package */ final class XingSeeker implements Mp3Extractor.Seeker {
|
||||
/* package */ final class XingSeeker extends Mp3Extractor.Seeker {
|
||||
|
||||
private static final int XING_HEADER = Util.getIntegerCodeForString("Xing");
|
||||
private static final int INFO_HEADER = Util.getIntegerCodeForString("Info");
|
||||
|
@ -113,6 +113,11 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
|
||||
// SeekMap implementation.
|
||||
|
||||
@Override
|
||||
public boolean isSeekable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPosition(long timeUs) {
|
||||
long earliestSamplePosition = Long.MAX_VALUE;
|
||||
@ -132,6 +137,8 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
return earliestSamplePosition;
|
||||
}
|
||||
|
||||
// Private methods.
|
||||
|
||||
private boolean readAtomHeader(ExtractorInput input) throws IOException, InterruptedException {
|
||||
if (!input.readFully(atomHeader.data, 0, Atom.HEADER_SIZE, true)) {
|
||||
return false;
|
||||
|
@ -19,6 +19,7 @@ import com.google.android.exoplayer.extractor.Extractor;
|
||||
import com.google.android.exoplayer.extractor.ExtractorInput;
|
||||
import com.google.android.exoplayer.extractor.ExtractorOutput;
|
||||
import com.google.android.exoplayer.extractor.PositionHolder;
|
||||
import com.google.android.exoplayer.extractor.SeekMap;
|
||||
import com.google.android.exoplayer.util.ParsableByteArray;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -27,19 +28,23 @@ import java.io.IOException;
|
||||
* Facilitates the extraction of AAC samples from elementary audio files formatted as AAC with ADTS
|
||||
* headers.
|
||||
*/
|
||||
public class AdtsExtractor implements Extractor {
|
||||
public class AdtsExtractor implements Extractor, SeekMap {
|
||||
|
||||
private static final int MAX_PACKET_SIZE = 200;
|
||||
|
||||
private final long firstSampleTimestamp;
|
||||
private final long firstSampleTimestampUs;
|
||||
private final ParsableByteArray packetBuffer;
|
||||
|
||||
// Accessed only by the loading thread.
|
||||
private AdtsReader adtsReader;
|
||||
private boolean firstPacket;
|
||||
|
||||
public AdtsExtractor(long firstSampleTimestamp) {
|
||||
this.firstSampleTimestamp = firstSampleTimestamp;
|
||||
public AdtsExtractor() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
public AdtsExtractor(long firstSampleTimestampUs) {
|
||||
this.firstSampleTimestampUs = firstSampleTimestampUs;
|
||||
packetBuffer = new ParsableByteArray(MAX_PACKET_SIZE);
|
||||
firstPacket = true;
|
||||
}
|
||||
@ -48,11 +53,13 @@ public class AdtsExtractor implements Extractor {
|
||||
public void init(ExtractorOutput output) {
|
||||
adtsReader = new AdtsReader(output.track(0));
|
||||
output.endTracks();
|
||||
output.seekMap(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek() {
|
||||
throw new UnsupportedOperationException();
|
||||
adtsReader.seek();
|
||||
firstPacket = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -69,9 +76,21 @@ public class AdtsExtractor implements Extractor {
|
||||
|
||||
// TODO: Make it possible for adtsReader to consume the dataSource directly, so that it becomes
|
||||
// unnecessary to copy the data through packetBuffer.
|
||||
adtsReader.consume(packetBuffer, firstSampleTimestamp, firstPacket);
|
||||
adtsReader.consume(packetBuffer, firstSampleTimestampUs, firstPacket);
|
||||
firstPacket = false;
|
||||
return RESULT_CONTINUE;
|
||||
}
|
||||
|
||||
// SeekMap implementation.
|
||||
|
||||
@Override
|
||||
public boolean isSeekable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPosition(long timeUs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -62,6 +62,18 @@ import java.util.Collections;
|
||||
state = STATE_FINDING_SYNC;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the reader that a seek has occurred.
|
||||
* <p>
|
||||
* The data passed to the next invocation of {@link #consume(ParsableByteArray, long, boolean)}
|
||||
* should not be treated as a continuation of the data passed to previous calls.
|
||||
*/
|
||||
public void seek() {
|
||||
state = STATE_FINDING_SYNC;
|
||||
bytesRead = 0;
|
||||
lastByteWasFF = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ParsableByteArray data, long pesTimeUs, boolean startOfPacket) {
|
||||
if (startOfPacket) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user