mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Big (and hopefully near-final) rename.
1. Rename "extensions" package to "decoder". It's used by both text and (should be used by) metadata, so it's no longer just for extensions. 2. Move Buffer objects move into the decoder package. 3. Rename SubtitleParser and MetadataParser to SubtitleDecoder and MetadataDecoder respectively, since they extend Decoder. Ditto for all subclasses. 4. Subtitle and Metadata decoders now throw their own exception types rather than ParserException. 5. Move MediaCodec classes into a mediacodec package, with the exception of the concrete audio and video renderers. 6. Create an audio package to hold the two audio renderer classes plus related util classes. 7. Create a video package to hold the one video renderer class plus related util classes. After this change the following nice properties hold: 1. Want a video renderer? Look in the video package. Ditto for audio, text and metadata. 2. All TrackRenderer implementations use a decoder of some kind to decode buffers received from the source, so we have consistent terminology there. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=127326805
This commit is contained in:
parent
4f718363b5
commit
f758082d40
@ -15,12 +15,12 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.demo;
|
||||
|
||||
import com.google.android.exoplayer2.CodecCounters;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.Renderer;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.decoder.DecoderCounters;
|
||||
import com.google.android.exoplayer2.drm.StreamingDrmSessionManager;
|
||||
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener;
|
||||
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
||||
@ -145,7 +145,7 @@ public class EventLogger implements ExoPlayer.EventListener, SimpleExoPlayer.Deb
|
||||
// SimpleExoPlayer.DebugListener
|
||||
|
||||
@Override
|
||||
public void onAudioEnabled(CodecCounters counters) {
|
||||
public void onAudioEnabled(DecoderCounters counters) {
|
||||
Log.d(TAG, "audioEnabled [" + getSessionTimeString() + "]");
|
||||
}
|
||||
|
||||
@ -167,12 +167,12 @@ public class EventLogger implements ExoPlayer.EventListener, SimpleExoPlayer.Deb
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioDisabled(CodecCounters counters) {
|
||||
public void onAudioDisabled(DecoderCounters counters) {
|
||||
Log.d(TAG, "audioDisabled [" + getSessionTimeString() + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoEnabled(CodecCounters counters) {
|
||||
public void onVideoEnabled(DecoderCounters counters) {
|
||||
Log.d(TAG, "videoEnabled [" + getSessionTimeString() + "]");
|
||||
}
|
||||
|
||||
@ -189,7 +189,7 @@ public class EventLogger implements ExoPlayer.EventListener, SimpleExoPlayer.Deb
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoDisabled(CodecCounters counters) {
|
||||
public void onVideoDisabled(DecoderCounters counters) {
|
||||
Log.d(TAG, "videoDisabled [" + getSessionTimeString() + "]");
|
||||
}
|
||||
|
||||
|
@ -20,13 +20,13 @@ import com.google.android.exoplayer2.DefaultLoadControl;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.MediaCodecRenderer.DecoderInitializationException;
|
||||
import com.google.android.exoplayer2.MediaCodecUtil.DecoderQueryException;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.drm.StreamingDrmSessionManager;
|
||||
import com.google.android.exoplayer2.drm.UnsupportedDrmException;
|
||||
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.DecoderInitializationException;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException;
|
||||
import com.google.android.exoplayer2.metadata.id3.ApicFrame;
|
||||
import com.google.android.exoplayer2.metadata.id3.GeobFrame;
|
||||
import com.google.android.exoplayer2.metadata.id3.Id3Frame;
|
||||
@ -52,7 +52,7 @@ import com.google.android.exoplayer2.upstream.BandwidthMeter;
|
||||
import com.google.android.exoplayer2.upstream.DataSourceFactory;
|
||||
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||
import com.google.android.exoplayer2.util.DebugTextViewHelper;
|
||||
import com.google.android.exoplayer2.ui.DebugTextViewHelper;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
import android.Manifest.permission;
|
||||
|
@ -15,12 +15,12 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.ext.ffmpeg;
|
||||
|
||||
import com.google.android.exoplayer2.AudioRendererEventListener;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.audio.AudioCapabilities;
|
||||
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
|
||||
import com.google.android.exoplayer2.audio.AudioTrack;
|
||||
import com.google.android.exoplayer2.extensions.AudioDecoderRenderer;
|
||||
import com.google.android.exoplayer2.audio.SimpleDecoderAudioRenderer;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
|
||||
import android.os.Handler;
|
||||
@ -28,7 +28,7 @@ import android.os.Handler;
|
||||
/**
|
||||
* Decodes and renders audio using FFmpeg.
|
||||
*/
|
||||
public final class FfmpegAudioRenderer extends AudioDecoderRenderer {
|
||||
public final class FfmpegAudioRenderer extends SimpleDecoderAudioRenderer {
|
||||
|
||||
private static final int NUM_BUFFERS = 16;
|
||||
private static final int INITIAL_INPUT_BUFFER_SIZE = 960 * 6;
|
||||
|
@ -15,9 +15,9 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.ext.ffmpeg;
|
||||
|
||||
import com.google.android.exoplayer2.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.extensions.SimpleDecoder;
|
||||
import com.google.android.exoplayer2.extensions.SimpleOutputBuffer;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.decoder.SimpleDecoder;
|
||||
import com.google.android.exoplayer2.decoder.SimpleOutputBuffer;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.ext.ffmpeg;
|
||||
|
||||
import com.google.android.exoplayer2.extensions.AudioDecoderException;
|
||||
import com.google.android.exoplayer2.audio.AudioDecoderException;
|
||||
|
||||
/**
|
||||
* Thrown when an FFmpeg decoder error occurs.
|
||||
|
@ -15,9 +15,9 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.ext.flac;
|
||||
|
||||
import com.google.android.exoplayer2.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.extensions.SimpleDecoder;
|
||||
import com.google.android.exoplayer2.extensions.SimpleOutputBuffer;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.decoder.SimpleDecoder;
|
||||
import com.google.android.exoplayer2.decoder.SimpleOutputBuffer;
|
||||
import com.google.android.exoplayer2.util.FlacStreamInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.ext.flac;
|
||||
|
||||
import com.google.android.exoplayer2.extensions.AudioDecoderException;
|
||||
import com.google.android.exoplayer2.audio.AudioDecoderException;
|
||||
|
||||
/**
|
||||
* Thrown when an Flac decoder error occurs.
|
||||
|
@ -15,11 +15,11 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.ext.flac;
|
||||
|
||||
import com.google.android.exoplayer2.AudioRendererEventListener;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.audio.AudioCapabilities;
|
||||
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
|
||||
import com.google.android.exoplayer2.audio.AudioTrack;
|
||||
import com.google.android.exoplayer2.extensions.AudioDecoderRenderer;
|
||||
import com.google.android.exoplayer2.audio.SimpleDecoderAudioRenderer;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
|
||||
import android.os.Handler;
|
||||
@ -27,7 +27,7 @@ import android.os.Handler;
|
||||
/**
|
||||
* Decodes and renders audio using the native Flac decoder.
|
||||
*/
|
||||
public class LibflacAudioRenderer extends AudioDecoderRenderer {
|
||||
public class LibflacAudioRenderer extends SimpleDecoderAudioRenderer {
|
||||
|
||||
private static final int NUM_BUFFERS = 16;
|
||||
|
||||
|
@ -15,11 +15,11 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.ext.opus;
|
||||
|
||||
import com.google.android.exoplayer2.AudioRendererEventListener;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.audio.AudioCapabilities;
|
||||
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
|
||||
import com.google.android.exoplayer2.audio.AudioTrack;
|
||||
import com.google.android.exoplayer2.extensions.AudioDecoderRenderer;
|
||||
import com.google.android.exoplayer2.audio.SimpleDecoderAudioRenderer;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
|
||||
import android.os.Handler;
|
||||
@ -27,7 +27,7 @@ import android.os.Handler;
|
||||
/**
|
||||
* Decodes and renders audio using the native Opus decoder.
|
||||
*/
|
||||
public final class LibopusAudioRenderer extends AudioDecoderRenderer {
|
||||
public final class LibopusAudioRenderer extends SimpleDecoderAudioRenderer {
|
||||
|
||||
private static final int NUM_BUFFERS = 16;
|
||||
private static final int INITIAL_INPUT_BUFFER_SIZE = 960 * 6;
|
||||
|
@ -16,9 +16,9 @@
|
||||
package com.google.android.exoplayer2.ext.opus;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.extensions.SimpleDecoder;
|
||||
import com.google.android.exoplayer2.extensions.SimpleOutputBuffer;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.decoder.SimpleDecoder;
|
||||
import com.google.android.exoplayer2.decoder.SimpleOutputBuffer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.ext.opus;
|
||||
|
||||
import com.google.android.exoplayer2.extensions.AudioDecoderException;
|
||||
import com.google.android.exoplayer2.audio.AudioDecoderException;
|
||||
|
||||
/**
|
||||
* Thrown when an Opus decoder error occurs.
|
||||
|
@ -16,17 +16,17 @@
|
||||
package com.google.android.exoplayer2.ext.vp9;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.CodecCounters;
|
||||
import com.google.android.exoplayer2.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.FormatHolder;
|
||||
import com.google.android.exoplayer2.Renderer;
|
||||
import com.google.android.exoplayer2.VideoRendererEventListener;
|
||||
import com.google.android.exoplayer2.VideoRendererEventListener.EventDispatcher;
|
||||
import com.google.android.exoplayer2.decoder.DecoderCounters;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.TraceUtil;
|
||||
import com.google.android.exoplayer2.video.VideoRendererEventListener;
|
||||
import com.google.android.exoplayer2.video.VideoRendererEventListener.EventDispatcher;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
@ -60,7 +60,7 @@ public final class LibvpxVideoRenderer extends Renderer {
|
||||
private final EventDispatcher eventDispatcher;
|
||||
private final FormatHolder formatHolder;
|
||||
|
||||
private CodecCounters codecCounters;
|
||||
private DecoderCounters decoderCounters;
|
||||
private Format format;
|
||||
private VpxDecoder decoder;
|
||||
private DecoderInputBuffer inputBuffer;
|
||||
@ -172,7 +172,7 @@ public final class LibvpxVideoRenderer extends Renderer {
|
||||
long codecInitializedTimestamp = SystemClock.elapsedRealtime();
|
||||
eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp,
|
||||
codecInitializedTimestamp - codecInitializingTimestamp);
|
||||
codecCounters.codecInitCount++;
|
||||
decoderCounters.codecInitCount++;
|
||||
}
|
||||
TraceUtil.beginSection("drainAndFeed");
|
||||
while (drainOutputBuffer(positionUs)) {}
|
||||
@ -181,7 +181,7 @@ public final class LibvpxVideoRenderer extends Renderer {
|
||||
} catch (VpxDecoderException e) {
|
||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||
}
|
||||
codecCounters.ensureUpdated();
|
||||
decoderCounters.ensureUpdated();
|
||||
}
|
||||
|
||||
private boolean drainOutputBuffer(long positionUs) throws VpxDecoderException {
|
||||
@ -200,7 +200,7 @@ public final class LibvpxVideoRenderer extends Renderer {
|
||||
if (outputBuffer == null) {
|
||||
return false;
|
||||
}
|
||||
codecCounters.skippedOutputBufferCount += outputBuffer.skippedOutputBufferCount;
|
||||
decoderCounters.skippedOutputBufferCount += outputBuffer.skippedOutputBufferCount;
|
||||
}
|
||||
|
||||
if (nextOutputBuffer == null) {
|
||||
@ -219,11 +219,12 @@ public final class LibvpxVideoRenderer extends Renderer {
|
||||
if ((joiningDeadlineMs != -1 && outputBuffer.timestampUs < positionUs - 30000)
|
||||
|| (nextOutputBuffer != null && !nextOutputBuffer.isEndOfStream()
|
||||
&& nextOutputBuffer.timestampUs < positionUs)) {
|
||||
codecCounters.droppedOutputBufferCount++;
|
||||
decoderCounters.droppedOutputBufferCount++;
|
||||
droppedFrameCount++;
|
||||
consecutiveDroppedFrameCount++;
|
||||
codecCounters.maxConsecutiveDroppedOutputBufferCount = Math.max(consecutiveDroppedFrameCount,
|
||||
codecCounters.maxConsecutiveDroppedOutputBufferCount);
|
||||
decoderCounters.maxConsecutiveDroppedOutputBufferCount = Math.max(
|
||||
consecutiveDroppedFrameCount,
|
||||
decoderCounters.maxConsecutiveDroppedOutputBufferCount);
|
||||
if (droppedFrameCount == maxDroppedFrameCountToNotify) {
|
||||
maybeNotifyDroppedFrameCount();
|
||||
}
|
||||
@ -248,7 +249,7 @@ public final class LibvpxVideoRenderer extends Renderer {
|
||||
}
|
||||
|
||||
private void renderBuffer() {
|
||||
codecCounters.renderedOutputBufferCount++;
|
||||
decoderCounters.renderedOutputBufferCount++;
|
||||
consecutiveDroppedFrameCount = 0;
|
||||
maybeNotifyVideoSizeChanged(outputBuffer.width, outputBuffer.height);
|
||||
if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_RGB && surface != null) {
|
||||
@ -310,7 +311,7 @@ public final class LibvpxVideoRenderer extends Renderer {
|
||||
}
|
||||
inputBuffer.flip();
|
||||
decoder.queueInputBuffer(inputBuffer);
|
||||
codecCounters.inputBufferCount++;
|
||||
decoderCounters.inputBufferCount++;
|
||||
inputBuffer = null;
|
||||
return true;
|
||||
}
|
||||
@ -354,8 +355,8 @@ public final class LibvpxVideoRenderer extends Renderer {
|
||||
|
||||
@Override
|
||||
protected void onEnabled(boolean joining) throws ExoPlaybackException {
|
||||
codecCounters = new CodecCounters();
|
||||
eventDispatcher.enabled(codecCounters);
|
||||
decoderCounters = new DecoderCounters();
|
||||
eventDispatcher.enabled(decoderCounters);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -392,11 +393,11 @@ public final class LibvpxVideoRenderer extends Renderer {
|
||||
if (decoder != null) {
|
||||
decoder.release();
|
||||
decoder = null;
|
||||
codecCounters.codecReleaseCount++;
|
||||
decoderCounters.codecReleaseCount++;
|
||||
}
|
||||
} finally {
|
||||
codecCounters.ensureUpdated();
|
||||
eventDispatcher.disabled(codecCounters);
|
||||
decoderCounters.ensureUpdated();
|
||||
eventDispatcher.disabled(decoderCounters);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,8 @@
|
||||
package com.google.android.exoplayer2.ext.vp9;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.extensions.SimpleDecoder;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.decoder.SimpleDecoder;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.ext.vp9;
|
||||
|
||||
import com.google.android.exoplayer2.extensions.OutputBuffer;
|
||||
import com.google.android.exoplayer2.decoder.OutputBuffer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
package com.google.android.exoplayer2.extractor.mp3;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.util.MpegAudioHeader;
|
||||
import com.google.android.exoplayer2.extractor.MpegAudioHeader;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
|
@ -15,36 +15,37 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.metadata.id3;
|
||||
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.metadata.MetadataDecoderException;
|
||||
|
||||
import android.test.MoreAsserts;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Test for {@link Id3Parser}
|
||||
* Test for {@link Id3Decoder}
|
||||
*/
|
||||
public class Id3ParserTest extends TestCase {
|
||||
public class Id3DecoderTest extends TestCase {
|
||||
|
||||
public void testParseTxxxFrame() throws ParserException {
|
||||
public void testDecodeTxxxFrame() throws MetadataDecoderException {
|
||||
byte[] rawId3 = new byte[] {73, 68, 51, 4, 0, 0, 0, 0, 0, 41, 84, 88, 88, 88, 0, 0, 0, 31, 0, 0,
|
||||
3, 0, 109, 100, 105, 97, 108, 111, 103, 95, 86, 73, 78, 68, 73, 67, 79, 49, 53, 50, 55, 54,
|
||||
54, 52, 95, 115, 116, 97, 114, 116, 0};
|
||||
Id3Parser parser = new Id3Parser();
|
||||
List<Id3Frame> id3Frames = parser.parse(rawId3, rawId3.length);
|
||||
Id3Decoder decoder = new Id3Decoder();
|
||||
List<Id3Frame> id3Frames = decoder.decode(rawId3, rawId3.length);
|
||||
assertEquals(1, id3Frames.size());
|
||||
TxxxFrame txxxFrame = (TxxxFrame) id3Frames.get(0);
|
||||
assertEquals("", txxxFrame.description);
|
||||
assertEquals("mdialog_VINDICO1527664_start", txxxFrame.value);
|
||||
}
|
||||
|
||||
public void testParseApicFrame() throws ParserException {
|
||||
public void testDecodeApicFrame() throws MetadataDecoderException {
|
||||
byte[] rawId3 = new byte[] {73, 68, 51, 4, 0, 0, 0, 0, 0, 45, 65, 80, 73, 67, 0, 0, 0, 35, 0, 0,
|
||||
3, 105, 109, 97, 103, 101, 47, 106, 112, 101, 103, 0, 16, 72, 101, 108, 108, 111, 32, 87,
|
||||
111, 114, 108, 100, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
|
||||
Id3Parser parser = new Id3Parser();
|
||||
List<Id3Frame> id3Frames = parser.parse(rawId3, rawId3.length);
|
||||
Id3Decoder decoder = new Id3Decoder();
|
||||
List<Id3Frame> id3Frames = decoder.decode(rawId3, rawId3.length);
|
||||
assertEquals(1, id3Frames.size());
|
||||
ApicFrame apicFrame = (ApicFrame) id3Frames.get(0);
|
||||
assertEquals("image/jpeg", apicFrame.mimeType);
|
||||
@ -54,11 +55,11 @@ public class Id3ParserTest extends TestCase {
|
||||
MoreAsserts.assertEquals(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, apicFrame.pictureData);
|
||||
}
|
||||
|
||||
public void testParseTextInformationFrame() throws ParserException {
|
||||
public void testDecodeTextInformationFrame() throws MetadataDecoderException {
|
||||
byte[] rawId3 = new byte[] {73, 68, 51, 4, 0, 0, 0, 0, 0, 23, 84, 73, 84, 50, 0, 0, 0, 13, 0, 0,
|
||||
3, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 0};
|
||||
Id3Parser parser = new Id3Parser();
|
||||
List<Id3Frame> id3Frames = parser.parse(rawId3, rawId3.length);
|
||||
Id3Decoder decoder = new Id3Decoder();
|
||||
List<Id3Frame> id3Frames = decoder.decode(rawId3, rawId3.length);
|
||||
assertEquals(1, id3Frames.size());
|
||||
TextInformationFrame textInformationFrame = (TextInformationFrame) id3Frames.get(0);
|
||||
assertEquals("TIT2", textInformationFrame.id);
|
@ -22,9 +22,9 @@ import android.test.InstrumentationTestCase;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Unit test for {@link SubripParser}.
|
||||
* Unit test for {@link SubripDecoder}.
|
||||
*/
|
||||
public final class SubripParserTest extends InstrumentationTestCase {
|
||||
public final class SubripDecoderTest extends InstrumentationTestCase {
|
||||
|
||||
private static final String EMPTY_FILE = "subrip/empty";
|
||||
private static final String TYPICAL_FILE = "subrip/typical";
|
||||
@ -34,69 +34,69 @@ public final class SubripParserTest extends InstrumentationTestCase {
|
||||
private static final String TYPICAL_MISSING_SEQUENCE = "subrip/typical_missing_sequence";
|
||||
private static final String NO_END_TIMECODES_FILE = "subrip/no_end_timecodes";
|
||||
|
||||
public void testParseEmpty() throws IOException {
|
||||
SubripParser parser = new SubripParser();
|
||||
public void testDecodeEmpty() throws IOException {
|
||||
SubripDecoder decoder = new SubripDecoder();
|
||||
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), EMPTY_FILE);
|
||||
SubripSubtitle subtitle = parser.decode(bytes, bytes.length);
|
||||
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length);
|
||||
// Assert that the subtitle is empty.
|
||||
assertEquals(0, subtitle.getEventTimeCount());
|
||||
assertTrue(subtitle.getCues(0).isEmpty());
|
||||
}
|
||||
|
||||
public void testParseTypical() throws IOException {
|
||||
SubripParser parser = new SubripParser();
|
||||
public void testDecodeTypical() throws IOException {
|
||||
SubripDecoder decoder = new SubripDecoder();
|
||||
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_FILE);
|
||||
SubripSubtitle subtitle = parser.decode(bytes, bytes.length);
|
||||
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length);
|
||||
assertEquals(6, subtitle.getEventTimeCount());
|
||||
assertTypicalCue1(subtitle, 0);
|
||||
assertTypicalCue2(subtitle, 2);
|
||||
assertTypicalCue3(subtitle, 4);
|
||||
}
|
||||
|
||||
public void testParseTypicalWithByteOrderMark() throws IOException {
|
||||
SubripParser parser = new SubripParser();
|
||||
public void testDecodeTypicalWithByteOrderMark() throws IOException {
|
||||
SubripDecoder decoder = new SubripDecoder();
|
||||
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_WITH_BYTE_ORDER_MARK);
|
||||
SubripSubtitle subtitle = parser.decode(bytes, bytes.length);
|
||||
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length);
|
||||
assertEquals(6, subtitle.getEventTimeCount());
|
||||
assertTypicalCue1(subtitle, 0);
|
||||
assertTypicalCue2(subtitle, 2);
|
||||
assertTypicalCue3(subtitle, 4);
|
||||
}
|
||||
|
||||
public void testParseTypicalExtraBlankLine() throws IOException {
|
||||
SubripParser parser = new SubripParser();
|
||||
public void testDecodeTypicalExtraBlankLine() throws IOException {
|
||||
SubripDecoder decoder = new SubripDecoder();
|
||||
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_EXTRA_BLANK_LINE);
|
||||
SubripSubtitle subtitle = parser.decode(bytes, bytes.length);
|
||||
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length);
|
||||
assertEquals(6, subtitle.getEventTimeCount());
|
||||
assertTypicalCue1(subtitle, 0);
|
||||
assertTypicalCue2(subtitle, 2);
|
||||
assertTypicalCue3(subtitle, 4);
|
||||
}
|
||||
|
||||
public void testParseTypicalMissingTimecode() throws IOException {
|
||||
public void testDecodeTypicalMissingTimecode() throws IOException {
|
||||
// Parsing should succeed, parsing the first and third cues only.
|
||||
SubripParser parser = new SubripParser();
|
||||
SubripDecoder decoder = new SubripDecoder();
|
||||
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_MISSING_TIMECODE);
|
||||
SubripSubtitle subtitle = parser.decode(bytes, bytes.length);
|
||||
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length);
|
||||
assertEquals(4, subtitle.getEventTimeCount());
|
||||
assertTypicalCue1(subtitle, 0);
|
||||
assertTypicalCue3(subtitle, 2);
|
||||
}
|
||||
|
||||
public void testParseTypicalMissingSequence() throws IOException {
|
||||
public void testDecodeTypicalMissingSequence() throws IOException {
|
||||
// Parsing should succeed, parsing the first and third cues only.
|
||||
SubripParser parser = new SubripParser();
|
||||
SubripDecoder decoder = new SubripDecoder();
|
||||
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_MISSING_SEQUENCE);
|
||||
SubripSubtitle subtitle = parser.decode(bytes, bytes.length);
|
||||
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length);
|
||||
assertEquals(4, subtitle.getEventTimeCount());
|
||||
assertTypicalCue1(subtitle, 0);
|
||||
assertTypicalCue3(subtitle, 2);
|
||||
}
|
||||
|
||||
public void testParseNoEndTimecodes() throws IOException {
|
||||
SubripParser parser = new SubripParser();
|
||||
public void testDecodeNoEndTimecodes() throws IOException {
|
||||
SubripDecoder decoder = new SubripDecoder();
|
||||
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), NO_END_TIMECODES_FILE);
|
||||
SubripSubtitle subtitle = parser.decode(bytes, bytes.length);
|
||||
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length);
|
||||
|
||||
// Test event count.
|
||||
assertEquals(3, subtitle.getEventTimeCount());
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer2.text.ttml;
|
||||
|
||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||
import com.google.android.exoplayer2.text.Cue;
|
||||
import com.google.android.exoplayer2.text.TextDecoderException;
|
||||
import com.google.android.exoplayer2.util.ColorParser;
|
||||
|
||||
import android.test.InstrumentationTestCase;
|
||||
@ -38,9 +39,9 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Unit test for {@link TtmlParser}.
|
||||
* Unit test for {@link TtmlDecoder}.
|
||||
*/
|
||||
public final class TtmlParserTest extends InstrumentationTestCase {
|
||||
public final class TtmlDecoderTest extends InstrumentationTestCase {
|
||||
|
||||
private static final String INLINE_ATTRIBUTES_TTML_FILE = "ttml/inline_style_attributes.xml";
|
||||
private static final String INHERIT_STYLE_TTML_FILE = "ttml/inherit_style.xml";
|
||||
@ -60,7 +61,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||
private static final String FONT_SIZE_EMPTY_TTML_FILE = "ttml/font_size_empty.xml";
|
||||
private static final String FRAME_RATE_TTML_FILE = "ttml/frame_rate.xml";
|
||||
|
||||
public void testInlineAttributes() throws IOException {
|
||||
public void testInlineAttributes() throws IOException, TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(INLINE_ATTRIBUTES_TTML_FILE);
|
||||
assertEquals(4, subtitle.getEventTimeCount());
|
||||
|
||||
@ -76,7 +77,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||
assertTrue(firstPStyle.isUnderline());
|
||||
}
|
||||
|
||||
public void testInheritInlineAttributes() throws IOException {
|
||||
public void testInheritInlineAttributes() throws IOException, TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(INLINE_ATTRIBUTES_TTML_FILE);
|
||||
assertEquals(4, subtitle.getEventTimeCount());
|
||||
assertSpans(subtitle, 20, "text 2", "sansSerif", TtmlStyle.STYLE_ITALIC,
|
||||
@ -94,21 +95,22 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||
* Kitkat Color</a>
|
||||
* @throws IOException thrown if reading subtitle file fails.
|
||||
*/
|
||||
public void testLime() throws IOException {
|
||||
public void testLime() throws IOException, TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(INLINE_ATTRIBUTES_TTML_FILE);
|
||||
assertEquals(4, subtitle.getEventTimeCount());
|
||||
assertSpans(subtitle, 20, "text 2", "sansSerif", TtmlStyle.STYLE_ITALIC, 0xFF00FFFF, 0xFF00FF00,
|
||||
false, true, null);
|
||||
}
|
||||
|
||||
public void testInheritGlobalStyle() throws IOException {
|
||||
public void testInheritGlobalStyle() throws IOException, TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(INHERIT_STYLE_TTML_FILE);
|
||||
assertEquals(2, subtitle.getEventTimeCount());
|
||||
assertSpans(subtitle, 10, "text 1", "serif", TtmlStyle.STYLE_BOLD_ITALIC, 0xFF0000FF,
|
||||
0xFFFFFF00, true, false, null);
|
||||
}
|
||||
|
||||
public void testInheritGlobalStyleOverriddenByInlineAttributes() throws IOException {
|
||||
public void testInheritGlobalStyleOverriddenByInlineAttributes() throws IOException,
|
||||
TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(INHERIT_STYLE_OVERRIDE_TTML_FILE);
|
||||
assertEquals(4, subtitle.getEventTimeCount());
|
||||
|
||||
@ -118,7 +120,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||
true, false, null);
|
||||
}
|
||||
|
||||
public void testInheritGlobalAndParent() throws IOException {
|
||||
public void testInheritGlobalAndParent() throws IOException, TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(INHERIT_GLOBAL_AND_PARENT_TTML_FILE);
|
||||
assertEquals(4, subtitle.getEventTimeCount());
|
||||
|
||||
@ -128,28 +130,29 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||
0xFFFFFF00, true, true, Layout.Alignment.ALIGN_CENTER);
|
||||
}
|
||||
|
||||
public void testInheritMultipleStyles() throws IOException {
|
||||
public void testInheritMultipleStyles() throws IOException, TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(INHERIT_MULTIPLE_STYLES_TTML_FILE);
|
||||
assertEquals(12, subtitle.getEventTimeCount());
|
||||
assertSpans(subtitle, 10, "text 1", "sansSerif", TtmlStyle.STYLE_BOLD_ITALIC, 0xFF0000FF,
|
||||
0xFFFFFF00, false, true, null);
|
||||
}
|
||||
|
||||
public void testInheritMultipleStylesWithoutLocalAttributes() throws IOException {
|
||||
public void testInheritMultipleStylesWithoutLocalAttributes() throws IOException,
|
||||
TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(INHERIT_MULTIPLE_STYLES_TTML_FILE);
|
||||
assertEquals(12, subtitle.getEventTimeCount());
|
||||
assertSpans(subtitle, 20, "text 2", "sansSerif", TtmlStyle.STYLE_BOLD_ITALIC, 0xFF0000FF,
|
||||
0xFF000000, false, true, null);
|
||||
}
|
||||
|
||||
public void testMergeMultipleStylesWithParentStyle() throws IOException {
|
||||
public void testMergeMultipleStylesWithParentStyle() throws IOException, TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(INHERIT_MULTIPLE_STYLES_TTML_FILE);
|
||||
assertEquals(12, subtitle.getEventTimeCount());
|
||||
assertSpans(subtitle, 30, "text 2.5", "sansSerifInline", TtmlStyle.STYLE_ITALIC, 0xFFFF0000,
|
||||
0xFFFFFF00, true, true, null);
|
||||
}
|
||||
|
||||
public void testMultipleRegions() throws IOException {
|
||||
public void testMultipleRegions() throws IOException, TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(MULTIPLE_REGIONS_TTML_FILE);
|
||||
List<Cue> output = subtitle.getCues(1000000);
|
||||
assertEquals(2, output.size());
|
||||
@ -199,7 +202,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||
assertEquals(45.f / 100.f, ttmlCue.line);
|
||||
}
|
||||
|
||||
public void testEmptyStyleAttribute() throws IOException {
|
||||
public void testEmptyStyleAttribute() throws IOException, TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(INHERIT_MULTIPLE_STYLES_TTML_FILE);
|
||||
assertEquals(12, subtitle.getEventTimeCount());
|
||||
|
||||
@ -210,7 +213,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||
assertNull(queryChildrenForTag(fourthDiv, TtmlNode.TAG_P, 0).getStyleIds());
|
||||
}
|
||||
|
||||
public void testNonexistingStyleId() throws IOException {
|
||||
public void testNonexistingStyleId() throws IOException, TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(INHERIT_MULTIPLE_STYLES_TTML_FILE);
|
||||
assertEquals(12, subtitle.getEventTimeCount());
|
||||
|
||||
@ -221,7 +224,8 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||
assertEquals(1, queryChildrenForTag(fifthDiv, TtmlNode.TAG_P, 0).getStyleIds().length);
|
||||
}
|
||||
|
||||
public void testNonExistingAndExistingStyleIdWithRedundantSpaces() throws IOException {
|
||||
public void testNonExistingAndExistingStyleIdWithRedundantSpaces() throws IOException,
|
||||
TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(INHERIT_MULTIPLE_STYLES_TTML_FILE);
|
||||
assertEquals(12, subtitle.getEventTimeCount());
|
||||
|
||||
@ -233,7 +237,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||
assertEquals(2, styleIds.length);
|
||||
}
|
||||
|
||||
public void testMultipleChaining() throws IOException {
|
||||
public void testMultipleChaining() throws IOException, TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(CHAIN_MULTIPLE_STYLES_TTML_FILE);
|
||||
assertEquals(2, subtitle.getEventTimeCount());
|
||||
|
||||
@ -255,7 +259,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||
assertTrue(style.isLinethrough());
|
||||
}
|
||||
|
||||
public void testNoUnderline() throws IOException {
|
||||
public void testNoUnderline() throws IOException, TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(NO_UNDERLINE_LINETHROUGH_TTML_FILE);
|
||||
assertEquals(4, subtitle.getEventTimeCount());
|
||||
|
||||
@ -267,7 +271,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||
assertFalse("noUnderline from inline attribute expected", style.isUnderline());
|
||||
}
|
||||
|
||||
public void testNoLinethrough() throws IOException {
|
||||
public void testNoLinethrough() throws IOException, TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(NO_UNDERLINE_LINETHROUGH_TTML_FILE);
|
||||
assertEquals(4, subtitle.getEventTimeCount());
|
||||
|
||||
@ -280,7 +284,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||
style.isLinethrough());
|
||||
}
|
||||
|
||||
public void testFontSizeSpans() throws IOException {
|
||||
public void testFontSizeSpans() throws IOException, TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(FONT_SIZE_TTML_FILE);
|
||||
assertEquals(10, subtitle.getEventTimeCount());
|
||||
|
||||
@ -315,7 +319,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||
assertRelativeFontSize(spannable, 0.5f);
|
||||
}
|
||||
|
||||
public void testFontSizeWithMissingUnitIsIgnored() throws IOException {
|
||||
public void testFontSizeWithMissingUnitIsIgnored() throws IOException, TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(FONT_SIZE_MISSING_UNIT_TTML_FILE);
|
||||
assertEquals(2, subtitle.getEventTimeCount());
|
||||
List<Cue> cues = subtitle.getCues(10 * 1000000);
|
||||
@ -326,7 +330,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||
assertEquals(0, spannable.getSpans(0, spannable.length(), AbsoluteSizeSpan.class).length);
|
||||
}
|
||||
|
||||
public void testFontSizeWithInvalidValueIsIgnored() throws IOException {
|
||||
public void testFontSizeWithInvalidValueIsIgnored() throws IOException, TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(FONT_SIZE_INVALID_TTML_FILE);
|
||||
assertEquals(6, subtitle.getEventTimeCount());
|
||||
|
||||
@ -354,7 +358,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||
assertEquals(0, spannable.getSpans(0, spannable.length(), AbsoluteSizeSpan.class).length);
|
||||
}
|
||||
|
||||
public void testFontSizeWithEmptyValueIsIgnored() throws IOException {
|
||||
public void testFontSizeWithEmptyValueIsIgnored() throws IOException, TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(FONT_SIZE_EMPTY_TTML_FILE);
|
||||
assertEquals(2, subtitle.getEventTimeCount());
|
||||
List<Cue> cues = subtitle.getCues(10 * 1000000);
|
||||
@ -365,7 +369,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||
assertEquals(0, spannable.getSpans(0, spannable.length(), AbsoluteSizeSpan.class).length);
|
||||
}
|
||||
|
||||
public void testFrameRate() throws IOException {
|
||||
public void testFrameRate() throws IOException, TextDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(FRAME_RATE_TTML_FILE);
|
||||
assertEquals(4, subtitle.getEventTimeCount());
|
||||
assertEquals(1_000_000, subtitle.getEventTime(0));
|
||||
@ -476,9 +480,10 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||
throw new IllegalStateException("tag not found");
|
||||
}
|
||||
|
||||
private TtmlSubtitle getSubtitle(String file) throws IOException {
|
||||
TtmlParser ttmlParser = new TtmlParser();
|
||||
private TtmlSubtitle getSubtitle(String file) throws IOException, TextDecoderException {
|
||||
TtmlDecoder ttmlDecoder = new TtmlDecoder();
|
||||
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), file);
|
||||
return ttmlParser.decode(bytes, bytes.length);
|
||||
return ttmlDecoder.decode(bytes, bytes.length);
|
||||
}
|
||||
|
||||
}
|
@ -15,20 +15,18 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.text.webvtt;
|
||||
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.text.Cue;
|
||||
import com.google.android.exoplayer2.text.Subtitle;
|
||||
import com.google.android.exoplayer2.text.TextDecoderException;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Unit test for {@link Mp4WebvttParser}.
|
||||
* As a side effect, it also involves the {@link Mp4WebvttSubtitle}.
|
||||
* Unit test for {@link Mp4WebvttDecoder}.
|
||||
*/
|
||||
public final class Mp4WebvttParserTest extends TestCase {
|
||||
public final class Mp4WebvttDecoderTest extends TestCase {
|
||||
|
||||
private static final byte[] SINGLE_CUE_SAMPLE = {
|
||||
0x00, 0x00, 0x00, 0x1C, // Size
|
||||
@ -81,42 +79,39 @@ public final class Mp4WebvttParserTest extends TestCase {
|
||||
0x76, 0x74, 0x74
|
||||
};
|
||||
|
||||
private Mp4WebvttParser parser;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
parser = new Mp4WebvttParser();
|
||||
}
|
||||
|
||||
// Positive tests.
|
||||
|
||||
public void testSingleCueSample() throws ParserException {
|
||||
Subtitle result = parser.decode(SINGLE_CUE_SAMPLE, SINGLE_CUE_SAMPLE.length);
|
||||
Cue expectedCue = new Cue("Hello World"); // Line feed must be trimmed by the parser
|
||||
public void testSingleCueSample() throws TextDecoderException {
|
||||
Mp4WebvttDecoder decoder = new Mp4WebvttDecoder();
|
||||
Subtitle result = decoder.decode(SINGLE_CUE_SAMPLE, SINGLE_CUE_SAMPLE.length);
|
||||
Cue expectedCue = new Cue("Hello World"); // Line feed must be trimmed by the decoder
|
||||
assertMp4WebvttSubtitleEquals(result, expectedCue);
|
||||
}
|
||||
|
||||
public void testTwoCuesSample() throws ParserException {
|
||||
Subtitle result = parser.decode(DOUBLE_CUE_SAMPLE, DOUBLE_CUE_SAMPLE.length);
|
||||
public void testTwoCuesSample() throws TextDecoderException {
|
||||
Mp4WebvttDecoder decoder = new Mp4WebvttDecoder();
|
||||
Subtitle result = decoder.decode(DOUBLE_CUE_SAMPLE, DOUBLE_CUE_SAMPLE.length);
|
||||
Cue firstExpectedCue = new Cue("Hello World");
|
||||
Cue secondExpectedCue = new Cue("Bye Bye");
|
||||
assertMp4WebvttSubtitleEquals(result, firstExpectedCue, secondExpectedCue);
|
||||
}
|
||||
|
||||
public void testNoCueSample() throws IOException {
|
||||
Subtitle result = parser.decode(NO_CUE_SAMPLE, NO_CUE_SAMPLE.length);
|
||||
public void testNoCueSample() throws TextDecoderException {
|
||||
Mp4WebvttDecoder decoder = new Mp4WebvttDecoder();
|
||||
Subtitle result = decoder.decode(NO_CUE_SAMPLE, NO_CUE_SAMPLE.length);
|
||||
assertMp4WebvttSubtitleEquals(result, new Cue[0]);
|
||||
}
|
||||
|
||||
// Negative tests.
|
||||
|
||||
public void testSampleWithIncompleteHeader() {
|
||||
Mp4WebvttDecoder decoder = new Mp4WebvttDecoder();
|
||||
try {
|
||||
parser.decode(INCOMPLETE_HEADER_SAMPLE, INCOMPLETE_HEADER_SAMPLE.length);
|
||||
} catch (ParserException e) {
|
||||
decoder.decode(INCOMPLETE_HEADER_SAMPLE, INCOMPLETE_HEADER_SAMPLE.length);
|
||||
} catch (TextDecoderException e) {
|
||||
return;
|
||||
}
|
||||
fail("The parser should have failed, no payload was included in the VTTCue.");
|
||||
fail();
|
||||
}
|
||||
|
||||
// Util methods
|
@ -15,9 +15,9 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.text.webvtt;
|
||||
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||
import com.google.android.exoplayer2.text.Cue;
|
||||
import com.google.android.exoplayer2.text.TextDecoderException;
|
||||
|
||||
import android.graphics.Typeface;
|
||||
import android.test.InstrumentationTestCase;
|
||||
@ -33,9 +33,9 @@ import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Unit test for {@link WebvttParser}.
|
||||
* Unit test for {@link WebvttDecoder}.
|
||||
*/
|
||||
public class WebvttParserTest extends InstrumentationTestCase {
|
||||
public class WebvttDecoderTest extends InstrumentationTestCase {
|
||||
|
||||
private static final String TYPICAL_FILE = "webvtt/typical";
|
||||
private static final String TYPICAL_WITH_IDS_FILE = "webvtt/typical_with_identifiers";
|
||||
@ -47,18 +47,18 @@ public class WebvttParserTest extends InstrumentationTestCase {
|
||||
private static final String WITH_CSS_COMPLEX_SELECTORS = "webvtt/with_css_complex_selectors";
|
||||
private static final String EMPTY_FILE = "webvtt/empty";
|
||||
|
||||
public void testParseEmpty() throws IOException {
|
||||
WebvttParser parser = new WebvttParser();
|
||||
public void testDecodeEmpty() throws IOException {
|
||||
WebvttDecoder decoder = new WebvttDecoder();
|
||||
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), EMPTY_FILE);
|
||||
try {
|
||||
parser.decode(bytes, bytes.length);
|
||||
fail("Expected ParserException");
|
||||
} catch (ParserException expected) {
|
||||
decoder.decode(bytes, bytes.length);
|
||||
fail();
|
||||
} catch (TextDecoderException expected) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
public void testParseTypical() throws IOException {
|
||||
public void testDecodeTypical() throws IOException, TextDecoderException {
|
||||
WebvttSubtitle subtitle = getSubtitleForTestAsset(TYPICAL_FILE);
|
||||
|
||||
// Test event count.
|
||||
@ -69,7 +69,7 @@ public class WebvttParserTest extends InstrumentationTestCase {
|
||||
assertCue(subtitle, 2, 2345000, 3456000, "This is the second subtitle.");
|
||||
}
|
||||
|
||||
public void testParseTypicalWithIds() throws IOException {
|
||||
public void testDecodeTypicalWithIds() throws IOException, TextDecoderException {
|
||||
WebvttSubtitle subtitle = getSubtitleForTestAsset(TYPICAL_WITH_IDS_FILE);
|
||||
|
||||
// Test event count.
|
||||
@ -80,7 +80,7 @@ public class WebvttParserTest extends InstrumentationTestCase {
|
||||
assertCue(subtitle, 2, 2345000, 3456000, "This is the second subtitle.");
|
||||
}
|
||||
|
||||
public void testParseTypicalWithComments() throws IOException {
|
||||
public void testDecodeTypicalWithComments() throws IOException, TextDecoderException {
|
||||
WebvttSubtitle subtitle = getSubtitleForTestAsset(TYPICAL_WITH_COMMENTS_FILE);
|
||||
|
||||
// test event count
|
||||
@ -91,7 +91,7 @@ public class WebvttParserTest extends InstrumentationTestCase {
|
||||
assertCue(subtitle, 2, 2345000, 3456000, "This is the second subtitle.");
|
||||
}
|
||||
|
||||
public void testParseWithTags() throws IOException {
|
||||
public void testDecodeWithTags() throws IOException, TextDecoderException {
|
||||
WebvttSubtitle subtitle = getSubtitleForTestAsset(WITH_TAGS_FILE);
|
||||
|
||||
// Test event count.
|
||||
@ -104,7 +104,7 @@ public class WebvttParserTest extends InstrumentationTestCase {
|
||||
assertCue(subtitle, 6, 6000000, 7000000, "This is the <fourth> &subtitle.");
|
||||
}
|
||||
|
||||
public void testParseWithPositioning() throws IOException {
|
||||
public void testDecodeWithPositioning() throws IOException, TextDecoderException {
|
||||
WebvttSubtitle subtitle = getSubtitleForTestAsset(WITH_POSITIONING_FILE);
|
||||
// Test event count.
|
||||
assertEquals(12, subtitle.getEventTimeCount());
|
||||
@ -128,7 +128,7 @@ public class WebvttParserTest extends InstrumentationTestCase {
|
||||
Cue.TYPE_UNSET, 0.35f);
|
||||
}
|
||||
|
||||
public void testParseWithBadCueHeader() throws IOException {
|
||||
public void testDecodeWithBadCueHeader() throws IOException, TextDecoderException {
|
||||
WebvttSubtitle subtitle = getSubtitleForTestAsset(WITH_BAD_CUE_HEADER_FILE);
|
||||
|
||||
// Test event count.
|
||||
@ -139,7 +139,7 @@ public class WebvttParserTest extends InstrumentationTestCase {
|
||||
assertCue(subtitle, 2, 4000000, 5000000, "This is the third subtitle.");
|
||||
}
|
||||
|
||||
public void testWebvttWithCssStyle() throws IOException {
|
||||
public void testWebvttWithCssStyle() throws IOException, TextDecoderException {
|
||||
WebvttSubtitle subtitle = getSubtitleForTestAsset(WITH_CSS_STYLES);
|
||||
|
||||
// Test event count.
|
||||
@ -162,7 +162,7 @@ public class WebvttParserTest extends InstrumentationTestCase {
|
||||
assertEquals(Typeface.BOLD, s4.getSpans(17, s4.length(), StyleSpan.class)[0].getStyle());
|
||||
}
|
||||
|
||||
public void testWithComplexCssSelectors() throws IOException {
|
||||
public void testWithComplexCssSelectors() throws IOException, TextDecoderException {
|
||||
WebvttSubtitle subtitle = getSubtitleForTestAsset(WITH_CSS_COMPLEX_SELECTORS);
|
||||
Spanned text = getUniqueSpanTextAt(subtitle, 0);
|
||||
assertEquals(1, text.getSpans(30, text.length(), ForegroundColorSpan.class).length);
|
||||
@ -192,10 +192,11 @@ public class WebvttParserTest extends InstrumentationTestCase {
|
||||
assertEquals(Typeface.ITALIC, text.getSpans(19, text.length(), StyleSpan.class)[0].getStyle());
|
||||
}
|
||||
|
||||
private WebvttSubtitle getSubtitleForTestAsset(String asset) throws IOException {
|
||||
WebvttParser parser = new WebvttParser();
|
||||
private WebvttSubtitle getSubtitleForTestAsset(String asset) throws IOException,
|
||||
TextDecoderException {
|
||||
WebvttDecoder decoder = new WebvttDecoder();
|
||||
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), asset);
|
||||
return parser.decode(bytes, bytes.length);
|
||||
return decoder.decode(bytes, bytes.length);
|
||||
}
|
||||
|
||||
private Spanned getUniqueSpanTextAt(WebvttSubtitle sub, long timeUs) {
|
@ -39,15 +39,16 @@ import com.google.android.exoplayer2.source.MediaSource;
|
||||
* is created. Hence {@link ExoPlayer} is capable of loading and playing any media for which a
|
||||
* {@link Renderer} implementation can be provided.
|
||||
*
|
||||
* <p>{@link MediaCodecAudioRenderer} and {@link MediaCodecVideoRenderer} can be used for
|
||||
* the common cases of rendering audio and video. These components in turn require an
|
||||
* <i>upstream</i> {@link MediaPeriod} to be injected through their constructors, where upstream
|
||||
* is defined to denote a component that is closer to the source of the media. This pattern of
|
||||
* upstream dependency injection is actively encouraged, since it means that the functionality of
|
||||
* the player is built up through the composition of components that can easily be exchanged for
|
||||
* alternate implementations. For example a {@link MediaPeriod} implementation may require a
|
||||
* further upstream data loading component to be injected through its constructor, with different
|
||||
* implementations enabling the loading of data from various sources.
|
||||
* <p>{@link com.google.android.exoplayer2.audio.MediaCodecAudioRenderer} and
|
||||
* {@link com.google.android.exoplayer2.video.MediaCodecVideoRenderer} can be used for the common
|
||||
* cases of rendering audio and video. These components in turn require an <i>upstream</i>
|
||||
* {@link MediaPeriod} to be injected through their constructors, where upstream is defined to
|
||||
* denote a component that is closer to the source of the media. This pattern of upstream dependency
|
||||
* injection is actively encouraged, since it means that the functionality of the player is built up
|
||||
* through the composition of components that can easily be exchanged for alternate implementations.
|
||||
* For example a {@link MediaPeriod} implementation may require a further upstream data loading
|
||||
* component to be injected through its constructor, with different implementations enabling the
|
||||
* loading of data from various sources.
|
||||
*
|
||||
* <a name="Threading"></a>
|
||||
* <h3>Threading model</h3>
|
||||
|
@ -24,7 +24,9 @@ import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelector;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelector.InvalidationListener;
|
||||
import com.google.android.exoplayer2.util.MediaClock;
|
||||
import com.google.android.exoplayer2.util.PriorityHandlerThread;
|
||||
import com.google.android.exoplayer2.util.StandaloneMediaClock;
|
||||
import com.google.android.exoplayer2.util.TraceUtil;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
|
@ -18,7 +18,7 @@ package com.google.android.exoplayer2;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Thrown when an error occurs parsing media data.
|
||||
* Thrown when an error occurs parsing loaded data.
|
||||
*/
|
||||
public class ParserException extends IOException {
|
||||
|
||||
|
@ -16,8 +16,10 @@
|
||||
package com.google.android.exoplayer2;
|
||||
|
||||
import com.google.android.exoplayer2.ExoPlayer.ExoPlayerComponent;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.source.SampleStream;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.MediaClock;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -16,16 +16,21 @@
|
||||
package com.google.android.exoplayer2;
|
||||
|
||||
import com.google.android.exoplayer2.audio.AudioCapabilities;
|
||||
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
|
||||
import com.google.android.exoplayer2.audio.AudioTrack;
|
||||
import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer;
|
||||
import com.google.android.exoplayer2.decoder.DecoderCounters;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
|
||||
import com.google.android.exoplayer2.metadata.MetadataRenderer;
|
||||
import com.google.android.exoplayer2.metadata.id3.Id3Decoder;
|
||||
import com.google.android.exoplayer2.metadata.id3.Id3Frame;
|
||||
import com.google.android.exoplayer2.metadata.id3.Id3Parser;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.text.Cue;
|
||||
import com.google.android.exoplayer2.text.TextRenderer;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelector;
|
||||
|
||||
import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
|
||||
import com.google.android.exoplayer2.video.VideoRendererEventListener;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
@ -60,17 +65,17 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
||||
* A listener for debugging information.
|
||||
*/
|
||||
public interface DebugListener {
|
||||
void onAudioEnabled(CodecCounters counters);
|
||||
void onAudioEnabled(DecoderCounters counters);
|
||||
void onAudioSessionId(int audioSessionId);
|
||||
void onAudioDecoderInitialized(String decoderName, long elapsedRealtimeMs,
|
||||
long initializationDurationMs);
|
||||
void onAudioFormatChanged(Format format);
|
||||
void onAudioDisabled(CodecCounters counters);
|
||||
void onVideoEnabled(CodecCounters counters);
|
||||
void onAudioDisabled(DecoderCounters counters);
|
||||
void onVideoEnabled(DecoderCounters counters);
|
||||
void onVideoDecoderInitialized(String decoderName, long elapsedRealtimeMs,
|
||||
long initializationDurationMs);
|
||||
void onVideoFormatChanged(Format format);
|
||||
void onVideoDisabled(CodecCounters counters);
|
||||
void onVideoDisabled(DecoderCounters counters);
|
||||
void onDroppedFrames(int count, long elapsed);
|
||||
void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs);
|
||||
}
|
||||
@ -106,8 +111,8 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
||||
private Id3MetadataListener id3MetadataListener;
|
||||
private VideoListener videoListener;
|
||||
private DebugListener debugListener;
|
||||
private CodecCounters videoCodecCounters;
|
||||
private CodecCounters audioCodecCounters;
|
||||
private DecoderCounters videoDecoderCounters;
|
||||
private DecoderCounters audioDecoderCounters;
|
||||
private int audioSessionId;
|
||||
|
||||
/* package */ SimpleExoPlayer(Context context, TrackSelector trackSelector,
|
||||
@ -246,19 +251,19 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The {@link CodecCounters} for video, or null if there is no video component to the
|
||||
* @return The {@link DecoderCounters} for video, or null if there is no video component to the
|
||||
* current media.
|
||||
*/
|
||||
public CodecCounters getVideoCodecCounters() {
|
||||
return videoCodecCounters;
|
||||
public DecoderCounters getVideoDecoderCounters() {
|
||||
return videoDecoderCounters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The {@link CodecCounters} for audio, or null if there is no audio component to the
|
||||
* @return The {@link DecoderCounters} for audio, or null if there is no audio component to the
|
||||
* current media.
|
||||
*/
|
||||
public CodecCounters getAudioCodecCounters() {
|
||||
return audioCodecCounters;
|
||||
public DecoderCounters getAudioDecoderCounters() {
|
||||
return audioDecoderCounters;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -413,7 +418,7 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
||||
renderersList.add(textRenderer);
|
||||
|
||||
MetadataRenderer<List<Id3Frame>> id3Renderer = new MetadataRenderer<>(componentListener,
|
||||
mainHandler.getLooper(), new Id3Parser());
|
||||
mainHandler.getLooper(), new Id3Decoder());
|
||||
renderersList.add(id3Renderer);
|
||||
}
|
||||
|
||||
@ -482,8 +487,8 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
||||
// VideoRendererEventListener implementation
|
||||
|
||||
@Override
|
||||
public void onVideoEnabled(CodecCounters counters) {
|
||||
videoCodecCounters = counters;
|
||||
public void onVideoEnabled(DecoderCounters counters) {
|
||||
videoDecoderCounters = counters;
|
||||
if (debugListener != null) {
|
||||
debugListener.onVideoEnabled(counters);
|
||||
}
|
||||
@ -530,19 +535,19 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoDisabled(CodecCounters counters) {
|
||||
public void onVideoDisabled(DecoderCounters counters) {
|
||||
if (debugListener != null) {
|
||||
debugListener.onVideoDisabled(counters);
|
||||
}
|
||||
videoFormat = null;
|
||||
videoCodecCounters = null;
|
||||
videoDecoderCounters = null;
|
||||
}
|
||||
|
||||
// AudioRendererEventListener implementation
|
||||
|
||||
@Override
|
||||
public void onAudioEnabled(CodecCounters counters) {
|
||||
audioCodecCounters = counters;
|
||||
public void onAudioEnabled(DecoderCounters counters) {
|
||||
audioDecoderCounters = counters;
|
||||
if (debugListener != null) {
|
||||
debugListener.onAudioEnabled(counters);
|
||||
}
|
||||
@ -582,12 +587,12 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioDisabled(CodecCounters counters) {
|
||||
public void onAudioDisabled(DecoderCounters counters) {
|
||||
if (debugListener != null) {
|
||||
debugListener.onAudioDisabled(counters);
|
||||
}
|
||||
audioFormat = null;
|
||||
audioCodecCounters = null;
|
||||
audioDecoderCounters = null;
|
||||
audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
|
||||
}
|
||||
|
||||
|
@ -13,10 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.util;
|
||||
package com.google.android.exoplayer2.audio;
|
||||
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.drm.DrmInitData;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.ParsableBitArray;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -13,12 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.extensions;
|
||||
package com.google.android.exoplayer2.audio;
|
||||
|
||||
/**
|
||||
* Thrown when a decoder error occurs.
|
||||
* Thrown when an audio decoder error occurs.
|
||||
*/
|
||||
public class AudioDecoderException extends Exception {
|
||||
public abstract class AudioDecoderException extends Exception {
|
||||
|
||||
public AudioDecoderException(String detailMessage) {
|
||||
super(detailMessage);
|
@ -13,9 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
package com.google.android.exoplayer2.audio;
|
||||
|
||||
import com.google.android.exoplayer2.audio.AudioTrack;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.Renderer;
|
||||
import com.google.android.exoplayer2.decoder.DecoderCounters;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
|
||||
import android.os.Handler;
|
||||
@ -29,10 +31,10 @@ public interface AudioRendererEventListener {
|
||||
/**
|
||||
* Invoked when the renderer is enabled.
|
||||
*
|
||||
* @param counters {@link CodecCounters} that will be updated by the renderer for as long as it
|
||||
* @param counters {@link DecoderCounters} that will be updated by the renderer for as long as it
|
||||
* remains enabled.
|
||||
*/
|
||||
void onAudioEnabled(CodecCounters counters);
|
||||
void onAudioEnabled(DecoderCounters counters);
|
||||
|
||||
/**
|
||||
* Invoked when the audio session is set.
|
||||
@ -73,9 +75,9 @@ public interface AudioRendererEventListener {
|
||||
/**
|
||||
* Invoked when the renderer is disabled.
|
||||
*
|
||||
* @param counters {@link CodecCounters} that were updated by the renderer.
|
||||
* @param counters {@link DecoderCounters} that were updated by the renderer.
|
||||
*/
|
||||
void onAudioDisabled(CodecCounters counters);
|
||||
void onAudioDisabled(DecoderCounters counters);
|
||||
|
||||
/**
|
||||
* Dispatches events to a {@link AudioRendererEventListener}.
|
||||
@ -90,12 +92,12 @@ public interface AudioRendererEventListener {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void enabled(final CodecCounters codecCounters) {
|
||||
public void enabled(final DecoderCounters decoderCounters) {
|
||||
if (listener != null) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onAudioEnabled(codecCounters);
|
||||
listener.onAudioEnabled(decoderCounters);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -137,7 +139,7 @@ public interface AudioRendererEventListener {
|
||||
}
|
||||
}
|
||||
|
||||
public void disabled(final CodecCounters counters) {
|
||||
public void disabled(final DecoderCounters counters) {
|
||||
if (listener != null) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
@ -16,9 +16,7 @@
|
||||
package com.google.android.exoplayer2.audio;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.util.Ac3Util;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.DtsUtil;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
|
@ -13,10 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.util;
|
||||
package com.google.android.exoplayer2.audio;
|
||||
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.drm.DrmInitData;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.ParsableBitArray;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -13,13 +13,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
package com.google.android.exoplayer2.audio;
|
||||
|
||||
import com.google.android.exoplayer2.AudioRendererEventListener.EventDispatcher;
|
||||
import com.google.android.exoplayer2.MediaCodecUtil.DecoderQueryException;
|
||||
import com.google.android.exoplayer2.audio.AudioCapabilities;
|
||||
import com.google.android.exoplayer2.audio.AudioTrack;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.Renderer;
|
||||
import com.google.android.exoplayer2.audio.AudioRendererEventListener.EventDispatcher;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException;
|
||||
import com.google.android.exoplayer2.util.MediaClock;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
@ -148,7 +154,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
if (allowPassthrough(mimeType) && mediaCodecSelector.getPassthroughDecoderInfo() != null) {
|
||||
return ADAPTIVE_NOT_SEAMLESS | FORMAT_HANDLED;
|
||||
}
|
||||
MediaCodecDecoderInfo decoderInfo = mediaCodecSelector.getDecoderInfo(mimeType,
|
||||
MediaCodecInfo decoderInfo = mediaCodecSelector.getDecoderInfo(mimeType,
|
||||
format.requiresSecureDecryption);
|
||||
if (decoderInfo == null) {
|
||||
return FORMAT_UNSUPPORTED_SUBTYPE;
|
||||
@ -164,10 +170,10 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MediaCodecDecoderInfo getDecoderInfo(MediaCodecSelector mediaCodecSelector,
|
||||
protected MediaCodecInfo getDecoderInfo(MediaCodecSelector mediaCodecSelector,
|
||||
Format format, boolean requiresSecureDecoder) throws DecoderQueryException {
|
||||
if (allowPassthrough(format.sampleMimeType)) {
|
||||
MediaCodecDecoderInfo passthroughDecoderInfo = mediaCodecSelector.getPassthroughDecoderInfo();
|
||||
MediaCodecInfo passthroughDecoderInfo = mediaCodecSelector.getPassthroughDecoderInfo();
|
||||
if (passthroughDecoderInfo != null) {
|
||||
passthroughEnabled = true;
|
||||
return passthroughDecoderInfo;
|
||||
@ -255,7 +261,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
@Override
|
||||
protected void onEnabled(boolean joining) throws ExoPlaybackException {
|
||||
super.onEnabled(joining);
|
||||
eventDispatcher.enabled(codecCounters);
|
||||
eventDispatcher.enabled(decoderCounters);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -287,8 +293,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
try {
|
||||
super.onDisabled();
|
||||
} finally {
|
||||
codecCounters.ensureUpdated();
|
||||
eventDispatcher.disabled(codecCounters);
|
||||
decoderCounters.ensureUpdated();
|
||||
eventDispatcher.disabled(decoderCounters);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -326,7 +332,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
|
||||
if (shouldSkip) {
|
||||
codec.releaseOutputBuffer(bufferIndex, false);
|
||||
codecCounters.skippedOutputBufferCount++;
|
||||
decoderCounters.skippedOutputBufferCount++;
|
||||
audioTrack.handleDiscontinuity();
|
||||
return true;
|
||||
}
|
||||
@ -378,7 +384,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
// Release the buffer if it was consumed.
|
||||
if ((handleBufferResult & AudioTrack.RESULT_BUFFER_CONSUMED) != 0) {
|
||||
codec.releaseOutputBuffer(bufferIndex, false);
|
||||
codecCounters.renderedOutputBufferCount++;
|
||||
decoderCounters.renderedOutputBufferCount++;
|
||||
return true;
|
||||
}
|
||||
|
@ -13,20 +13,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.extensions;
|
||||
package com.google.android.exoplayer2.audio;
|
||||
|
||||
import com.google.android.exoplayer2.AudioRendererEventListener;
|
||||
import com.google.android.exoplayer2.AudioRendererEventListener.EventDispatcher;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.CodecCounters;
|
||||
import com.google.android.exoplayer2.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.FormatHolder;
|
||||
import com.google.android.exoplayer2.MediaClock;
|
||||
import com.google.android.exoplayer2.Renderer;
|
||||
import com.google.android.exoplayer2.audio.AudioCapabilities;
|
||||
import com.google.android.exoplayer2.audio.AudioTrack;
|
||||
import com.google.android.exoplayer2.audio.AudioRendererEventListener.EventDispatcher;
|
||||
import com.google.android.exoplayer2.decoder.DecoderCounters;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.decoder.SimpleDecoder;
|
||||
import com.google.android.exoplayer2.decoder.SimpleOutputBuffer;
|
||||
import com.google.android.exoplayer2.util.MediaClock;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.TraceUtil;
|
||||
|
||||
@ -38,12 +37,12 @@ import android.os.SystemClock;
|
||||
/**
|
||||
* Decodes and renders audio using a {@link SimpleDecoder}.
|
||||
*/
|
||||
public abstract class AudioDecoderRenderer extends Renderer implements MediaClock {
|
||||
public abstract class SimpleDecoderAudioRenderer extends Renderer implements MediaClock {
|
||||
|
||||
private final EventDispatcher eventDispatcher;
|
||||
private final FormatHolder formatHolder;
|
||||
|
||||
private CodecCounters codecCounters;
|
||||
private DecoderCounters decoderCounters;
|
||||
private Format inputFormat;
|
||||
private SimpleDecoder<DecoderInputBuffer, ? extends SimpleOutputBuffer,
|
||||
? extends AudioDecoderException> decoder;
|
||||
@ -61,7 +60,7 @@ public abstract class AudioDecoderRenderer extends Renderer implements MediaCloc
|
||||
private boolean audioTrackHasData;
|
||||
private long lastFeedElapsedRealtimeMs;
|
||||
|
||||
public AudioDecoderRenderer() {
|
||||
public SimpleDecoderAudioRenderer() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
@ -70,7 +69,7 @@ public abstract class AudioDecoderRenderer extends Renderer implements MediaCloc
|
||||
* null if delivery of events is not required.
|
||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||
*/
|
||||
public AudioDecoderRenderer(Handler eventHandler,
|
||||
public SimpleDecoderAudioRenderer(Handler eventHandler,
|
||||
AudioRendererEventListener eventListener) {
|
||||
this (eventHandler, eventListener, null, AudioManager.STREAM_MUSIC);
|
||||
}
|
||||
@ -83,7 +82,7 @@ public abstract class AudioDecoderRenderer extends Renderer implements MediaCloc
|
||||
* default capabilities (no encoded audio passthrough support) should be assumed.
|
||||
* @param streamType The type of audio stream for the {@link AudioTrack}.
|
||||
*/
|
||||
public AudioDecoderRenderer(Handler eventHandler,
|
||||
public SimpleDecoderAudioRenderer(Handler eventHandler,
|
||||
AudioRendererEventListener eventListener, AudioCapabilities audioCapabilities,
|
||||
int streamType) {
|
||||
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||
@ -119,7 +118,7 @@ public abstract class AudioDecoderRenderer extends Renderer implements MediaCloc
|
||||
long codecInitializedTimestamp = SystemClock.elapsedRealtime();
|
||||
eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp,
|
||||
codecInitializedTimestamp - codecInitializingTimestamp);
|
||||
codecCounters.codecInitCount++;
|
||||
decoderCounters.codecInitCount++;
|
||||
} catch (AudioDecoderException e) {
|
||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||
}
|
||||
@ -135,7 +134,7 @@ public abstract class AudioDecoderRenderer extends Renderer implements MediaCloc
|
||||
| AudioDecoderException e) {
|
||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||
}
|
||||
codecCounters.ensureUpdated();
|
||||
decoderCounters.ensureUpdated();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -170,7 +169,7 @@ public abstract class AudioDecoderRenderer extends Renderer implements MediaCloc
|
||||
if (outputBuffer == null) {
|
||||
return false;
|
||||
}
|
||||
codecCounters.skippedOutputBufferCount += outputBuffer.skippedOutputBufferCount;
|
||||
decoderCounters.skippedOutputBufferCount += outputBuffer.skippedOutputBufferCount;
|
||||
}
|
||||
|
||||
if (outputBuffer.isEndOfStream()) {
|
||||
@ -219,7 +218,7 @@ public abstract class AudioDecoderRenderer extends Renderer implements MediaCloc
|
||||
|
||||
// Release the buffer if it was consumed.
|
||||
if ((handleBufferResult & AudioTrack.RESULT_BUFFER_CONSUMED) != 0) {
|
||||
codecCounters.renderedOutputBufferCount++;
|
||||
decoderCounters.renderedOutputBufferCount++;
|
||||
outputBuffer.release();
|
||||
outputBuffer = null;
|
||||
return true;
|
||||
@ -256,7 +255,7 @@ public abstract class AudioDecoderRenderer extends Renderer implements MediaCloc
|
||||
}
|
||||
inputBuffer.flip();
|
||||
decoder.queueInputBuffer(inputBuffer);
|
||||
codecCounters.inputBufferCount++;
|
||||
decoderCounters.inputBufferCount++;
|
||||
inputBuffer = null;
|
||||
return true;
|
||||
}
|
||||
@ -307,8 +306,8 @@ public abstract class AudioDecoderRenderer extends Renderer implements MediaCloc
|
||||
|
||||
@Override
|
||||
protected void onEnabled(boolean joining) throws ExoPlaybackException {
|
||||
codecCounters = new CodecCounters();
|
||||
eventDispatcher.enabled(codecCounters);
|
||||
decoderCounters = new DecoderCounters();
|
||||
eventDispatcher.enabled(decoderCounters);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -343,12 +342,12 @@ public abstract class AudioDecoderRenderer extends Renderer implements MediaCloc
|
||||
if (decoder != null) {
|
||||
decoder.release();
|
||||
decoder = null;
|
||||
codecCounters.codecReleaseCount++;
|
||||
decoderCounters.codecReleaseCount++;
|
||||
}
|
||||
audioTrack.release();
|
||||
} finally {
|
||||
codecCounters.ensureUpdated();
|
||||
eventDispatcher.disabled(codecCounters);
|
||||
decoderCounters.ensureUpdated();
|
||||
eventDispatcher.disabled(decoderCounters);
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.util;
|
||||
package com.google.android.exoplayer2.decoder;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
package com.google.android.exoplayer2.decoder;
|
||||
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.extensions;
|
||||
package com.google.android.exoplayer2.decoder;
|
||||
|
||||
/**
|
||||
* A media decoder.
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
package com.google.android.exoplayer2.decoder;
|
||||
|
||||
/**
|
||||
* Maintains codec event counts, for debugging purposes only.
|
||||
@ -22,7 +22,7 @@ package com.google.android.exoplayer2;
|
||||
* To ensure that the counter values are correctly reflected between threads, users of this class
|
||||
* should invoke {@link #ensureUpdated()} prior to reading and after writing.
|
||||
*/
|
||||
public final class CodecCounters {
|
||||
public final class DecoderCounters {
|
||||
|
||||
/**
|
||||
* The number of times the codec has been initialized.
|
||||
@ -73,9 +73,9 @@ public final class CodecCounters {
|
||||
/**
|
||||
* Merges the counts from {@code other} into this instance.
|
||||
*
|
||||
* @param other The {@link CodecCounters} to merge into this instance.
|
||||
* @param other The {@link DecoderCounters} to merge into this instance.
|
||||
*/
|
||||
public void merge(CodecCounters other) {
|
||||
public void merge(DecoderCounters other) {
|
||||
codecInitCount += other.codecInitCount;
|
||||
codecReleaseCount += other.codecReleaseCount;
|
||||
inputBufferCount += other.inputBufferCount;
|
@ -13,9 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
package com.google.android.exoplayer2.decoder;
|
||||
|
||||
import com.google.android.exoplayer2.util.Buffer;
|
||||
import com.google.android.exoplayer2.C;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -13,9 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.extensions;
|
||||
|
||||
import com.google.android.exoplayer2.util.Buffer;
|
||||
package com.google.android.exoplayer2.decoder;
|
||||
|
||||
/**
|
||||
* Output buffer decoded by a {@link Decoder}.
|
@ -13,10 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.extensions;
|
||||
package com.google.android.exoplayer2.decoder;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
|
||||
import java.util.LinkedList;
|
||||
@ -27,20 +26,6 @@ import java.util.LinkedList;
|
||||
public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends OutputBuffer,
|
||||
E extends Exception> implements Decoder<I, O, E> {
|
||||
|
||||
/**
|
||||
* Listener for {@link SimpleDecoder} events.
|
||||
*/
|
||||
public interface EventListener<E> {
|
||||
|
||||
/**
|
||||
* Invoked when the decoder encounters an error.
|
||||
*
|
||||
* @param e The corresponding exception.
|
||||
*/
|
||||
void onDecoderError(E e);
|
||||
|
||||
}
|
||||
|
||||
private final Thread decodeThread;
|
||||
|
||||
private final Object lock;
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.extensions;
|
||||
package com.google.android.exoplayer2.decoder;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -16,9 +16,9 @@
|
||||
package com.google.android.exoplayer2.extractor;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.FormatHolder;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.upstream.Allocation;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
|
@ -52,7 +52,7 @@ public final class GaplessInfoHolder {
|
||||
/**
|
||||
* Populates the holder with data from an MP3 Xing header, if valid and non-zero.
|
||||
*
|
||||
* @param value The 24-bit value to parse.
|
||||
* @param value The 24-bit value to decode.
|
||||
* @return Whether the holder was populated.
|
||||
*/
|
||||
public boolean setFromXingHeaderValue(int value) {
|
||||
|
@ -13,10 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.util;
|
||||
package com.google.android.exoplayer2.extractor;
|
||||
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
|
||||
/**
|
||||
* Representation of an MPEG audio frame header.
|
||||
* An MPEG audio frame header.
|
||||
*/
|
||||
public final class MpegAudioHeader {
|
||||
|
@ -101,7 +101,7 @@ import java.util.List;
|
||||
hasOutputFormat = true;
|
||||
} else if (packetType == AVC_PACKET_TYPE_AVC_NALU) {
|
||||
// TODO: Deduplicate with Mp4Extractor.
|
||||
// Zero the top three bytes of the array that we'll use to parse nal unit lengths, in case
|
||||
// Zero the top three bytes of the array that we'll use to decode nal unit lengths, in case
|
||||
// they're only 1 or 2 bytes long.
|
||||
byte[] nalLengthData = nalLength.data;
|
||||
nalLengthData[0] = 0;
|
||||
|
@ -920,7 +920,7 @@ public final class MatroskaExtractor implements Extractor {
|
||||
if (CODEC_ID_H264.equals(track.codecId) || CODEC_ID_H265.equals(track.codecId)) {
|
||||
// TODO: Deduplicate with Mp4Extractor.
|
||||
|
||||
// Zero the top three bytes of the array that we'll use to parse nal unit lengths, in case
|
||||
// Zero the top three bytes of the array that we'll use to decode nal unit lengths, in case
|
||||
// they're only 1 or 2 bytes long.
|
||||
byte[] nalLengthData = nalLength.data;
|
||||
nalLengthData[0] = 0;
|
||||
|
@ -26,7 +26,7 @@ import com.google.android.exoplayer2.extractor.GaplessInfoHolder;
|
||||
import com.google.android.exoplayer2.extractor.PositionHolder;
|
||||
import com.google.android.exoplayer2.extractor.SeekMap;
|
||||
import com.google.android.exoplayer2.extractor.TrackOutput;
|
||||
import com.google.android.exoplayer2.util.MpegAudioHeader;
|
||||
import com.google.android.exoplayer2.extractor.MpegAudioHeader;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
package com.google.android.exoplayer2.extractor.mp3;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.util.MpegAudioHeader;
|
||||
import com.google.android.exoplayer2.extractor.MpegAudioHeader;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
package com.google.android.exoplayer2.extractor.mp3;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.util.MpegAudioHeader;
|
||||
import com.google.android.exoplayer2.extractor.MpegAudioHeader;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
|
@ -18,9 +18,9 @@ package com.google.android.exoplayer2.extractor.mp4;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.audio.Ac3Util;
|
||||
import com.google.android.exoplayer2.drm.DrmInitData;
|
||||
import com.google.android.exoplayer2.extractor.GaplessInfoHolder;
|
||||
import com.google.android.exoplayer2.util.Ac3Util;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.CodecSpecificDataUtil;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
@ -49,7 +49,7 @@ import java.util.List;
|
||||
/**
|
||||
* Parses a trak atom (defined in 14496-12).
|
||||
*
|
||||
* @param trak Atom to parse.
|
||||
* @param trak Atom to decode.
|
||||
* @param mvhd Movie header atom, used to get the timescale.
|
||||
* @param duration The duration in units of the timescale declared in the mvhd atom, or -1 if the
|
||||
* duration should be parsed from the tkhd atom.
|
||||
@ -93,7 +93,7 @@ import java.util.List;
|
||||
* Parses an stbl atom (defined in 14496-12).
|
||||
*
|
||||
* @param track Track to which this sample table corresponds.
|
||||
* @param stblAtom stbl (sample table) atom to parse.
|
||||
* @param stblAtom stbl (sample table) atom to decode.
|
||||
* @param gaplessInfoHolder Holder to populate with gapless playback information.
|
||||
* @return Sample table described by the stbl atom.
|
||||
* @throws ParserException If the resulting sample sequence does not contain a sync sample.
|
||||
@ -191,7 +191,7 @@ import java.util.List;
|
||||
remainingSamplesAtTimestampOffset = ctts.readUnsignedIntToInt();
|
||||
// The BMFF spec (ISO 14496-12) states that sample offsets should be unsigned integers
|
||||
// in version 0 ctts boxes, however some streams violate the spec and use signed
|
||||
// integers instead. It's safe to always parse sample offsets as signed integers here,
|
||||
// integers instead. It's safe to always decode sample offsets as signed integers here,
|
||||
// because unsigned integers will still be parsed correctly (unless their top bit is
|
||||
// set, which is never true in practice because sample offsets are always small).
|
||||
timestampOffset = ctts.readInt();
|
||||
@ -378,14 +378,14 @@ import java.util.List;
|
||||
/**
|
||||
* Parses a udta atom.
|
||||
*
|
||||
* @param udtaAtom The udta (user data) atom to parse.
|
||||
* @param udtaAtom The udta (user data) atom to decode.
|
||||
* @param isQuickTime True for QuickTime media. False otherwise.
|
||||
* @param out {@link GaplessInfoHolder} to populate with gapless playback information.
|
||||
*/
|
||||
public static void parseUdta(Atom.LeafAtom udtaAtom, boolean isQuickTime, GaplessInfoHolder out) {
|
||||
if (isQuickTime) {
|
||||
// Meta boxes are regular boxes rather than full boxes in QuickTime. For now, don't try and
|
||||
// parse one.
|
||||
// decode one.
|
||||
return;
|
||||
}
|
||||
ParsableByteArray udtaData = udtaAtom.data;
|
||||
@ -535,7 +535,7 @@ import java.util.List;
|
||||
/**
|
||||
* Parses an hdlr atom.
|
||||
*
|
||||
* @param hdlr The hdlr atom to parse.
|
||||
* @param hdlr The hdlr atom to decode.
|
||||
* @return The track type.
|
||||
*/
|
||||
private static int parseHdlr(ParsableByteArray hdlr) {
|
||||
@ -556,7 +556,7 @@ import java.util.List;
|
||||
/**
|
||||
* Parses an mdhd atom (defined in 14496-12).
|
||||
*
|
||||
* @param mdhd The mdhd atom to parse.
|
||||
* @param mdhd The mdhd atom to decode.
|
||||
* @return A pair consisting of the media timescale defined as the number of time units that pass
|
||||
* in one second, and the language code.
|
||||
*/
|
||||
@ -577,7 +577,7 @@ import java.util.List;
|
||||
/**
|
||||
* Parses a stsd atom (defined in 14496-12).
|
||||
*
|
||||
* @param stsd The stsd atom to parse.
|
||||
* @param stsd The stsd atom to decode.
|
||||
* @param trackId The track's identifier in its container.
|
||||
* @param rotationDegrees The rotation of the track in degrees.
|
||||
* @param language The language of the track.
|
||||
@ -780,7 +780,7 @@ import java.util.List;
|
||||
/**
|
||||
* Parses the edts atom (defined in 14496-12 subsection 8.6.5).
|
||||
*
|
||||
* @param edtsAtom edts (edit box) atom to parse.
|
||||
* @param edtsAtom edts (edit box) atom to decode.
|
||||
* @return Pair of edit list durations and edit list media times, or a pair of nulls if they are
|
||||
* not present.
|
||||
*/
|
||||
|
@ -538,7 +538,7 @@ public final class FragmentedMp4Extractor implements Extractor {
|
||||
/**
|
||||
* Parses a saio atom (defined in 14496-12).
|
||||
*
|
||||
* @param saio The saio atom to parse.
|
||||
* @param saio The saio atom to decode.
|
||||
* @param out The {@link TrackFragment} to populate with data from the saio atom.
|
||||
*/
|
||||
private static void parseSaio(ParsableByteArray saio, TrackFragment out) throws ParserException {
|
||||
@ -565,7 +565,7 @@ public final class FragmentedMp4Extractor implements Extractor {
|
||||
* returns the {@link TrackBundle} of the corresponding {@link Track}. If the tfhd does not refer
|
||||
* to any {@link TrackBundle}, {@code null} is returned and no changes are made.
|
||||
*
|
||||
* @param tfhd The tfhd atom to parse.
|
||||
* @param tfhd The tfhd atom to decode.
|
||||
* @param trackBundles The track bundles, one of which corresponds to the tfhd atom being parsed.
|
||||
* @return The {@link TrackBundle} to which the {@link TrackFragment} belongs, or null if the tfhd
|
||||
* does not refer to any {@link TrackBundle}.
|
||||
@ -621,7 +621,7 @@ public final class FragmentedMp4Extractor implements Extractor {
|
||||
* which parsed data should be placed.
|
||||
* @param decodeTime The decode time of the first sample in the fragment run.
|
||||
* @param flags Flags to allow any required workaround to be executed.
|
||||
* @param trun The trun atom to parse.
|
||||
* @param trun The trun atom to decode.
|
||||
*/
|
||||
private static void parseTrun(TrackBundle trackBundle, long decodeTime, int flags,
|
||||
ParsableByteArray trun) {
|
||||
@ -681,7 +681,7 @@ public final class FragmentedMp4Extractor implements Extractor {
|
||||
if (sampleCompositionTimeOffsetsPresent) {
|
||||
// The BMFF spec (ISO 14496-12) states that sample offsets should be unsigned integers in
|
||||
// version 0 trun boxes, however a significant number of streams violate the spec and use
|
||||
// signed integers instead. It's safe to always parse sample offsets as signed integers
|
||||
// signed integers instead. It's safe to always decode sample offsets as signed integers
|
||||
// here, because unsigned integers will still be parsed correctly (unless their top bit is
|
||||
// set, which is never true in practice because sample offsets are always small).
|
||||
int sampleOffset = trun.readInt();
|
||||
@ -926,7 +926,7 @@ public final class FragmentedMp4Extractor implements Extractor {
|
||||
TrackOutput output = currentTrackBundle.output;
|
||||
int sampleIndex = currentTrackBundle.currentSampleIndex;
|
||||
if (track.nalUnitLengthFieldLength != -1) {
|
||||
// Zero the top three bytes of the array that we'll use to parse nal unit lengths, in case
|
||||
// Zero the top three bytes of the array that we'll use to decode nal unit lengths, in case
|
||||
// they're only 1 or 2 bytes long.
|
||||
byte[] nalLengthData = nalLength.data;
|
||||
nalLengthData[0] = 0;
|
||||
@ -1044,7 +1044,7 @@ public final class FragmentedMp4Extractor implements Extractor {
|
||||
return 1 + vectorSize + subsampleDataLength;
|
||||
}
|
||||
|
||||
/** Returns whether the extractor should parse a leaf atom with type {@code atom}. */
|
||||
/** Returns whether the extractor should decode a leaf atom with type {@code atom}. */
|
||||
private static boolean shouldParseLeafAtom(int atom) {
|
||||
return atom == Atom.TYPE_hdlr || atom == Atom.TYPE_mdhd || atom == Atom.TYPE_mvhd
|
||||
|| atom == Atom.TYPE_sidx || atom == Atom.TYPE_stsd || atom == Atom.TYPE_tfdt
|
||||
@ -1055,7 +1055,7 @@ public final class FragmentedMp4Extractor implements Extractor {
|
||||
|| atom == Atom.TYPE_mehd;
|
||||
}
|
||||
|
||||
/** Returns whether the extractor should parse a container atom with type {@code atom}. */
|
||||
/** Returns whether the extractor should decode a container atom with type {@code atom}. */
|
||||
private static boolean shouldParseContainerAtom(int atom) {
|
||||
return atom == Atom.TYPE_moov || atom == Atom.TYPE_trak || atom == Atom.TYPE_mdia
|
||||
|| atom == Atom.TYPE_minf || atom == Atom.TYPE_stbl || atom == Atom.TYPE_moof
|
||||
|
@ -401,7 +401,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
}
|
||||
input.skipFully((int) skipAmount);
|
||||
if (track.track.nalUnitLengthFieldLength != -1) {
|
||||
// Zero the top three bytes of the array that we'll use to parse nal unit lengths, in case
|
||||
// Zero the top three bytes of the array that we'll use to decode nal unit lengths, in case
|
||||
// they're only 1 or 2 bytes long.
|
||||
byte[] nalLengthData = nalLength.data;
|
||||
nalLengthData[0] = 0;
|
||||
@ -470,7 +470,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the extractor should parse a leaf atom with type {@code atom}.
|
||||
* Returns whether the extractor should decode a leaf atom with type {@code atom}.
|
||||
*/
|
||||
private static boolean shouldParseLeafAtom(int atom) {
|
||||
return atom == Atom.TYPE_mdhd || atom == Atom.TYPE_mvhd || atom == Atom.TYPE_hdlr
|
||||
@ -481,7 +481,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the extractor should parse a container atom with type {@code atom}.
|
||||
* Returns whether the extractor should decode a container atom with type {@code atom}.
|
||||
*/
|
||||
private static boolean shouldParseContainerAtom(int atom) {
|
||||
return atom == Atom.TYPE_moov || atom == Atom.TYPE_trak || atom == Atom.TYPE_mdia
|
||||
|
@ -17,8 +17,8 @@ package com.google.android.exoplayer2.extractor.ts;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.audio.Ac3Util;
|
||||
import com.google.android.exoplayer2.extractor.TrackOutput;
|
||||
import com.google.android.exoplayer2.util.Ac3Util;
|
||||
import com.google.android.exoplayer2.util.ParsableBitArray;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
@ -56,7 +56,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
*
|
||||
* @param output Track output for extracted samples.
|
||||
* @param isEac3 Whether the stream is E-AC-3 (ETSI TS 102 366 Annex E). Specify {@code false} to
|
||||
* parse sample headers as AC-3.
|
||||
* decode sample headers as AC-3.
|
||||
*/
|
||||
public Ac3Reader(TrackOutput output, boolean isEac3) {
|
||||
super(output);
|
||||
|
@ -17,8 +17,8 @@ package com.google.android.exoplayer2.extractor.ts;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.audio.DtsUtil;
|
||||
import com.google.android.exoplayer2.extractor.TrackOutput;
|
||||
import com.google.android.exoplayer2.util.DtsUtil;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
/**
|
||||
|
@ -119,7 +119,7 @@ import java.util.Collections;
|
||||
// passed to csdDataTargetBuffer.
|
||||
int bytesAlreadyPassed = lengthToStartCode < 0 ? -lengthToStartCode : 0;
|
||||
if (csdBuffer.onStartCode(startCodeValue, bytesAlreadyPassed)) {
|
||||
// The csd data is complete, so we can parse and output the media format.
|
||||
// The csd data is complete, so we can decode and output the media format.
|
||||
Pair<Format, Long> result = parseCsdBuffer(csdBuffer);
|
||||
output.format(result.first);
|
||||
frameDurationUs = result.second;
|
||||
|
@ -330,7 +330,7 @@ import java.util.List;
|
||||
}
|
||||
int picParameterSetId = bitArray.readUnsignedExpGolombCodedInt();
|
||||
if (pps.indexOfKey(picParameterSetId) < 0) {
|
||||
// We have not seen the PPS yet, so don't try to parse the slice header.
|
||||
// We have not seen the PPS yet, so don't try to decode the slice header.
|
||||
isFilling = false;
|
||||
return;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ package com.google.android.exoplayer2.extractor.ts;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.extractor.TrackOutput;
|
||||
import com.google.android.exoplayer2.util.MpegAudioHeader;
|
||||
import com.google.android.exoplayer2.extractor.MpegAudioHeader;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
/**
|
||||
|
@ -18,7 +18,7 @@ package com.google.android.exoplayer2.extractor.ts;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.extractor.TrackOutput;
|
||||
import com.google.android.exoplayer2.text.eia608.Eia608Parser;
|
||||
import com.google.android.exoplayer2.text.eia608.Eia608Decoder;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
@ -51,7 +51,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
payloadSize += b;
|
||||
} while (b == 0xFF);
|
||||
// Process the payload.
|
||||
if (Eia608Parser.isSeiMessageEia608(payloadType, payloadSize, seiBuffer)) {
|
||||
if (Eia608Decoder.isSeiMessageEia608(payloadType, payloadSize, seiBuffer)) {
|
||||
// Ignore country_code (1) + provider_code (2) + user_identifier (4)
|
||||
// + user_data_type_code (1).
|
||||
seiBuffer.skipBytes(8);
|
||||
|
@ -13,57 +13,59 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
package com.google.android.exoplayer2.mediacodec;
|
||||
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.media.MediaCodecInfo;
|
||||
import android.media.MediaCodec;
|
||||
import android.media.MediaCodecInfo.AudioCapabilities;
|
||||
import android.media.MediaCodecInfo.CodecCapabilities;
|
||||
import android.media.MediaCodecInfo.CodecProfileLevel;
|
||||
import android.media.MediaCodecInfo.VideoCapabilities;
|
||||
import android.util.Pair;
|
||||
|
||||
/**
|
||||
* Contains information about a media decoder.
|
||||
* Contains information about a {@link MediaCodec} decoder.
|
||||
*/
|
||||
@TargetApi(16)
|
||||
public final class MediaCodecDecoderInfo {
|
||||
public final class MediaCodecInfo {
|
||||
|
||||
/**
|
||||
* The name of the decoder.
|
||||
* <p>
|
||||
* May be passed to {@link android.media.MediaCodec#createByCodecName(String)} to create an
|
||||
* instance of the decoder.
|
||||
* May be passed to {@link MediaCodec#createByCodecName(String)} to create an instance of the
|
||||
* decoder.
|
||||
*/
|
||||
public final String name;
|
||||
|
||||
/**
|
||||
* Whether the decoder supports seamless resolution switches.
|
||||
*
|
||||
* @see MediaCodecInfo.CodecCapabilities#isFeatureSupported(String)
|
||||
* @see MediaCodecInfo.CodecCapabilities#FEATURE_AdaptivePlayback
|
||||
* @see CodecCapabilities#isFeatureSupported(String)
|
||||
* @see CodecCapabilities#FEATURE_AdaptivePlayback
|
||||
*/
|
||||
public final boolean adaptive;
|
||||
|
||||
private final String mimeType;
|
||||
private final CodecCapabilities capabilities;
|
||||
|
||||
public static MediaCodecDecoderInfo newPassthroughInstance(String name) {
|
||||
return new MediaCodecDecoderInfo(name, null, null);
|
||||
public static MediaCodecInfo newPassthroughInstance(String name) {
|
||||
return new MediaCodecInfo(name, null, null);
|
||||
}
|
||||
|
||||
public static MediaCodecDecoderInfo newInstance(String name, String mimeType,
|
||||
public static MediaCodecInfo newInstance(String name, String mimeType,
|
||||
CodecCapabilities capabilities) {
|
||||
return new MediaCodecDecoderInfo(name, mimeType, capabilities);
|
||||
return new MediaCodecInfo(name, mimeType, capabilities);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name The name of the decoder.
|
||||
* @param capabilities The capabilities of the decoder.
|
||||
*/
|
||||
private MediaCodecDecoderInfo(String name, String mimeType, CodecCapabilities capabilities) {
|
||||
private MediaCodecInfo(String name, String mimeType, CodecCapabilities capabilities) {
|
||||
this.name = Assertions.checkNotNull(name);
|
||||
this.mimeType = mimeType;
|
||||
this.capabilities = capabilities;
|
||||
@ -101,13 +103,12 @@ public final class MediaCodecDecoderInfo {
|
||||
if (!codecMimeType.equals(MimeTypes.VIDEO_H265)) {
|
||||
return true;
|
||||
}
|
||||
Pair<Integer, Integer> codecProfileAndLevel =
|
||||
MediaCodecUtil.getHevcProfileAndLevel(codec);
|
||||
Pair<Integer, Integer> codecProfileAndLevel = MediaCodecUtil.getHevcProfileAndLevel(codec);
|
||||
if (codecProfileAndLevel == null) {
|
||||
// If we don't know any better, we assume that the profile and level are supported.
|
||||
return true;
|
||||
}
|
||||
for (MediaCodecInfo.CodecProfileLevel capabilities : getProfileLevels()) {
|
||||
for (CodecProfileLevel capabilities : getProfileLevels()) {
|
||||
if (capabilities.profile == codecProfileAndLevel.first
|
||||
&& capabilities.level >= codecProfileAndLevel.second) {
|
||||
return true;
|
||||
@ -130,7 +131,7 @@ public final class MediaCodecDecoderInfo {
|
||||
if (capabilities == null) {
|
||||
return false;
|
||||
}
|
||||
MediaCodecInfo.VideoCapabilities videoCapabilities = capabilities.getVideoCapabilities();
|
||||
VideoCapabilities videoCapabilities = capabilities.getVideoCapabilities();
|
||||
return videoCapabilities != null && videoCapabilities.isSizeSupported(width, height);
|
||||
}
|
||||
|
||||
@ -149,7 +150,7 @@ public final class MediaCodecDecoderInfo {
|
||||
if (capabilities == null) {
|
||||
return false;
|
||||
}
|
||||
MediaCodecInfo.VideoCapabilities videoCapabilities = capabilities.getVideoCapabilities();
|
||||
VideoCapabilities videoCapabilities = capabilities.getVideoCapabilities();
|
||||
return videoCapabilities != null && videoCapabilities.areSizeAndRateSupported(width, height,
|
||||
frameRate);
|
||||
}
|
||||
@ -167,7 +168,7 @@ public final class MediaCodecDecoderInfo {
|
||||
if (capabilities == null) {
|
||||
return false;
|
||||
}
|
||||
MediaCodecInfo.AudioCapabilities audioCapabilities = capabilities.getAudioCapabilities();
|
||||
AudioCapabilities audioCapabilities = capabilities.getAudioCapabilities();
|
||||
return audioCapabilities != null && audioCapabilities.isSampleRateSupported(sampleRate);
|
||||
}
|
||||
|
||||
@ -184,7 +185,7 @@ public final class MediaCodecDecoderInfo {
|
||||
if (capabilities == null) {
|
||||
return false;
|
||||
}
|
||||
MediaCodecInfo.AudioCapabilities audioCapabilities = capabilities.getAudioCapabilities();
|
||||
AudioCapabilities audioCapabilities = capabilities.getAudioCapabilities();
|
||||
return audioCapabilities != null && audioCapabilities.getMaxInputChannelCount() >= channelCount;
|
||||
}
|
||||
|
@ -13,11 +13,18 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
package com.google.android.exoplayer2.mediacodec;
|
||||
|
||||
import com.google.android.exoplayer2.MediaCodecUtil.DecoderQueryException;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.FormatHolder;
|
||||
import com.google.android.exoplayer2.Renderer;
|
||||
import com.google.android.exoplayer2.decoder.DecoderCounters;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.drm.DrmSession;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.NalUnitUtil;
|
||||
@ -195,7 +202,7 @@ public abstract class MediaCodecRenderer extends Renderer {
|
||||
private boolean outputStreamEnded;
|
||||
private boolean waitingForKeys;
|
||||
|
||||
protected CodecCounters codecCounters;
|
||||
protected DecoderCounters decoderCounters;
|
||||
|
||||
/**
|
||||
* @param mediaCodecSelector A decoder selector.
|
||||
@ -248,16 +255,16 @@ public abstract class MediaCodecRenderer extends Renderer {
|
||||
throws DecoderQueryException;
|
||||
|
||||
/**
|
||||
* Returns a {@link MediaCodecDecoderInfo} for a given format.
|
||||
* Returns a {@link MediaCodecInfo} for a given format.
|
||||
*
|
||||
* @param mediaCodecSelector The decoder selector.
|
||||
* @param format The format for which a decoder is required.
|
||||
* @param requiresSecureDecoder Whether a secure decoder is required.
|
||||
* @return A {@link MediaCodecDecoderInfo} describing the decoder to instantiate, or null if no
|
||||
* @return A {@link MediaCodecInfo} describing the decoder to instantiate, or null if no
|
||||
* suitable decoder exists.
|
||||
* @throws DecoderQueryException Thrown if there was an error querying decoders.
|
||||
*/
|
||||
protected MediaCodecDecoderInfo getDecoderInfo(MediaCodecSelector mediaCodecSelector,
|
||||
protected MediaCodecInfo getDecoderInfo(MediaCodecSelector mediaCodecSelector,
|
||||
Format format, boolean requiresSecureDecoder) throws DecoderQueryException {
|
||||
return mediaCodecSelector.getDecoderInfo(format.sampleMimeType, requiresSecureDecoder);
|
||||
}
|
||||
@ -301,7 +308,7 @@ public abstract class MediaCodecRenderer extends Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
MediaCodecDecoderInfo decoderInfo = null;
|
||||
MediaCodecInfo decoderInfo = null;
|
||||
try {
|
||||
decoderInfo = getDecoderInfo(mediaCodecSelector, format, drmSessionRequiresSecureDecoder);
|
||||
if (decoderInfo == null && drmSessionRequiresSecureDecoder) {
|
||||
@ -358,7 +365,7 @@ public abstract class MediaCodecRenderer extends Renderer {
|
||||
? (SystemClock.elapsedRealtime() + MAX_CODEC_HOTSWAP_TIME_MS) : -1;
|
||||
inputIndex = -1;
|
||||
outputIndex = -1;
|
||||
codecCounters.codecInitCount++;
|
||||
decoderCounters.codecInitCount++;
|
||||
}
|
||||
|
||||
private void throwDecoderInitError(DecoderInitializationException e)
|
||||
@ -372,7 +379,7 @@ public abstract class MediaCodecRenderer extends Renderer {
|
||||
|
||||
@Override
|
||||
protected void onEnabled(boolean joining) throws ExoPlaybackException {
|
||||
codecCounters = new CodecCounters();
|
||||
decoderCounters = new DecoderCounters();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -421,7 +428,7 @@ public abstract class MediaCodecRenderer extends Renderer {
|
||||
codecReceivedEos = false;
|
||||
codecReconfigurationState = RECONFIGURATION_STATE_NONE;
|
||||
codecReinitializationState = REINITIALIZATION_STATE_NONE;
|
||||
codecCounters.codecReleaseCount++;
|
||||
decoderCounters.codecReleaseCount++;
|
||||
try {
|
||||
codec.stop();
|
||||
} finally {
|
||||
@ -456,7 +463,7 @@ public abstract class MediaCodecRenderer extends Renderer {
|
||||
while (feedInputBuffer()) {}
|
||||
TraceUtil.endSection();
|
||||
}
|
||||
codecCounters.ensureUpdated();
|
||||
decoderCounters.ensureUpdated();
|
||||
}
|
||||
|
||||
private void readFormat() throws ExoPlaybackException {
|
||||
@ -631,7 +638,7 @@ public abstract class MediaCodecRenderer extends Renderer {
|
||||
inputIndex = -1;
|
||||
codecReceivedBuffers = true;
|
||||
codecReconfigurationState = RECONFIGURATION_STATE_NONE;
|
||||
codecCounters.inputBufferCount++;
|
||||
decoderCounters.inputBufferCount++;
|
||||
} catch (CryptoException e) {
|
||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||
}
|
@ -13,9 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
package com.google.android.exoplayer2.mediacodec;
|
||||
|
||||
import com.google.android.exoplayer2.MediaCodecUtil.DecoderQueryException;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException;
|
||||
|
||||
import android.media.MediaCodec;
|
||||
|
||||
@ -30,13 +30,13 @@ public interface MediaCodecSelector {
|
||||
MediaCodecSelector DEFAULT = new MediaCodecSelector() {
|
||||
|
||||
@Override
|
||||
public MediaCodecDecoderInfo getDecoderInfo(String mimeType, boolean requiresSecureDecoder)
|
||||
public MediaCodecInfo getDecoderInfo(String mimeType, boolean requiresSecureDecoder)
|
||||
throws DecoderQueryException {
|
||||
return MediaCodecUtil.getDecoderInfo(mimeType, requiresSecureDecoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaCodecDecoderInfo getPassthroughDecoderInfo() throws DecoderQueryException {
|
||||
public MediaCodecInfo getPassthroughDecoderInfo() throws DecoderQueryException {
|
||||
return MediaCodecUtil.getPassthroughDecoderInfo();
|
||||
}
|
||||
|
||||
@ -47,20 +47,20 @@ public interface MediaCodecSelector {
|
||||
*
|
||||
* @param mimeType The mime type for which a decoder is required.
|
||||
* @param requiresSecureDecoder Whether a secure decoder is required.
|
||||
* @return A {@link MediaCodecDecoderInfo} describing the decoder, or null if no suitable decoder
|
||||
* @return A {@link MediaCodecInfo} describing the decoder, or null if no suitable decoder
|
||||
* exists.
|
||||
* @throws DecoderQueryException Thrown if there was an error querying decoders.
|
||||
*/
|
||||
MediaCodecDecoderInfo getDecoderInfo(String mimeType, boolean requiresSecureDecoder)
|
||||
MediaCodecInfo getDecoderInfo(String mimeType, boolean requiresSecureDecoder)
|
||||
throws DecoderQueryException;
|
||||
|
||||
/**
|
||||
* Selects a decoder to instantiate for audio passthrough.
|
||||
*
|
||||
* @return A {@link MediaCodecDecoderInfo} describing the decoder, or null if no suitable decoder
|
||||
* @return A {@link MediaCodecInfo} describing the decoder, or null if no suitable decoder
|
||||
* exists.
|
||||
* @throws DecoderQueryException Thrown if there was an error querying decoders.
|
||||
*/
|
||||
MediaCodecDecoderInfo getPassthroughDecoderInfo() throws DecoderQueryException;
|
||||
MediaCodecInfo getPassthroughDecoderInfo() throws DecoderQueryException;
|
||||
|
||||
}
|
@ -13,14 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
package com.google.android.exoplayer2.mediacodec;
|
||||
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.media.MediaCodecInfo;
|
||||
import android.media.MediaCodecInfo.CodecCapabilities;
|
||||
import android.media.MediaCodecInfo.CodecProfileLevel;
|
||||
import android.media.MediaCodecList;
|
||||
@ -58,15 +57,14 @@ public final class MediaCodecUtil {
|
||||
}
|
||||
|
||||
private static final String TAG = "MediaCodecUtil";
|
||||
private static final MediaCodecDecoderInfo PASSTHROUGH_DECODER_INFO =
|
||||
MediaCodecDecoderInfo.newPassthroughInstance("OMX.google.raw.decoder");
|
||||
private static final MediaCodecInfo PASSTHROUGH_DECODER_INFO =
|
||||
MediaCodecInfo.newPassthroughInstance("OMX.google.raw.decoder");
|
||||
private static final Map<String, Integer> HEVC_CODEC_STRING_TO_PROFILE_LEVEL;
|
||||
private static final String CODEC_ID_HEV1 = "hev1";
|
||||
private static final String CODEC_ID_HVC1 = "hvc1";
|
||||
private static final Pattern PROFILE_PATTERN = Pattern.compile("^\\D?(\\d+)$");
|
||||
|
||||
private static final HashMap<CodecKey, List<MediaCodecDecoderInfo>> decoderInfosCache =
|
||||
new HashMap<>();
|
||||
private static final HashMap<CodecKey, List<MediaCodecInfo>> decoderInfosCache = new HashMap<>();
|
||||
|
||||
// Lazily initialized.
|
||||
private static int maxH264DecodableFrameSize = -1;
|
||||
@ -94,10 +92,10 @@ public final class MediaCodecUtil {
|
||||
/**
|
||||
* Returns information about a decoder suitable for audio passthrough.
|
||||
**
|
||||
* @return A {@link MediaCodecDecoderInfo} describing the decoder, or null if no suitable decoder
|
||||
* @return A {@link MediaCodecInfo} describing the decoder, or null if no suitable decoder
|
||||
* exists.
|
||||
*/
|
||||
public static MediaCodecDecoderInfo getPassthroughDecoderInfo() {
|
||||
public static MediaCodecInfo getPassthroughDecoderInfo() {
|
||||
// TODO: Return null if the raw decoder doesn't exist.
|
||||
return PASSTHROUGH_DECODER_INFO;
|
||||
}
|
||||
@ -108,29 +106,29 @@ public final class MediaCodecUtil {
|
||||
* @param mimeType The mime type.
|
||||
* @param secure Whether the decoder is required to support secure decryption. Always pass false
|
||||
* unless secure decryption really is required.
|
||||
* @return A {@link MediaCodecDecoderInfo} describing the decoder, or null if no suitable decoder
|
||||
* @return A {@link MediaCodecInfo} describing the decoder, or null if no suitable decoder
|
||||
* exists.
|
||||
*/
|
||||
public static MediaCodecDecoderInfo getDecoderInfo(String mimeType, boolean secure)
|
||||
public static MediaCodecInfo getDecoderInfo(String mimeType, boolean secure)
|
||||
throws DecoderQueryException {
|
||||
List<MediaCodecDecoderInfo> decoderInfos = getDecoderInfos(mimeType, secure);
|
||||
List<MediaCodecInfo> decoderInfos = getDecoderInfos(mimeType, secure);
|
||||
return decoderInfos.isEmpty() ? null : decoderInfos.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all {@link MediaCodecDecoderInfo}s for the given mime type, in the order given by
|
||||
* Returns all {@link MediaCodecInfo}s for the given mime type, in the order given by
|
||||
* {@link MediaCodecList}.
|
||||
*
|
||||
* @param mimeType The mime type.
|
||||
* @param secure Whether the decoder is required to support secure decryption. Always pass false
|
||||
* unless secure decryption really is required.
|
||||
* @return A list of all @{link MediaCodecDecoderInfo}s for the given mime type, in the order
|
||||
* @return A list of all @{link MediaCodecInfo}s for the given mime type, in the order
|
||||
* given by {@link MediaCodecList}.
|
||||
*/
|
||||
public static synchronized List<MediaCodecDecoderInfo> getDecoderInfos(String mimeType,
|
||||
public static synchronized List<MediaCodecInfo> getDecoderInfos(String mimeType,
|
||||
boolean secure) throws DecoderQueryException {
|
||||
CodecKey key = new CodecKey(mimeType, secure);
|
||||
List<MediaCodecDecoderInfo> decoderInfos = decoderInfosCache.get(key);
|
||||
List<MediaCodecInfo> decoderInfos = decoderInfosCache.get(key);
|
||||
if (decoderInfos != null) {
|
||||
return decoderInfos;
|
||||
}
|
||||
@ -152,16 +150,16 @@ public final class MediaCodecUtil {
|
||||
return decoderInfos;
|
||||
}
|
||||
|
||||
private static List<MediaCodecDecoderInfo> getDecoderInfosInternal(
|
||||
private static List<MediaCodecInfo> getDecoderInfosInternal(
|
||||
CodecKey key, MediaCodecListCompat mediaCodecList) throws DecoderQueryException {
|
||||
try {
|
||||
List<MediaCodecDecoderInfo> decoderInfos = new ArrayList<>();
|
||||
List<MediaCodecInfo> decoderInfos = new ArrayList<>();
|
||||
String mimeType = key.mimeType;
|
||||
int numberOfCodecs = mediaCodecList.getCodecCount();
|
||||
boolean secureDecodersExplicit = mediaCodecList.secureDecodersExplicit();
|
||||
// Note: MediaCodecList is sorted by the framework such that the best decoders come first.
|
||||
for (int i = 0; i < numberOfCodecs; i++) {
|
||||
MediaCodecInfo codecInfo = mediaCodecList.getCodecInfoAt(i);
|
||||
android.media.MediaCodecInfo codecInfo = mediaCodecList.getCodecInfoAt(i);
|
||||
String codecName = codecInfo.getName();
|
||||
if (isCodecUsableDecoder(codecInfo, codecName, secureDecodersExplicit)) {
|
||||
for (String supportedType : codecInfo.getSupportedTypes()) {
|
||||
@ -172,9 +170,9 @@ public final class MediaCodecUtil {
|
||||
if ((secureDecodersExplicit && key.secure == secure)
|
||||
|| (!secureDecodersExplicit && !key.secure)) {
|
||||
decoderInfos.add(
|
||||
MediaCodecDecoderInfo.newInstance(codecName, mimeType, capabilities));
|
||||
MediaCodecInfo.newInstance(codecName, mimeType, capabilities));
|
||||
} else if (!secureDecodersExplicit && secure) {
|
||||
decoderInfos.add(MediaCodecDecoderInfo.newInstance(codecName + ".secure",
|
||||
decoderInfos.add(MediaCodecInfo.newInstance(codecName + ".secure",
|
||||
mimeType, capabilities));
|
||||
// It only makes sense to have one synthesized secure decoder, return immediately.
|
||||
return decoderInfos;
|
||||
@ -205,7 +203,7 @@ public final class MediaCodecUtil {
|
||||
/**
|
||||
* Returns whether the specified codec is usable for decoding on the current device.
|
||||
*/
|
||||
private static boolean isCodecUsableDecoder(MediaCodecInfo info, String name,
|
||||
private static boolean isCodecUsableDecoder(android.media.MediaCodecInfo info, String name,
|
||||
boolean secureDecodersExplicit) {
|
||||
if (info.isEncoder() || (!secureDecodersExplicit && name.endsWith(".secure"))) {
|
||||
return false;
|
||||
@ -277,7 +275,7 @@ public final class MediaCodecUtil {
|
||||
public static int maxH264DecodableFrameSize() throws DecoderQueryException {
|
||||
if (maxH264DecodableFrameSize == -1) {
|
||||
int result = 0;
|
||||
MediaCodecDecoderInfo decoderInfo = getDecoderInfo(MimeTypes.VIDEO_H264, false);
|
||||
MediaCodecInfo decoderInfo = getDecoderInfo(MimeTypes.VIDEO_H264, false);
|
||||
if (decoderInfo != null) {
|
||||
for (CodecProfileLevel profileLevel : decoderInfo.getProfileLevels()) {
|
||||
result = Math.max(avcLevelToMaxFrameSize(profileLevel.level), result);
|
||||
@ -291,8 +289,8 @@ public final class MediaCodecUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HEVC profile and level (as defined by {@link MediaCodecInfo.CodecProfileLevel})
|
||||
* corresponding to the given codec description string (as defined by RFC 6381).
|
||||
* Returns the HEVC profile and level (as defined by {@link CodecProfileLevel}) corresponding to
|
||||
* the given codec description string (as defined by RFC 6381).
|
||||
*
|
||||
* @param codec An HEVC codec description string, as defined by RFC 6381.
|
||||
* @return A pair (profile constant, level constant) if {@code codec} is well-formed and
|
||||
@ -375,7 +373,7 @@ public final class MediaCodecUtil {
|
||||
*
|
||||
* @param index The index.
|
||||
*/
|
||||
MediaCodecInfo getCodecInfoAt(int index);
|
||||
android.media.MediaCodecInfo getCodecInfoAt(int index);
|
||||
|
||||
/**
|
||||
* @return Returns whether secure decoders are explicitly listed, if present.
|
||||
@ -384,7 +382,7 @@ public final class MediaCodecUtil {
|
||||
|
||||
/**
|
||||
* Whether secure playback is supported for the given {@link CodecCapabilities}, which should
|
||||
* have been obtained from a {@link MediaCodecInfo} obtained from this list.
|
||||
* have been obtained from a {@link android.media.MediaCodecInfo} obtained from this list.
|
||||
*/
|
||||
boolean isSecurePlaybackSupported(String mimeType, CodecCapabilities capabilities);
|
||||
|
||||
@ -395,7 +393,7 @@ public final class MediaCodecUtil {
|
||||
|
||||
private final int codecKind;
|
||||
|
||||
private MediaCodecInfo[] mediaCodecInfos;
|
||||
private android.media.MediaCodecInfo[] mediaCodecInfos;
|
||||
|
||||
public MediaCodecListCompatV21(boolean includeSecure) {
|
||||
codecKind = includeSecure ? MediaCodecList.ALL_CODECS : MediaCodecList.REGULAR_CODECS;
|
||||
@ -408,7 +406,7 @@ public final class MediaCodecUtil {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaCodecInfo getCodecInfoAt(int index) {
|
||||
public android.media.MediaCodecInfo getCodecInfoAt(int index) {
|
||||
ensureMediaCodecInfosInitialized();
|
||||
return mediaCodecInfos[index];
|
||||
}
|
||||
@ -440,7 +438,7 @@ public final class MediaCodecUtil {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaCodecInfo getCodecInfoAt(int index) {
|
||||
public android.media.MediaCodecInfo getCodecInfoAt(int index) {
|
||||
return MediaCodecList.getCodecInfoAt(index);
|
||||
}
|
||||
|
@ -15,31 +15,30 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.metadata;
|
||||
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
|
||||
// TODO: This class should implement SimpleDecoder.
|
||||
/**
|
||||
* Parses objects of type <T> from binary data.
|
||||
*
|
||||
* @param <T> The type of the metadata.
|
||||
*/
|
||||
public interface MetadataParser<T> {
|
||||
public interface MetadataDecoder<T> {
|
||||
|
||||
/**
|
||||
* Checks whether the parser supports a given mime type.
|
||||
* Checks whether the decoder supports a given mime type.
|
||||
*
|
||||
* @param mimeType A metadata mime type.
|
||||
* @return Whether the mime type is supported.
|
||||
*/
|
||||
boolean canParse(String mimeType);
|
||||
boolean canDecode(String mimeType);
|
||||
|
||||
/**
|
||||
* Parses metadata objects of type <T> from the provided binary data.
|
||||
* Decodes metadata objects of type <T> from the provided binary data.
|
||||
*
|
||||
* @param data The raw binary data from which to parse the metadata.
|
||||
* @param data The raw binary data from which to decode the metadata.
|
||||
* @param size The size of the input data.
|
||||
* @return @return A parsed metadata object of type <T>.
|
||||
* @throws ParserException If a problem occurred parsing the data.
|
||||
* @return @return A decoded metadata object of type <T>.
|
||||
* @throws MetadataDecoderException If a problem occurred decoding the data.
|
||||
*/
|
||||
T parse(byte[] data, int size) throws ParserException;
|
||||
T decode(byte[] data, int size) throws MetadataDecoderException;
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.exoplayer2.metadata;
|
||||
|
||||
/**
|
||||
* Thrown when an error occurs decoding metadata.
|
||||
*/
|
||||
public class MetadataDecoderException extends Exception {
|
||||
|
||||
public MetadataDecoderException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public MetadataDecoderException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
@ -16,11 +16,11 @@
|
||||
package com.google.android.exoplayer2.metadata;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.FormatHolder;
|
||||
import com.google.android.exoplayer2.Renderer;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
|
||||
import android.os.Handler;
|
||||
@ -28,7 +28,6 @@ import android.os.Handler.Callback;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
@ -56,7 +55,7 @@ public final class MetadataRenderer<T> extends Renderer implements Callback {
|
||||
|
||||
private static final int MSG_INVOKE_RENDERER = 0;
|
||||
|
||||
private final MetadataParser<T> metadataParser;
|
||||
private final MetadataDecoder<T> metadataDecoder;
|
||||
private final Output<T> output;
|
||||
private final Handler outputHandler;
|
||||
private final FormatHolder formatHolder;
|
||||
@ -73,12 +72,13 @@ public final class MetadataRenderer<T> extends Renderer implements Callback {
|
||||
* normally be the looper associated with the application's main thread, which can be obtained
|
||||
* using {@link android.app.Activity#getMainLooper()}. Null may be passed if the output
|
||||
* should be invoked directly on the player's internal rendering thread.
|
||||
* @param metadataParser A parser for parsing the metadata.
|
||||
* @param metadataDecoder A decoder for the metadata.
|
||||
*/
|
||||
public MetadataRenderer(Output<T> output, Looper outputLooper, MetadataParser<T> metadataParser) {
|
||||
public MetadataRenderer(Output<T> output, Looper outputLooper,
|
||||
MetadataDecoder<T> metadataDecoder) {
|
||||
this.output = Assertions.checkNotNull(output);
|
||||
this.outputHandler = outputLooper == null ? null : new Handler(outputLooper, this);
|
||||
this.metadataParser = Assertions.checkNotNull(metadataParser);
|
||||
this.metadataDecoder = Assertions.checkNotNull(metadataDecoder);
|
||||
formatHolder = new FormatHolder();
|
||||
buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
|
||||
}
|
||||
@ -90,7 +90,7 @@ public final class MetadataRenderer<T> extends Renderer implements Callback {
|
||||
|
||||
@Override
|
||||
public int supportsFormat(Format format) {
|
||||
return metadataParser.canParse(format.sampleMimeType) ? Renderer.FORMAT_HANDLED
|
||||
return metadataDecoder.canDecode(format.sampleMimeType) ? Renderer.FORMAT_HANDLED
|
||||
: Renderer.FORMAT_UNSUPPORTED_TYPE;
|
||||
}
|
||||
|
||||
@ -113,8 +113,8 @@ public final class MetadataRenderer<T> extends Renderer implements Callback {
|
||||
try {
|
||||
buffer.flip();
|
||||
ByteBuffer bufferData = buffer.data;
|
||||
pendingMetadata = metadataParser.parse(bufferData.array(), bufferData.limit());
|
||||
} catch (IOException e) {
|
||||
pendingMetadata = metadataDecoder.decode(bufferData.array(), bufferData.limit());
|
||||
} catch (MetadataDecoderException e) {
|
||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,8 @@
|
||||
package com.google.android.exoplayer2.metadata.id3;
|
||||
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.metadata.MetadataParser;
|
||||
import com.google.android.exoplayer2.metadata.MetadataDecoder;
|
||||
import com.google.android.exoplayer2.metadata.MetadataDecoderException;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
@ -28,9 +29,9 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Extracts individual TXXX text frames from raw ID3 data.
|
||||
* Decodes individual TXXX text frames from raw ID3 data.
|
||||
*/
|
||||
public final class Id3Parser implements MetadataParser<List<Id3Frame>> {
|
||||
public final class Id3Decoder implements MetadataDecoder<List<Id3Frame>> {
|
||||
|
||||
private static final int ID3_TEXT_ENCODING_ISO_8859_1 = 0;
|
||||
private static final int ID3_TEXT_ENCODING_UTF_16 = 1;
|
||||
@ -38,15 +39,15 @@ public final class Id3Parser implements MetadataParser<List<Id3Frame>> {
|
||||
private static final int ID3_TEXT_ENCODING_UTF_8 = 3;
|
||||
|
||||
@Override
|
||||
public boolean canParse(String mimeType) {
|
||||
public boolean canDecode(String mimeType) {
|
||||
return mimeType.equals(MimeTypes.APPLICATION_ID3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Id3Frame> parse(byte[] data, int size) throws ParserException {
|
||||
public List<Id3Frame> decode(byte[] data, int size) throws MetadataDecoderException {
|
||||
List<Id3Frame> id3Frames = new ArrayList<>();
|
||||
ParsableByteArray id3Data = new ParsableByteArray(data, size);
|
||||
int id3Size = parseId3Header(id3Data);
|
||||
int id3Size = decodeId3Header(id3Data);
|
||||
|
||||
while (id3Size > 0) {
|
||||
int frameId0 = id3Data.readUnsignedByte();
|
||||
@ -64,24 +65,24 @@ public final class Id3Parser implements MetadataParser<List<Id3Frame>> {
|
||||
try {
|
||||
Id3Frame frame;
|
||||
if (frameId0 == 'T' && frameId1 == 'X' && frameId2 == 'X' && frameId3 == 'X') {
|
||||
frame = parseTxxxFrame(id3Data, frameSize);
|
||||
frame = decodeTxxxFrame(id3Data, frameSize);
|
||||
} else if (frameId0 == 'P' && frameId1 == 'R' && frameId2 == 'I' && frameId3 == 'V') {
|
||||
frame = parsePrivFrame(id3Data, frameSize);
|
||||
frame = decodePrivFrame(id3Data, frameSize);
|
||||
} else if (frameId0 == 'G' && frameId1 == 'E' && frameId2 == 'O' && frameId3 == 'B') {
|
||||
frame = parseGeobFrame(id3Data, frameSize);
|
||||
frame = decodeGeobFrame(id3Data, frameSize);
|
||||
} else if (frameId0 == 'A' && frameId1 == 'P' && frameId2 == 'I' && frameId3 == 'C') {
|
||||
frame = parseApicFrame(id3Data, frameSize);
|
||||
frame = decodeApicFrame(id3Data, frameSize);
|
||||
} else if (frameId0 == 'T') {
|
||||
String id = String.format(Locale.US, "%c%c%c%c", frameId0, frameId1, frameId2, frameId3);
|
||||
frame = parseTextInformationFrame(id3Data, frameSize, id);
|
||||
frame = decodeTextInformationFrame(id3Data, frameSize, id);
|
||||
} else {
|
||||
String id = String.format(Locale.US, "%c%c%c%c", frameId0, frameId1, frameId2, frameId3);
|
||||
frame = parseBinaryFrame(id3Data, frameSize, id);
|
||||
frame = decodeBinaryFrame(id3Data, frameSize, id);
|
||||
}
|
||||
id3Frames.add(frame);
|
||||
id3Size -= frameSize + 10 /* header size */;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new ParserException(e);
|
||||
throw new MetadataDecoderException("Unsupported encoding", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,18 +123,16 @@ public final class Id3Parser implements MetadataParser<List<Id3Frame>> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an ID3 header.
|
||||
*
|
||||
* @param id3Buffer A {@link ParsableByteArray} from which data should be read.
|
||||
* @return The size of ID3 frames in bytes, excluding the header and footer.
|
||||
* @throws ParserException If ID3 file identifier != "ID3".
|
||||
*/
|
||||
private static int parseId3Header(ParsableByteArray id3Buffer) throws ParserException {
|
||||
private static int decodeId3Header(ParsableByteArray id3Buffer) throws MetadataDecoderException {
|
||||
int id1 = id3Buffer.readUnsignedByte();
|
||||
int id2 = id3Buffer.readUnsignedByte();
|
||||
int id3 = id3Buffer.readUnsignedByte();
|
||||
if (id1 != 'I' || id2 != 'D' || id3 != '3') {
|
||||
throw new ParserException(String.format(Locale.US,
|
||||
throw new MetadataDecoderException(String.format(Locale.US,
|
||||
"Unexpected ID3 file identifier, expected \"ID3\", actual \"%c%c%c\".", id1, id2, id3));
|
||||
}
|
||||
id3Buffer.skipBytes(2); // Skip version.
|
||||
@ -158,7 +157,7 @@ public final class Id3Parser implements MetadataParser<List<Id3Frame>> {
|
||||
return id3Size;
|
||||
}
|
||||
|
||||
private static TxxxFrame parseTxxxFrame(ParsableByteArray id3Data, int frameSize)
|
||||
private static TxxxFrame decodeTxxxFrame(ParsableByteArray id3Data, int frameSize)
|
||||
throws UnsupportedEncodingException {
|
||||
int encoding = id3Data.readUnsignedByte();
|
||||
String charset = getCharsetName(encoding);
|
||||
@ -176,7 +175,7 @@ public final class Id3Parser implements MetadataParser<List<Id3Frame>> {
|
||||
return new TxxxFrame(description, value);
|
||||
}
|
||||
|
||||
private static PrivFrame parsePrivFrame(ParsableByteArray id3Data, int frameSize)
|
||||
private static PrivFrame decodePrivFrame(ParsableByteArray id3Data, int frameSize)
|
||||
throws UnsupportedEncodingException {
|
||||
byte[] data = new byte[frameSize];
|
||||
id3Data.readBytes(data, 0, frameSize);
|
||||
@ -190,7 +189,7 @@ public final class Id3Parser implements MetadataParser<List<Id3Frame>> {
|
||||
return new PrivFrame(owner, privateData);
|
||||
}
|
||||
|
||||
private static GeobFrame parseGeobFrame(ParsableByteArray id3Data, int frameSize)
|
||||
private static GeobFrame decodeGeobFrame(ParsableByteArray id3Data, int frameSize)
|
||||
throws UnsupportedEncodingException {
|
||||
int encoding = id3Data.readUnsignedByte();
|
||||
String charset = getCharsetName(encoding);
|
||||
@ -217,7 +216,7 @@ public final class Id3Parser implements MetadataParser<List<Id3Frame>> {
|
||||
return new GeobFrame(mimeType, filename, description, objectData);
|
||||
}
|
||||
|
||||
private static ApicFrame parseApicFrame(ParsableByteArray id3Data, int frameSize)
|
||||
private static ApicFrame decodeApicFrame(ParsableByteArray id3Data, int frameSize)
|
||||
throws UnsupportedEncodingException {
|
||||
int encoding = id3Data.readUnsignedByte();
|
||||
String charset = getCharsetName(encoding);
|
||||
@ -241,7 +240,7 @@ public final class Id3Parser implements MetadataParser<List<Id3Frame>> {
|
||||
return new ApicFrame(mimeType, description, pictureType, pictureData);
|
||||
}
|
||||
|
||||
private static TextInformationFrame parseTextInformationFrame(ParsableByteArray id3Data,
|
||||
private static TextInformationFrame decodeTextInformationFrame(ParsableByteArray id3Data,
|
||||
int frameSize, String id) throws UnsupportedEncodingException {
|
||||
int encoding = id3Data.readUnsignedByte();
|
||||
String charset = getCharsetName(encoding);
|
||||
@ -255,7 +254,8 @@ public final class Id3Parser implements MetadataParser<List<Id3Frame>> {
|
||||
return new TextInformationFrame(id, description);
|
||||
}
|
||||
|
||||
private static BinaryFrame parseBinaryFrame(ParsableByteArray id3Data, int frameSize, String id) {
|
||||
private static BinaryFrame decodeBinaryFrame(ParsableByteArray id3Data, int frameSize,
|
||||
String id) {
|
||||
byte[] frame = new byte[frameSize];
|
||||
id3Data.readBytes(frame, 0, frameSize);
|
||||
|
@ -16,10 +16,10 @@
|
||||
package com.google.android.exoplayer2.source;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.FormatHolder;
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
|
||||
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
|
||||
import com.google.android.exoplayer2.extractor.DefaultTrackOutput;
|
||||
|
@ -16,8 +16,8 @@
|
||||
package com.google.android.exoplayer2.source;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.FormatHolder;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -16,9 +16,9 @@
|
||||
package com.google.android.exoplayer2.source;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.FormatHolder;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
|
@ -16,9 +16,9 @@
|
||||
package com.google.android.exoplayer2.source.chunk;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.FormatHolder;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.extractor.DefaultTrackOutput;
|
||||
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher;
|
||||
import com.google.android.exoplayer2.source.SampleStream;
|
||||
|
@ -29,7 +29,7 @@ import com.google.android.exoplayer2.util.Util;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A {@link BaseMediaChunk} that uses an {@link Extractor} to parse sample data.
|
||||
* A {@link BaseMediaChunk} that uses an {@link Extractor} to decode sample data.
|
||||
*/
|
||||
public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMetadataOutput {
|
||||
|
||||
@ -109,7 +109,7 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe
|
||||
trackOutput.formatWithOffset(sampleFormat, sampleOffsetUs);
|
||||
extractorWrapper.init(this, trackOutput);
|
||||
}
|
||||
// Load and parse the sample data.
|
||||
// Load and decode the sample data.
|
||||
try {
|
||||
int result = Extractor.RESULT_CONTINUE;
|
||||
while (result == Extractor.RESULT_CONTINUE && !loadCanceled) {
|
||||
|
@ -31,7 +31,7 @@ import com.google.android.exoplayer2.util.Util;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A {@link Chunk} that uses an {@link Extractor} to parse initialization data for single track.
|
||||
* A {@link Chunk} that uses an {@link Extractor} to decode initialization data for single track.
|
||||
*/
|
||||
public final class InitializationChunk extends Chunk implements SingleTrackMetadataOutput,
|
||||
TrackOutput {
|
||||
@ -140,7 +140,7 @@ public final class InitializationChunk extends Chunk implements SingleTrackMetad
|
||||
// Set the target to ourselves.
|
||||
extractorWrapper.init(this, this);
|
||||
}
|
||||
// Load and parse the initialization data.
|
||||
// Load and decode the initialization data.
|
||||
try {
|
||||
int result = Extractor.RESULT_CONTINUE;
|
||||
while (result == Extractor.RESULT_CONTINUE && !loadCanceled) {
|
||||
|
@ -28,9 +28,9 @@ import com.google.android.exoplayer2.source.dash.mpd.SegmentBase.SingleSegmentBa
|
||||
import com.google.android.exoplayer2.upstream.ParsingLoadable;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.ParserUtil;
|
||||
import com.google.android.exoplayer2.util.UriUtil;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import com.google.android.exoplayer2.util.XmlPullParserUtil;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
@ -120,16 +120,16 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
boolean seenFirstBaseUrl = false;
|
||||
do {
|
||||
xpp.next();
|
||||
if (ParserUtil.isStartTag(xpp, "BaseURL")) {
|
||||
if (XmlPullParserUtil.isStartTag(xpp, "BaseURL")) {
|
||||
if (!seenFirstBaseUrl) {
|
||||
baseUrl = parseBaseUrl(xpp, baseUrl);
|
||||
seenFirstBaseUrl = true;
|
||||
}
|
||||
} else if (ParserUtil.isStartTag(xpp, "UTCTiming")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "UTCTiming")) {
|
||||
utcTiming = parseUtcTiming(xpp);
|
||||
} else if (ParserUtil.isStartTag(xpp, "Location")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "Location")) {
|
||||
location = Uri.parse(xpp.nextText());
|
||||
} else if (ParserUtil.isStartTag(xpp, "Period") && !seenEarlyAccessPeriod) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "Period") && !seenEarlyAccessPeriod) {
|
||||
Pair<Period, Long> periodWithDurationMs = parsePeriod(xpp, baseUrl, nextPeriodStartMs);
|
||||
Period period = periodWithDurationMs.first;
|
||||
if (period.startMs == -1) {
|
||||
@ -146,7 +146,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
periods.add(period);
|
||||
}
|
||||
}
|
||||
} while (!ParserUtil.isEndTag(xpp, "MPD"));
|
||||
} while (!XmlPullParserUtil.isEndTag(xpp, "MPD"));
|
||||
|
||||
if (durationMs == -1) {
|
||||
if (nextPeriodStartMs != -1) {
|
||||
@ -193,21 +193,21 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
boolean seenFirstBaseUrl = false;
|
||||
do {
|
||||
xpp.next();
|
||||
if (ParserUtil.isStartTag(xpp, "BaseURL")) {
|
||||
if (XmlPullParserUtil.isStartTag(xpp, "BaseURL")) {
|
||||
if (!seenFirstBaseUrl) {
|
||||
baseUrl = parseBaseUrl(xpp, baseUrl);
|
||||
seenFirstBaseUrl = true;
|
||||
}
|
||||
} else if (ParserUtil.isStartTag(xpp, "AdaptationSet")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "AdaptationSet")) {
|
||||
adaptationSets.add(parseAdaptationSet(xpp, baseUrl, segmentBase));
|
||||
} else if (ParserUtil.isStartTag(xpp, "SegmentBase")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) {
|
||||
segmentBase = parseSegmentBase(xpp, baseUrl, null);
|
||||
} else if (ParserUtil.isStartTag(xpp, "SegmentList")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentList")) {
|
||||
segmentBase = parseSegmentList(xpp, baseUrl, null);
|
||||
} else if (ParserUtil.isStartTag(xpp, "SegmentTemplate")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) {
|
||||
segmentBase = parseSegmentTemplate(xpp, baseUrl, null);
|
||||
}
|
||||
} while (!ParserUtil.isEndTag(xpp, "Period"));
|
||||
} while (!XmlPullParserUtil.isEndTag(xpp, "Period"));
|
||||
|
||||
return Pair.create(buildPeriod(id, startMs, adaptationSets), durationMs);
|
||||
}
|
||||
@ -237,37 +237,37 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
boolean seenFirstBaseUrl = false;
|
||||
do {
|
||||
xpp.next();
|
||||
if (ParserUtil.isStartTag(xpp, "BaseURL")) {
|
||||
if (XmlPullParserUtil.isStartTag(xpp, "BaseURL")) {
|
||||
if (!seenFirstBaseUrl) {
|
||||
baseUrl = parseBaseUrl(xpp, baseUrl);
|
||||
seenFirstBaseUrl = true;
|
||||
}
|
||||
} else if (ParserUtil.isStartTag(xpp, "ContentProtection")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "ContentProtection")) {
|
||||
SchemeData contentProtection = parseContentProtection(xpp);
|
||||
if (contentProtection != null) {
|
||||
drmSchemeDatas.add(contentProtection);
|
||||
}
|
||||
} else if (ParserUtil.isStartTag(xpp, "ContentComponent")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "ContentComponent")) {
|
||||
language = checkLanguageConsistency(language, xpp.getAttributeValue(null, "lang"));
|
||||
contentType = checkContentTypeConsistency(contentType, parseContentType(xpp));
|
||||
} else if (ParserUtil.isStartTag(xpp, "Representation")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "Representation")) {
|
||||
RepresentationInfo representationInfo = parseRepresentation(xpp, baseUrl, mimeType, codecs,
|
||||
width, height, frameRate, audioChannels, audioSamplingRate, language, segmentBase);
|
||||
contentType = checkContentTypeConsistency(contentType,
|
||||
getContentType(representationInfo.format));
|
||||
representationInfos.add(representationInfo);
|
||||
} else if (ParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) {
|
||||
audioChannels = parseAudioChannelConfiguration(xpp);
|
||||
} else if (ParserUtil.isStartTag(xpp, "SegmentBase")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) {
|
||||
segmentBase = parseSegmentBase(xpp, baseUrl, (SingleSegmentBase) segmentBase);
|
||||
} else if (ParserUtil.isStartTag(xpp, "SegmentList")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentList")) {
|
||||
segmentBase = parseSegmentList(xpp, baseUrl, (SegmentList) segmentBase);
|
||||
} else if (ParserUtil.isStartTag(xpp, "SegmentTemplate")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) {
|
||||
segmentBase = parseSegmentTemplate(xpp, baseUrl, (SegmentTemplate) segmentBase);
|
||||
} else if (ParserUtil.isStartTag(xpp)) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp)) {
|
||||
parseAdaptationSetChild(xpp);
|
||||
}
|
||||
} while (!ParserUtil.isEndTag(xpp, "AdaptationSet"));
|
||||
} while (!XmlPullParserUtil.isEndTag(xpp, "AdaptationSet"));
|
||||
|
||||
List<Representation> representations = new ArrayList<>(representationInfos.size());
|
||||
for (int i = 0; i < representationInfos.size(); i++) {
|
||||
@ -321,7 +321,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
do {
|
||||
xpp.next();
|
||||
// The cenc:pssh element is defined in 23001-7:2015.
|
||||
if (ParserUtil.isStartTag(xpp, "cenc:pssh") && xpp.next() == XmlPullParser.TEXT) {
|
||||
if (XmlPullParserUtil.isStartTag(xpp, "cenc:pssh") && xpp.next() == XmlPullParser.TEXT) {
|
||||
seenPsshElement = true;
|
||||
byte[] data = Base64.decode(xpp.getText(), Base64.DEFAULT);
|
||||
UUID uuid = PsshAtomUtil.parseUuid(data);
|
||||
@ -329,7 +329,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
schemeData = new SchemeData(uuid, MimeTypes.VIDEO_MP4, data);
|
||||
}
|
||||
}
|
||||
} while (!ParserUtil.isEndTag(xpp, "ContentProtection"));
|
||||
} while (!XmlPullParserUtil.isEndTag(xpp, "ContentProtection"));
|
||||
if (seenPsshElement && schemeData == null) {
|
||||
Log.w(TAG, "Skipped unsupported ContentProtection element");
|
||||
return null;
|
||||
@ -371,26 +371,26 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
boolean seenFirstBaseUrl = false;
|
||||
do {
|
||||
xpp.next();
|
||||
if (ParserUtil.isStartTag(xpp, "BaseURL")) {
|
||||
if (XmlPullParserUtil.isStartTag(xpp, "BaseURL")) {
|
||||
if (!seenFirstBaseUrl) {
|
||||
baseUrl = parseBaseUrl(xpp, baseUrl);
|
||||
seenFirstBaseUrl = true;
|
||||
}
|
||||
} else if (ParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) {
|
||||
audioChannels = parseAudioChannelConfiguration(xpp);
|
||||
} else if (ParserUtil.isStartTag(xpp, "SegmentBase")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) {
|
||||
segmentBase = parseSegmentBase(xpp, baseUrl, (SingleSegmentBase) segmentBase);
|
||||
} else if (ParserUtil.isStartTag(xpp, "SegmentList")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentList")) {
|
||||
segmentBase = parseSegmentList(xpp, baseUrl, (SegmentList) segmentBase);
|
||||
} else if (ParserUtil.isStartTag(xpp, "SegmentTemplate")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) {
|
||||
segmentBase = parseSegmentTemplate(xpp, baseUrl, (SegmentTemplate) segmentBase);
|
||||
} else if (ParserUtil.isStartTag(xpp, "ContentProtection")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "ContentProtection")) {
|
||||
SchemeData contentProtection = parseContentProtection(xpp);
|
||||
if (contentProtection != null) {
|
||||
drmSchemeDatas.add(contentProtection);
|
||||
}
|
||||
}
|
||||
} while (!ParserUtil.isEndTag(xpp, "Representation"));
|
||||
} while (!XmlPullParserUtil.isEndTag(xpp, "Representation"));
|
||||
|
||||
Format format = buildFormat(id, mimeType, width, height, frameRate, audioChannels,
|
||||
audioSamplingRate, bandwidth, adaptationSetLanguage, codecs);
|
||||
@ -453,10 +453,10 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
RangedUri initialization = parent != null ? parent.initialization : null;
|
||||
do {
|
||||
xpp.next();
|
||||
if (ParserUtil.isStartTag(xpp, "Initialization")) {
|
||||
if (XmlPullParserUtil.isStartTag(xpp, "Initialization")) {
|
||||
initialization = parseInitialization(xpp, baseUrl);
|
||||
}
|
||||
} while (!ParserUtil.isEndTag(xpp, "SegmentBase"));
|
||||
} while (!XmlPullParserUtil.isEndTag(xpp, "SegmentBase"));
|
||||
|
||||
return buildSingleSegmentBase(initialization, timescale, presentationTimeOffset, baseUrl,
|
||||
indexStart, indexLength);
|
||||
@ -483,17 +483,17 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
|
||||
do {
|
||||
xpp.next();
|
||||
if (ParserUtil.isStartTag(xpp, "Initialization")) {
|
||||
if (XmlPullParserUtil.isStartTag(xpp, "Initialization")) {
|
||||
initialization = parseInitialization(xpp, baseUrl);
|
||||
} else if (ParserUtil.isStartTag(xpp, "SegmentTimeline")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTimeline")) {
|
||||
timeline = parseSegmentTimeline(xpp);
|
||||
} else if (ParserUtil.isStartTag(xpp, "SegmentURL")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentURL")) {
|
||||
if (segments == null) {
|
||||
segments = new ArrayList<>();
|
||||
}
|
||||
segments.add(parseSegmentUrl(xpp, baseUrl));
|
||||
}
|
||||
} while (!ParserUtil.isEndTag(xpp, "SegmentList"));
|
||||
} while (!XmlPullParserUtil.isEndTag(xpp, "SegmentList"));
|
||||
|
||||
if (parent != null) {
|
||||
initialization = initialization != null ? initialization : parent.initialization;
|
||||
@ -530,12 +530,12 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
|
||||
do {
|
||||
xpp.next();
|
||||
if (ParserUtil.isStartTag(xpp, "Initialization")) {
|
||||
if (XmlPullParserUtil.isStartTag(xpp, "Initialization")) {
|
||||
initialization = parseInitialization(xpp, baseUrl);
|
||||
} else if (ParserUtil.isStartTag(xpp, "SegmentTimeline")) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTimeline")) {
|
||||
timeline = parseSegmentTimeline(xpp);
|
||||
}
|
||||
} while (!ParserUtil.isEndTag(xpp, "SegmentTemplate"));
|
||||
} while (!XmlPullParserUtil.isEndTag(xpp, "SegmentTemplate"));
|
||||
|
||||
if (parent != null) {
|
||||
initialization = initialization != null ? initialization : parent.initialization;
|
||||
@ -560,7 +560,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
long elapsedTime = 0;
|
||||
do {
|
||||
xpp.next();
|
||||
if (ParserUtil.isStartTag(xpp, "S")) {
|
||||
if (XmlPullParserUtil.isStartTag(xpp, "S")) {
|
||||
elapsedTime = parseLong(xpp, "t", elapsedTime);
|
||||
long duration = parseLong(xpp, "d");
|
||||
int count = 1 + parseInt(xpp, "r", 0);
|
||||
@ -569,7 +569,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
elapsedTime += duration;
|
||||
}
|
||||
}
|
||||
} while (!ParserUtil.isEndTag(xpp, "SegmentTimeline"));
|
||||
} while (!XmlPullParserUtil.isEndTag(xpp, "SegmentTimeline"));
|
||||
return segmentTimeline;
|
||||
}
|
||||
|
||||
@ -628,7 +628,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
}
|
||||
do {
|
||||
xpp.next();
|
||||
} while (!ParserUtil.isEndTag(xpp, "AudioChannelConfiguration"));
|
||||
} while (!XmlPullParserUtil.isEndTag(xpp, "AudioChannelConfiguration"));
|
||||
return audioChannels;
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
* @param endTimeUs The end time of the media contained by the chunk, in microseconds.
|
||||
* @param chunkIndex The index of the chunk.
|
||||
* @param discontinuitySequenceNumber The discontinuity sequence number of the chunk.
|
||||
* @param extractor The extractor to parse samples from the data.
|
||||
* @param extractor The extractor to decode samples from the data.
|
||||
* @param extractorNeedsInit Whether the extractor needs initializing with the target
|
||||
* {@link HlsSampleStreamWrapper}.
|
||||
* @param shouldSpliceIn Whether the samples parsed from this chunk should be spliced into any
|
||||
|
@ -16,9 +16,9 @@
|
||||
package com.google.android.exoplayer2.source.hls;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.FormatHolder;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.extractor.DefaultTrackOutput;
|
||||
import com.google.android.exoplayer2.extractor.DefaultTrackOutput.UpstreamFormatChangedListener;
|
||||
import com.google.android.exoplayer2.extractor.ExtractorOutput;
|
||||
|
@ -25,6 +25,7 @@ import com.google.android.exoplayer2.extractor.PositionHolder;
|
||||
import com.google.android.exoplayer2.extractor.SeekMap;
|
||||
import com.google.android.exoplayer2.extractor.TrackOutput;
|
||||
import com.google.android.exoplayer2.extractor.ts.PtsTimestampAdjuster;
|
||||
import com.google.android.exoplayer2.text.TextDecoderException;
|
||||
import com.google.android.exoplayer2.text.webvtt.WebvttParserUtil;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
@ -119,7 +120,11 @@ import java.util.regex.Pattern;
|
||||
ParsableByteArray webvttData = new ParsableByteArray(sampleData);
|
||||
|
||||
// Validate the first line of the header.
|
||||
try {
|
||||
WebvttParserUtil.validateWebvttHeaderLine(webvttData);
|
||||
} catch (TextDecoderException e) {
|
||||
throw new ParserException(e);
|
||||
}
|
||||
|
||||
// Defaults to use if the header doesn't contain an X-TIMESTAMP-MAP header.
|
||||
long vttTimestampUs = 0;
|
||||
|
@ -15,21 +15,20 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.text;
|
||||
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.extensions.SimpleDecoder;
|
||||
import com.google.android.exoplayer2.decoder.SimpleDecoder;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Base class for subtitle parsers that use their own decode thread.
|
||||
*/
|
||||
public abstract class SimpleSubtitleParser extends
|
||||
SimpleDecoder<SubtitleInputBuffer, SubtitleOutputBuffer, ParserException> implements
|
||||
SubtitleParser {
|
||||
public abstract class SimpleSubtitleDecoder extends
|
||||
SimpleDecoder<SubtitleInputBuffer, SubtitleOutputBuffer, TextDecoderException> implements
|
||||
SubtitleDecoder {
|
||||
|
||||
private final String name;
|
||||
|
||||
protected SimpleSubtitleParser(String name) {
|
||||
protected SimpleSubtitleDecoder(String name) {
|
||||
super(new SubtitleInputBuffer[2], new SubtitleOutputBuffer[2]);
|
||||
this.name = name;
|
||||
setInitialInputBufferSize(1024);
|
||||
@ -61,14 +60,14 @@ public abstract class SimpleSubtitleParser extends
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final ParserException decode(SubtitleInputBuffer inputBuffer,
|
||||
protected final TextDecoderException decode(SubtitleInputBuffer inputBuffer,
|
||||
SubtitleOutputBuffer outputBuffer, boolean reset) {
|
||||
try {
|
||||
ByteBuffer inputData = inputBuffer.data;
|
||||
Subtitle subtitle = decode(inputData.array(), inputData.limit());
|
||||
outputBuffer.setOutput(inputBuffer.timeUs, subtitle, inputBuffer.subsampleOffsetUs);
|
||||
return null;
|
||||
} catch (ParserException e) {
|
||||
} catch (TextDecoderException e) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
@ -79,8 +78,8 @@ public abstract class SimpleSubtitleParser extends
|
||||
* @param data The data to be decoded.
|
||||
* @param size The size of the data.
|
||||
* @return A {@link Subtitle} to rendered.
|
||||
* @throws ParserException A parsing exception.
|
||||
* @throws TextDecoderException If a decoding error occurs.
|
||||
*/
|
||||
protected abstract Subtitle decode(byte[] data, int size) throws ParserException;
|
||||
protected abstract Subtitle decode(byte[] data, int size) throws TextDecoderException;
|
||||
|
||||
}
|
@ -16,13 +16,13 @@
|
||||
package com.google.android.exoplayer2.text;
|
||||
|
||||
/**
|
||||
* A {@link Subtitle} output from a subtitle parser that extends {@link SimpleSubtitleParser}.
|
||||
* A {@link Subtitle} output from a subtitle parser that extends {@link SimpleSubtitleDecoder}.
|
||||
*/
|
||||
public final class SimpleSubtitleOutputBuffer extends SubtitleOutputBuffer {
|
||||
|
||||
private SimpleSubtitleParser owner;
|
||||
private SimpleSubtitleDecoder owner;
|
||||
|
||||
public SimpleSubtitleOutputBuffer(SimpleSubtitleParser owner) {
|
||||
public SimpleSubtitleOutputBuffer(SimpleSubtitleDecoder owner) {
|
||||
super();
|
||||
this.owner = owner;
|
||||
}
|
||||
|
@ -15,14 +15,13 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.text;
|
||||
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.extensions.Decoder;
|
||||
import com.google.android.exoplayer2.decoder.Decoder;
|
||||
|
||||
/**
|
||||
* Parses {@link Subtitle}s from {@link SubtitleInputBuffer}s.
|
||||
*/
|
||||
public interface SubtitleParser extends
|
||||
Decoder<SubtitleInputBuffer, SubtitleOutputBuffer, ParserException> {
|
||||
public interface SubtitleDecoder extends
|
||||
Decoder<SubtitleInputBuffer, SubtitleOutputBuffer, TextDecoderException> {
|
||||
|
||||
/**
|
||||
* Informs the parser of the current playback position.
|
@ -16,13 +16,19 @@
|
||||
package com.google.android.exoplayer2.text;
|
||||
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.extensions.Decoder;
|
||||
import com.google.android.exoplayer2.decoder.Decoder;
|
||||
import com.google.android.exoplayer2.text.eia608.Eia608Decoder;
|
||||
import com.google.android.exoplayer2.text.subrip.SubripDecoder;
|
||||
import com.google.android.exoplayer2.text.ttml.TtmlDecoder;
|
||||
import com.google.android.exoplayer2.text.tx3g.Tx3gDecoder;
|
||||
import com.google.android.exoplayer2.text.webvtt.Mp4WebvttDecoder;
|
||||
import com.google.android.exoplayer2.text.webvtt.WebvttDecoder;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
|
||||
/**
|
||||
* A factory for {@link Decoder} instances that will parse subtitles.
|
||||
* A factory for {@link Decoder} instances that will decode subtitles.
|
||||
*/
|
||||
public interface SubtitleParserFactory {
|
||||
public interface SubtitleDecoderFactory {
|
||||
|
||||
/**
|
||||
* Returns whether the factory is able to instantiate a {@link Decoder} for the given
|
||||
@ -40,56 +46,56 @@ public interface SubtitleParserFactory {
|
||||
* @return A new {@link Decoder}.
|
||||
* @throws IllegalArgumentException If the {@link Format} is not supported.
|
||||
*/
|
||||
SubtitleParser createParser(Format format);
|
||||
SubtitleDecoder createDecoder(Format format);
|
||||
|
||||
/**
|
||||
* Default {@link SubtitleParserFactory} implementation.
|
||||
* Default {@link SubtitleDecoderFactory} implementation.
|
||||
* <p>
|
||||
* The formats supported by this factory are:
|
||||
* <ul>
|
||||
* <li>WebVTT ({@link com.google.android.exoplayer2.text.webvtt.WebvttParser})</li>
|
||||
* <li>WebVTT (MP4) ({@link com.google.android.exoplayer2.text.webvtt.Mp4WebvttParser})</li>
|
||||
* <li>TTML ({@link com.google.android.exoplayer2.text.ttml.TtmlParser})</li>
|
||||
* <li>SubRip ({@link com.google.android.exoplayer2.text.subrip.SubripParser})</li>
|
||||
* <li>TX3G ({@link com.google.android.exoplayer2.text.tx3g.Tx3gParser})</li>
|
||||
* <li>Eia608 ({@link com.google.android.exoplayer2.text.eia608.Eia608Parser})</li>
|
||||
* <li>WebVTT ({@link WebvttDecoder})</li>
|
||||
* <li>WebVTT (MP4) ({@link Mp4WebvttDecoder})</li>
|
||||
* <li>TTML ({@link TtmlDecoder})</li>
|
||||
* <li>SubRip ({@link SubripDecoder})</li>
|
||||
* <li>TX3G ({@link Tx3gDecoder})</li>
|
||||
* <li>Eia608 ({@link Eia608Decoder})</li>
|
||||
* </ul>
|
||||
*/
|
||||
SubtitleParserFactory DEFAULT = new SubtitleParserFactory() {
|
||||
SubtitleDecoderFactory DEFAULT = new SubtitleDecoderFactory() {
|
||||
|
||||
@Override
|
||||
public boolean supportsFormat(Format format) {
|
||||
return getParserClass(format.sampleMimeType) != null;
|
||||
return getDecoderClass(format.sampleMimeType) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubtitleParser createParser(Format format) {
|
||||
public SubtitleDecoder createDecoder(Format format) {
|
||||
try {
|
||||
Class<?> clazz = getParserClass(format.sampleMimeType);
|
||||
Class<?> clazz = getDecoderClass(format.sampleMimeType);
|
||||
if (clazz == null) {
|
||||
throw new IllegalArgumentException("Attempted to create parser for unsupported format");
|
||||
throw new IllegalArgumentException("Attempted to create decoder for unsupported format");
|
||||
}
|
||||
return clazz.asSubclass(SubtitleParser.class).newInstance();
|
||||
return clazz.asSubclass(SubtitleDecoder.class).getConstructor().newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Unexpected error instantiating parser", e);
|
||||
throw new IllegalStateException("Unexpected error instantiating decoder", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> getParserClass(String mimeType) {
|
||||
private Class<?> getDecoderClass(String mimeType) {
|
||||
try {
|
||||
switch (mimeType) {
|
||||
case MimeTypes.TEXT_VTT:
|
||||
return Class.forName("com.google.android.exoplayer2.text.webvtt.WebvttParser");
|
||||
return Class.forName("com.google.android.exoplayer2.text.webvtt.WebvttDecoder");
|
||||
case MimeTypes.APPLICATION_TTML:
|
||||
return Class.forName("com.google.android.exoplayer2.text.ttml.TtmlParser");
|
||||
return Class.forName("com.google.android.exoplayer2.text.ttml.TtmlDecoder");
|
||||
case MimeTypes.APPLICATION_MP4VTT:
|
||||
return Class.forName("com.google.android.exoplayer2.text.webvtt.Mp4WebvttParser");
|
||||
return Class.forName("com.google.android.exoplayer2.text.webvtt.Mp4WebvttDecoder");
|
||||
case MimeTypes.APPLICATION_SUBRIP:
|
||||
return Class.forName("com.google.android.exoplayer2.text.subrip.SubripParser");
|
||||
return Class.forName("com.google.android.exoplayer2.text.subrip.SubripDecoder");
|
||||
case MimeTypes.APPLICATION_TX3G:
|
||||
return Class.forName("com.google.android.exoplayer2.text.tx3g.Tx3gParser");
|
||||
return Class.forName("com.google.android.exoplayer2.text.tx3g.Tx3gDecoder");
|
||||
case MimeTypes.APPLICATION_EIA608:
|
||||
return Class.forName("com.google.android.exoplayer2.text.eia608.Eia608Parser");
|
||||
return Class.forName("com.google.android.exoplayer2.text.eia608.Eia608Decoder");
|
||||
default:
|
||||
return null;
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.text;
|
||||
|
||||
import com.google.android.exoplayer2.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
|
||||
/**
|
||||
* An input buffer for a subtitle parser.
|
||||
|
@ -16,7 +16,7 @@
|
||||
package com.google.android.exoplayer2.text;
|
||||
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.extensions.OutputBuffer;
|
||||
import com.google.android.exoplayer2.decoder.OutputBuffer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.exoplayer2.text;
|
||||
|
||||
/**
|
||||
* Thrown when an error occurs decoding text data.
|
||||
*/
|
||||
public class TextDecoderException extends Exception {
|
||||
|
||||
public TextDecoderException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public TextDecoderException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
@ -19,9 +19,7 @@ import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.FormatHolder;
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.Renderer;
|
||||
import com.google.android.exoplayer2.extensions.Decoder;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
|
||||
@ -31,15 +29,14 @@ import android.os.Handler.Callback;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A {@link Renderer} for subtitles.
|
||||
* <p>
|
||||
* Text is parsed from sample data using {@link Decoder} instances obtained from a
|
||||
* {@link SubtitleParserFactory}. The actual rendering of each line of text is delegated to a
|
||||
* Text is parsed from sample data using {@link SubtitleDecoder} instances obtained from a
|
||||
* {@link SubtitleDecoderFactory}. The actual rendering of each line of text is delegated to a
|
||||
* {@link Output}.
|
||||
*/
|
||||
@TargetApi(16)
|
||||
@ -63,12 +60,12 @@ public final class TextRenderer extends Renderer implements Callback {
|
||||
|
||||
private final Handler outputHandler;
|
||||
private final Output output;
|
||||
private final SubtitleParserFactory parserFactory;
|
||||
private final SubtitleDecoderFactory decoderFactory;
|
||||
private final FormatHolder formatHolder;
|
||||
|
||||
private boolean inputStreamEnded;
|
||||
private boolean outputStreamEnded;
|
||||
private SubtitleParser parser;
|
||||
private SubtitleDecoder decoder;
|
||||
private SubtitleInputBuffer nextInputBuffer;
|
||||
private SubtitleOutputBuffer subtitle;
|
||||
private SubtitleOutputBuffer nextSubtitle;
|
||||
@ -83,7 +80,7 @@ public final class TextRenderer extends Renderer implements Callback {
|
||||
* should be invoked directly on the player's internal rendering thread.
|
||||
*/
|
||||
public TextRenderer(Output output, Looper outputLooper) {
|
||||
this(output, outputLooper, SubtitleParserFactory.DEFAULT);
|
||||
this(output, outputLooper, SubtitleDecoderFactory.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -93,12 +90,12 @@ public final class TextRenderer extends Renderer implements Callback {
|
||||
* normally be the looper associated with the application's main thread, which can be obtained
|
||||
* using {@link android.app.Activity#getMainLooper()}. Null may be passed if the output
|
||||
* should be invoked directly on the player's internal rendering thread.
|
||||
* @param parserFactory A factory from which to obtain {@link Decoder} instances.
|
||||
* @param decoderFactory A factory from which to obtain {@link SubtitleDecoder} instances.
|
||||
*/
|
||||
public TextRenderer(Output output, Looper outputLooper, SubtitleParserFactory parserFactory) {
|
||||
public TextRenderer(Output output, Looper outputLooper, SubtitleDecoderFactory decoderFactory) {
|
||||
this.output = Assertions.checkNotNull(output);
|
||||
this.outputHandler = outputLooper == null ? null : new Handler(outputLooper, this);
|
||||
this.parserFactory = parserFactory;
|
||||
this.decoderFactory = decoderFactory;
|
||||
formatHolder = new FormatHolder();
|
||||
}
|
||||
|
||||
@ -109,17 +106,17 @@ public final class TextRenderer extends Renderer implements Callback {
|
||||
|
||||
@Override
|
||||
public int supportsFormat(Format format) {
|
||||
return parserFactory.supportsFormat(format) ? Renderer.FORMAT_HANDLED
|
||||
return decoderFactory.supportsFormat(format) ? Renderer.FORMAT_HANDLED
|
||||
: (MimeTypes.isText(format.sampleMimeType) ? FORMAT_UNSUPPORTED_SUBTYPE
|
||||
: FORMAT_UNSUPPORTED_TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStreamChanged(Format[] formats) throws ExoPlaybackException {
|
||||
if (parser != null) {
|
||||
parser.release();
|
||||
if (decoder != null) {
|
||||
decoder.release();
|
||||
}
|
||||
parser = parserFactory.createParser(formats[0]);
|
||||
decoder = decoderFactory.createDecoder(formats[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -136,7 +133,7 @@ public final class TextRenderer extends Renderer implements Callback {
|
||||
}
|
||||
nextInputBuffer = null;
|
||||
clearOutput();
|
||||
parser.flush();
|
||||
decoder.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -146,10 +143,10 @@ public final class TextRenderer extends Renderer implements Callback {
|
||||
}
|
||||
|
||||
if (nextSubtitle == null) {
|
||||
parser.setPositionUs(positionUs);
|
||||
decoder.setPositionUs(positionUs);
|
||||
try {
|
||||
nextSubtitle = parser.dequeueOutputBuffer();
|
||||
} catch (IOException e) {
|
||||
nextSubtitle = decoder.dequeueOutputBuffer();
|
||||
} catch (TextDecoderException e) {
|
||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||
}
|
||||
}
|
||||
@ -195,7 +192,7 @@ public final class TextRenderer extends Renderer implements Callback {
|
||||
try {
|
||||
while (!inputStreamEnded) {
|
||||
if (nextInputBuffer == null) {
|
||||
nextInputBuffer = parser.dequeueInputBuffer();
|
||||
nextInputBuffer = decoder.dequeueInputBuffer();
|
||||
if (nextInputBuffer == null) {
|
||||
return;
|
||||
}
|
||||
@ -211,13 +208,13 @@ public final class TextRenderer extends Renderer implements Callback {
|
||||
nextInputBuffer.subsampleOffsetUs = formatHolder.format.subsampleOffsetUs;
|
||||
nextInputBuffer.flip();
|
||||
}
|
||||
parser.queueInputBuffer(nextInputBuffer);
|
||||
decoder.queueInputBuffer(nextInputBuffer);
|
||||
nextInputBuffer = null;
|
||||
} else if (result == C.RESULT_NOTHING_READ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (ParserException e) {
|
||||
} catch (TextDecoderException e) {
|
||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||
}
|
||||
}
|
||||
@ -232,8 +229,8 @@ public final class TextRenderer extends Renderer implements Callback {
|
||||
nextSubtitle.release();
|
||||
nextSubtitle = null;
|
||||
}
|
||||
parser.release();
|
||||
parser = null;
|
||||
decoder.release();
|
||||
decoder = null;
|
||||
nextInputBuffer = null;
|
||||
clearOutput();
|
||||
super.onDisabled();
|
||||
|
@ -16,10 +16,10 @@
|
||||
package com.google.android.exoplayer2.text.eia608;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.text.SubtitleDecoder;
|
||||
import com.google.android.exoplayer2.text.SubtitleInputBuffer;
|
||||
import com.google.android.exoplayer2.text.SubtitleOutputBuffer;
|
||||
import com.google.android.exoplayer2.text.SubtitleParser;
|
||||
import com.google.android.exoplayer2.text.TextDecoderException;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
@ -29,10 +29,9 @@ import java.util.LinkedList;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Facilitates the extraction and parsing of EIA-608 (a.k.a. "line 21 captions" and "CEA-608")
|
||||
* Closed Captions from the SEI data block from H.264.
|
||||
* A {@link SubtitleDecoder} for EIA-608 (also known as "line 21 captions" and "CEA-608").
|
||||
*/
|
||||
public final class Eia608Parser implements SubtitleParser {
|
||||
public final class Eia608Decoder implements SubtitleDecoder {
|
||||
|
||||
private static final int NUM_INPUT_BUFFERS = 10;
|
||||
private static final int NUM_OUTPUT_BUFFERS = 2;
|
||||
@ -184,7 +183,7 @@ public final class Eia608Parser implements SubtitleParser {
|
||||
private byte repeatableControlCc1;
|
||||
private byte repeatableControlCc2;
|
||||
|
||||
public Eia608Parser() {
|
||||
public Eia608Decoder() {
|
||||
availableInputBuffers = new LinkedList<>();
|
||||
for (int i = 0; i < NUM_INPUT_BUFFERS; i++) {
|
||||
availableInputBuffers.add(new SubtitleInputBuffer());
|
||||
@ -205,7 +204,7 @@ public final class Eia608Parser implements SubtitleParser {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Eia608Parser";
|
||||
return "Eia608Decoder";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -214,7 +213,7 @@ public final class Eia608Parser implements SubtitleParser {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubtitleInputBuffer dequeueInputBuffer() throws ParserException {
|
||||
public SubtitleInputBuffer dequeueInputBuffer() throws TextDecoderException {
|
||||
Assertions.checkState(dequeuedInputBuffer == null);
|
||||
if (availableInputBuffers.isEmpty()) {
|
||||
return null;
|
||||
@ -224,7 +223,7 @@ public final class Eia608Parser implements SubtitleParser {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queueInputBuffer(SubtitleInputBuffer inputBuffer) throws ParserException {
|
||||
public void queueInputBuffer(SubtitleInputBuffer inputBuffer) throws TextDecoderException {
|
||||
Assertions.checkArgument(inputBuffer != null);
|
||||
Assertions.checkArgument(inputBuffer == dequeuedInputBuffer);
|
||||
queuedInputBuffers.add(inputBuffer);
|
||||
@ -232,7 +231,7 @@ public final class Eia608Parser implements SubtitleParser {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubtitleOutputBuffer dequeueOutputBuffer() throws ParserException {
|
||||
public SubtitleOutputBuffer dequeueOutputBuffer() throws TextDecoderException {
|
||||
if (availableOutputBuffers.isEmpty()) {
|
||||
return null;
|
||||
}
|
@ -19,13 +19,13 @@ import com.google.android.exoplayer2.text.Subtitle;
|
||||
import com.google.android.exoplayer2.text.SubtitleOutputBuffer;
|
||||
|
||||
/**
|
||||
* A {@link Subtitle} output from an {@link Eia608Parser}.
|
||||
* A {@link Subtitle} output from an {@link Eia608Decoder}.
|
||||
*/
|
||||
public final class Eia608SubtitleOutputBuffer extends SubtitleOutputBuffer {
|
||||
|
||||
private Eia608Parser owner;
|
||||
private Eia608Decoder owner;
|
||||
|
||||
public Eia608SubtitleOutputBuffer(Eia608Parser owner) {
|
||||
public Eia608SubtitleOutputBuffer(Eia608Decoder owner) {
|
||||
super();
|
||||
this.owner = owner;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
package com.google.android.exoplayer2.text.subrip;
|
||||
|
||||
import com.google.android.exoplayer2.text.Cue;
|
||||
import com.google.android.exoplayer2.text.SimpleSubtitleParser;
|
||||
import com.google.android.exoplayer2.text.SimpleSubtitleDecoder;
|
||||
import com.google.android.exoplayer2.util.LongArray;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
@ -30,11 +30,11 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A simple SubRip parser.
|
||||
* A {@link SimpleSubtitleDecoder} for SubRip.
|
||||
*/
|
||||
public final class SubripParser extends SimpleSubtitleParser {
|
||||
public final class SubripDecoder extends SimpleSubtitleDecoder {
|
||||
|
||||
private static final String TAG = "SubripParser";
|
||||
private static final String TAG = "SubripDecoder";
|
||||
|
||||
private static final Pattern SUBRIP_TIMING_LINE = Pattern.compile("(\\S*)\\s*-->\\s*(\\S*)");
|
||||
private static final Pattern SUBRIP_TIMESTAMP =
|
||||
@ -42,8 +42,8 @@ public final class SubripParser extends SimpleSubtitleParser {
|
||||
|
||||
private final StringBuilder textBuilder;
|
||||
|
||||
public SubripParser() {
|
||||
super("SubripParser");
|
||||
public SubripDecoder() {
|
||||
super("SubripDecoder");
|
||||
textBuilder = new StringBuilder();
|
||||
}
|
||||
|
@ -16,12 +16,12 @@
|
||||
package com.google.android.exoplayer2.text.ttml;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.text.Cue;
|
||||
import com.google.android.exoplayer2.text.SimpleSubtitleParser;
|
||||
import com.google.android.exoplayer2.text.SimpleSubtitleDecoder;
|
||||
import com.google.android.exoplayer2.text.TextDecoderException;
|
||||
import com.google.android.exoplayer2.util.ColorParser;
|
||||
import com.google.android.exoplayer2.util.ParserUtil;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import com.google.android.exoplayer2.util.XmlPullParserUtil;
|
||||
|
||||
import android.text.Layout;
|
||||
import android.util.Log;
|
||||
@ -40,7 +40,7 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A simple TTML parser that supports DFXP presentation profile.
|
||||
* A {@link SimpleSubtitleDecoder} for TTML supporting the DFXP presentation profile.
|
||||
* <p>
|
||||
* Supported features in this parser are:
|
||||
* <ul>
|
||||
@ -60,9 +60,9 @@ import java.util.regex.Pattern;
|
||||
* </p>
|
||||
* @see <a href="http://www.w3.org/TR/ttaf1-dfxp/">TTML specification</a>
|
||||
*/
|
||||
public final class TtmlParser extends SimpleSubtitleParser {
|
||||
public final class TtmlDecoder extends SimpleSubtitleDecoder {
|
||||
|
||||
private static final String TAG = "TtmlParser";
|
||||
private static final String TAG = "TtmlDecoder";
|
||||
|
||||
private static final String TTP = "http://www.w3.org/ns/ttml#parameter";
|
||||
|
||||
@ -88,8 +88,8 @@ public final class TtmlParser extends SimpleSubtitleParser {
|
||||
|
||||
private final XmlPullParserFactory xmlParserFactory;
|
||||
|
||||
public TtmlParser() {
|
||||
super("TtmlParser");
|
||||
public TtmlDecoder() {
|
||||
super("TtmlDecoder");
|
||||
try {
|
||||
xmlParserFactory = XmlPullParserFactory.newInstance();
|
||||
xmlParserFactory.setNamespaceAware(true);
|
||||
@ -99,7 +99,7 @@ public final class TtmlParser extends SimpleSubtitleParser {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TtmlSubtitle decode(byte[] bytes, int length) throws ParserException {
|
||||
protected TtmlSubtitle decode(byte[] bytes, int length) throws TextDecoderException {
|
||||
try {
|
||||
XmlPullParser xmlParser = xmlParserFactory.newPullParser();
|
||||
Map<String, TtmlStyle> globalStyles = new HashMap<>();
|
||||
@ -132,7 +132,7 @@ public final class TtmlParser extends SimpleSubtitleParser {
|
||||
if (parent != null) {
|
||||
parent.addChild(node);
|
||||
}
|
||||
} catch (ParserException e) {
|
||||
} catch (TextDecoderException e) {
|
||||
Log.w(TAG, "Suppressing parser error", e);
|
||||
// Treat the node (and by extension, all of its children) as unsupported.
|
||||
unsupportedNodeDepth++;
|
||||
@ -158,13 +158,14 @@ public final class TtmlParser extends SimpleSubtitleParser {
|
||||
}
|
||||
return ttmlSubtitle;
|
||||
} catch (XmlPullParserException xppe) {
|
||||
throw new ParserException("Unable to parse source", xppe);
|
||||
throw new TextDecoderException("Unable to decode source", xppe);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Unexpected error when reading input.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private FrameAndTickRate parseFrameAndTickRates(XmlPullParser xmlParser) throws ParserException {
|
||||
private FrameAndTickRate parseFrameAndTickRates(XmlPullParser xmlParser)
|
||||
throws TextDecoderException {
|
||||
int frameRate = DEFAULT_FRAME_RATE;
|
||||
String frameRateString = xmlParser.getAttributeValue(TTP, "frameRate");
|
||||
if (frameRateString != null) {
|
||||
@ -176,7 +177,7 @@ public final class TtmlParser extends SimpleSubtitleParser {
|
||||
if (frameRateMultiplierString != null) {
|
||||
String[] parts = frameRateMultiplierString.split(" ");
|
||||
if (parts.length != 2) {
|
||||
throw new ParserException("frameRateMultiplier doesn't have 2 parts");
|
||||
throw new TextDecoderException("frameRateMultiplier doesn't have 2 parts");
|
||||
}
|
||||
float numerator = Integer.parseInt(parts[0]);
|
||||
float denominator = Integer.parseInt(parts[1]);
|
||||
@ -202,8 +203,8 @@ public final class TtmlParser extends SimpleSubtitleParser {
|
||||
throws IOException, XmlPullParserException {
|
||||
do {
|
||||
xmlParser.next();
|
||||
if (ParserUtil.isStartTag(xmlParser, TtmlNode.TAG_STYLE)) {
|
||||
String parentStyleId = ParserUtil.getAttributeValue(xmlParser, ATTR_STYLE);
|
||||
if (XmlPullParserUtil.isStartTag(xmlParser, TtmlNode.TAG_STYLE)) {
|
||||
String parentStyleId = XmlPullParserUtil.getAttributeValue(xmlParser, ATTR_STYLE);
|
||||
TtmlStyle style = parseStyleAttributes(xmlParser, new TtmlStyle());
|
||||
if (parentStyleId != null) {
|
||||
for (String id : parseStyleIds(parentStyleId)) {
|
||||
@ -213,13 +214,13 @@ public final class TtmlParser extends SimpleSubtitleParser {
|
||||
if (style.getId() != null) {
|
||||
globalStyles.put(style.getId(), style);
|
||||
}
|
||||
} else if (ParserUtil.isStartTag(xmlParser, TtmlNode.TAG_REGION)) {
|
||||
} else if (XmlPullParserUtil.isStartTag(xmlParser, TtmlNode.TAG_REGION)) {
|
||||
Pair<String, TtmlRegion> ttmlRegionInfo = parseRegionAttributes(xmlParser);
|
||||
if (ttmlRegionInfo != null) {
|
||||
globalRegions.put(ttmlRegionInfo.first, ttmlRegionInfo.second);
|
||||
}
|
||||
}
|
||||
} while (!ParserUtil.isEndTag(xmlParser, TtmlNode.TAG_HEAD));
|
||||
} while (!XmlPullParserUtil.isEndTag(xmlParser, TtmlNode.TAG_HEAD));
|
||||
return globalStyles;
|
||||
}
|
||||
|
||||
@ -228,9 +229,9 @@ public final class TtmlParser extends SimpleSubtitleParser {
|
||||
* terms of percentage of the viewport. Regions that do not correctly declare origin are ignored.
|
||||
*/
|
||||
private Pair<String, TtmlRegion> parseRegionAttributes(XmlPullParser xmlParser) {
|
||||
String regionId = ParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_ID);
|
||||
String regionOrigin = ParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_ORIGIN);
|
||||
String regionExtent = ParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_EXTENT);
|
||||
String regionId = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_ID);
|
||||
String regionOrigin = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_ORIGIN);
|
||||
String regionExtent = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_EXTENT);
|
||||
if (regionOrigin == null || regionId == null) {
|
||||
return null;
|
||||
}
|
||||
@ -298,7 +299,7 @@ public final class TtmlParser extends SimpleSubtitleParser {
|
||||
try {
|
||||
style = createIfNull(style);
|
||||
parseFontSize(attributeValue, style);
|
||||
} catch (ParserException e) {
|
||||
} catch (TextDecoderException e) {
|
||||
Log.w(TAG, "failed parsing fontSize value: '" + attributeValue + "'");
|
||||
}
|
||||
break;
|
||||
@ -358,7 +359,8 @@ public final class TtmlParser extends SimpleSubtitleParser {
|
||||
}
|
||||
|
||||
private TtmlNode parseNode(XmlPullParser parser, TtmlNode parent,
|
||||
Map<String, TtmlRegion> regionMap, FrameAndTickRate frameAndTickRate) throws ParserException {
|
||||
Map<String, TtmlRegion> regionMap, FrameAndTickRate frameAndTickRate)
|
||||
throws TextDecoderException {
|
||||
long duration = 0;
|
||||
long startTime = TtmlNode.UNDEFINED_TIME;
|
||||
long endTime = TtmlNode.UNDEFINED_TIME;
|
||||
@ -436,7 +438,7 @@ public final class TtmlParser extends SimpleSubtitleParser {
|
||||
|| tag.equals(TtmlNode.TAG_SMPTE_INFORMATION);
|
||||
}
|
||||
|
||||
private static void parseFontSize(String expression, TtmlStyle out) throws ParserException {
|
||||
private static void parseFontSize(String expression, TtmlStyle out) throws TextDecoderException {
|
||||
String[] expressions = expression.split("\\s+");
|
||||
Matcher matcher;
|
||||
if (expressions.length == 1) {
|
||||
@ -446,7 +448,7 @@ public final class TtmlParser extends SimpleSubtitleParser {
|
||||
Log.w(TAG, "Multiple values in fontSize attribute. Picking the second value for vertical font"
|
||||
+ " size and ignoring the first.");
|
||||
} else {
|
||||
throw new ParserException("Invalid number of entries for fontSize: " + expressions.length
|
||||
throw new TextDecoderException("Invalid number of entries for fontSize: " + expressions.length
|
||||
+ ".");
|
||||
}
|
||||
|
||||
@ -463,11 +465,11 @@ public final class TtmlParser extends SimpleSubtitleParser {
|
||||
out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_PERCENT);
|
||||
break;
|
||||
default:
|
||||
throw new ParserException("Invalid unit for fontSize: '" + unit + "'.");
|
||||
throw new TextDecoderException("Invalid unit for fontSize: '" + unit + "'.");
|
||||
}
|
||||
out.setFontSize(Float.valueOf(matcher.group(1)));
|
||||
} else {
|
||||
throw new ParserException("Invalid expression for fontSize: '" + expression + "'.");
|
||||
throw new TextDecoderException("Invalid expression for fontSize: '" + expression + "'.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -480,10 +482,10 @@ public final class TtmlParser extends SimpleSubtitleParser {
|
||||
* @param time A string that includes the time expression.
|
||||
* @param frameAndTickRate The effective frame and tick rates of the stream.
|
||||
* @return The parsed timestamp in microseconds.
|
||||
* @throws ParserException If the given string does not contain a valid time expression.
|
||||
* @throws TextDecoderException If the given string does not contain a valid time expression.
|
||||
*/
|
||||
private static long parseTimeExpression(String time, FrameAndTickRate frameAndTickRate)
|
||||
throws ParserException {
|
||||
throws TextDecoderException {
|
||||
Matcher matcher = CLOCK_TIME.matcher(time);
|
||||
if (matcher.matches()) {
|
||||
String hours = matcher.group(1);
|
||||
@ -531,7 +533,7 @@ public final class TtmlParser extends SimpleSubtitleParser {
|
||||
}
|
||||
return (long) (offsetSeconds * C.MICROS_PER_SECOND);
|
||||
}
|
||||
throw new ParserException("Malformed time expression: " + time);
|
||||
throw new TextDecoderException("Malformed time expression: " + time);
|
||||
}
|
||||
|
||||
private static final class FrameAndTickRate {
|
@ -16,18 +16,18 @@
|
||||
package com.google.android.exoplayer2.text.tx3g;
|
||||
|
||||
import com.google.android.exoplayer2.text.Cue;
|
||||
import com.google.android.exoplayer2.text.SimpleSubtitleParser;
|
||||
import com.google.android.exoplayer2.text.SimpleSubtitleDecoder;
|
||||
import com.google.android.exoplayer2.text.Subtitle;
|
||||
|
||||
/**
|
||||
* A subtitle parser for tx3g.
|
||||
* A {@link SimpleSubtitleDecoder} for tx3g.
|
||||
* <p>
|
||||
* Currently only supports parsing of a single text track.
|
||||
*/
|
||||
public final class Tx3gParser extends SimpleSubtitleParser {
|
||||
public final class Tx3gDecoder extends SimpleSubtitleDecoder {
|
||||
|
||||
public Tx3gParser() {
|
||||
super("Tx3gParser");
|
||||
public Tx3gDecoder() {
|
||||
super("Tx3gDecoder");
|
||||
}
|
||||
|
||||
@Override
|
@ -15,9 +15,9 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.text.webvtt;
|
||||
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.text.Cue;
|
||||
import com.google.android.exoplayer2.text.SimpleSubtitleParser;
|
||||
import com.google.android.exoplayer2.text.SimpleSubtitleDecoder;
|
||||
import com.google.android.exoplayer2.text.TextDecoderException;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
@ -26,9 +26,9 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A subtitle parser for Webvtt embedded in a Mp4 container file.
|
||||
* A {@link SimpleSubtitleDecoder} for Webvtt embedded in a Mp4 container file.
|
||||
*/
|
||||
public final class Mp4WebvttParser extends SimpleSubtitleParser {
|
||||
public final class Mp4WebvttDecoder extends SimpleSubtitleDecoder {
|
||||
|
||||
private static final int BOX_HEADER_SIZE = 8;
|
||||
|
||||
@ -39,21 +39,21 @@ public final class Mp4WebvttParser extends SimpleSubtitleParser {
|
||||
private final ParsableByteArray sampleData;
|
||||
private final WebvttCue.Builder builder;
|
||||
|
||||
public Mp4WebvttParser() {
|
||||
super("Mp4WebvttParser");
|
||||
public Mp4WebvttDecoder() {
|
||||
super("Mp4WebvttDecoder");
|
||||
sampleData = new ParsableByteArray();
|
||||
builder = new WebvttCue.Builder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mp4WebvttSubtitle decode(byte[] bytes, int length) throws ParserException {
|
||||
protected Mp4WebvttSubtitle decode(byte[] bytes, int length) throws TextDecoderException {
|
||||
// Webvtt in Mp4 samples have boxes inside of them, so we have to do a traditional box parsing:
|
||||
// first 4 bytes size and then 4 bytes type.
|
||||
sampleData.reset(bytes, length);
|
||||
List<Cue> resultingCueList = new ArrayList<>();
|
||||
while (sampleData.bytesLeft() > 0) {
|
||||
if (sampleData.bytesLeft() < BOX_HEADER_SIZE) {
|
||||
throw new ParserException("Incomplete Mp4Webvtt Top Level box header found.");
|
||||
throw new TextDecoderException("Incomplete Mp4Webvtt Top Level box header found.");
|
||||
}
|
||||
int boxSize = sampleData.readInt();
|
||||
int boxType = sampleData.readInt();
|
||||
@ -68,11 +68,11 @@ public final class Mp4WebvttParser extends SimpleSubtitleParser {
|
||||
}
|
||||
|
||||
private static Cue parseVttCueBox(ParsableByteArray sampleData, WebvttCue.Builder builder,
|
||||
int remainingCueBoxBytes) throws ParserException {
|
||||
int remainingCueBoxBytes) throws TextDecoderException {
|
||||
builder.reset();
|
||||
while (remainingCueBoxBytes > 0) {
|
||||
if (remainingCueBoxBytes < BOX_HEADER_SIZE) {
|
||||
throw new ParserException("Incomplete vtt cue box header found.");
|
||||
throw new TextDecoderException("Incomplete vtt cue box header found.");
|
||||
}
|
||||
int boxSize = sampleData.readInt();
|
||||
int boxType = sampleData.readInt();
|
@ -15,8 +15,8 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.text.webvtt;
|
||||
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.text.SimpleSubtitleParser;
|
||||
import com.google.android.exoplayer2.text.SimpleSubtitleDecoder;
|
||||
import com.google.android.exoplayer2.text.TextDecoderException;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
import android.text.TextUtils;
|
||||
@ -25,11 +25,11 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A simple WebVTT parser.
|
||||
* A {@link SimpleSubtitleDecoder} for WebVTT.
|
||||
* <p>
|
||||
* @see <a href="http://dev.w3.org/html5/webvtt">WebVTT specification</a>
|
||||
*/
|
||||
public final class WebvttParser extends SimpleSubtitleParser {
|
||||
public final class WebvttDecoder extends SimpleSubtitleDecoder {
|
||||
|
||||
private static final int NO_EVENT_FOUND = -1;
|
||||
private static final int END_OF_FILE_FOUND = 0;
|
||||
@ -45,8 +45,8 @@ public final class WebvttParser extends SimpleSubtitleParser {
|
||||
private final CssParser cssParser;
|
||||
private final List<WebvttCssStyle> definedStyles;
|
||||
|
||||
public WebvttParser() {
|
||||
super("WebvttParser");
|
||||
public WebvttDecoder() {
|
||||
super("WebvttDecoder");
|
||||
cueParser = new WebvttCueParser();
|
||||
parsableWebvttData = new ParsableByteArray();
|
||||
webvttCueBuilder = new WebvttCue.Builder();
|
||||
@ -55,7 +55,7 @@ public final class WebvttParser extends SimpleSubtitleParser {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WebvttSubtitle decode(byte[] bytes, int length) throws ParserException {
|
||||
protected WebvttSubtitle decode(byte[] bytes, int length) throws TextDecoderException {
|
||||
parsableWebvttData.reset(bytes, length);
|
||||
// Initialization for consistent starting state.
|
||||
webvttCueBuilder.reset();
|
||||
@ -72,7 +72,7 @@ public final class WebvttParser extends SimpleSubtitleParser {
|
||||
skipComment(parsableWebvttData);
|
||||
} else if (eventFound == STYLE_BLOCK_FOUND) {
|
||||
if (!subtitles.isEmpty()) {
|
||||
throw new ParserException("A style block was found after the first cue.");
|
||||
throw new TextDecoderException("A style block was found after the first cue.");
|
||||
}
|
||||
parsableWebvttData.readLine(); // Consume the "STYLE" header.
|
||||
WebvttCssStyle styleBlock = cssParser.parseBlock(parsableWebvttData);
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.text.webvtt;
|
||||
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.text.TextDecoderException;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
@ -35,12 +35,12 @@ public final class WebvttParserUtil {
|
||||
* Reads and validates the first line of a WebVTT file.
|
||||
*
|
||||
* @param input The input from which the line should be read.
|
||||
* @throws ParserException If the line isn't the start of a valid WebVTT file.
|
||||
* @throws TextDecoderException If the line isn't the start of a valid WebVTT file.
|
||||
*/
|
||||
public static void validateWebvttHeaderLine(ParsableByteArray input) throws ParserException {
|
||||
public static void validateWebvttHeaderLine(ParsableByteArray input) throws TextDecoderException {
|
||||
String line = input.readLine();
|
||||
if (line == null || !HEADER.matcher(line).matches()) {
|
||||
throw new ParserException("Expected WEBVTT. Got " + line);
|
||||
throw new TextDecoderException("Expected WEBVTT. Got " + line);
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,10 +62,11 @@ public final class WebvttParserUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a percentage and returns a scaled float.
|
||||
* @param s contains the number to parse.
|
||||
* @return a float scaled number. 1.0 represents 100%.
|
||||
* @throws NumberFormatException if the number format is invalid or does not end with '%'.
|
||||
* Parses a percentage string.
|
||||
*
|
||||
* @param s The percentage string.
|
||||
* @return The parsed value, where 1.0 represents 100%.
|
||||
* @throws NumberFormatException If the percentage could not be parsed.
|
||||
*/
|
||||
public static float parsePercentage(String s) throws NumberFormatException {
|
||||
if (!s.endsWith("%")) {
|
||||
|
@ -13,13 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.util;
|
||||
package com.google.android.exoplayer2.ui;
|
||||
|
||||
import com.google.android.exoplayer2.CodecCounters;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.decoder.DecoderCounters;
|
||||
|
||||
import android.widget.TextView;
|
||||
|
||||
@ -116,7 +116,8 @@ public final class DebugTextViewHelper implements Runnable, ExoPlayer.EventListe
|
||||
return "";
|
||||
}
|
||||
return "\n" + format.sampleMimeType + "(id:" + format.id + " r:" + format.width + "x"
|
||||
+ format.height + getCodecCounterBufferCountString(player.getVideoCodecCounters()) + ")";
|
||||
+ format.height + getDecoderCountersBufferCountString(player.getVideoDecoderCounters())
|
||||
+ ")";
|
||||
}
|
||||
|
||||
private String getAudioString() {
|
||||
@ -125,11 +126,11 @@ public final class DebugTextViewHelper implements Runnable, ExoPlayer.EventListe
|
||||
return "";
|
||||
}
|
||||
return "\n" + format.sampleMimeType + "(id:" + format.id + " hz:" + format.sampleRate + " ch:"
|
||||
+ format.channelCount + getCodecCounterBufferCountString(player.getAudioCodecCounters())
|
||||
+ ")";
|
||||
+ format.channelCount
|
||||
+ getDecoderCountersBufferCountString(player.getAudioDecoderCounters()) + ")";
|
||||
}
|
||||
|
||||
private static String getCodecCounterBufferCountString(CodecCounters counters) {
|
||||
private static String getDecoderCountersBufferCountString(DecoderCounters counters) {
|
||||
if (counters == null) {
|
||||
return "";
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
package com.google.android.exoplayer2.ui;
|
||||
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.MediaCodecAudioRenderer;
|
||||
import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
import android.widget.MediaController.MediaPlayerControl;
|
||||
|
@ -128,10 +128,10 @@ public final class CodecSpecificDataUtil {
|
||||
*/
|
||||
public static byte[] buildAacAudioSpecificConfig(int audioObjectType, int sampleRateIndex,
|
||||
int channelConfig) {
|
||||
byte[] audioSpecificConfig = new byte[2];
|
||||
audioSpecificConfig[0] = (byte) ((audioObjectType << 3) & 0xF8 | (sampleRateIndex >> 1) & 0x07);
|
||||
audioSpecificConfig[1] = (byte) ((sampleRateIndex << 7) & 0x80 | (channelConfig << 3) & 0x78);
|
||||
return audioSpecificConfig;
|
||||
byte[] specificConfig = new byte[2];
|
||||
specificConfig[0] = (byte) (((audioObjectType << 3) & 0xF8) | ((sampleRateIndex >> 1) & 0x07));
|
||||
specificConfig[1] = (byte) (((sampleRateIndex << 7) & 0x80) | ((channelConfig << 3) & 0x78));
|
||||
return specificConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
package com.google.android.exoplayer2.util;
|
||||
|
||||
/**
|
||||
* Tracks the progression of media time.
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
package com.google.android.exoplayer2.util;
|
||||
|
||||
import android.os.SystemClock;
|
||||
|
||||
@ -21,7 +21,7 @@ import android.os.SystemClock;
|
||||
* A standalone {@link MediaClock}. The clock can be started, stopped and its time can be set and
|
||||
* retrieved. When started, this clock is based on {@link SystemClock#elapsedRealtime()}.
|
||||
*/
|
||||
/* package */ final class StandaloneMediaClock implements MediaClock {
|
||||
public final class StandaloneMediaClock implements MediaClock {
|
||||
|
||||
private boolean started;
|
||||
|
@ -49,7 +49,7 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Miscellaneous utility functions.
|
||||
* Miscellaneous utility methods.
|
||||
*/
|
||||
public final class Util {
|
||||
|
||||
@ -346,7 +346,7 @@ public final class Util {
|
||||
/**
|
||||
* Parses an xs:duration attribute value, returning the parsed duration in milliseconds.
|
||||
*
|
||||
* @param value The attribute value to parse.
|
||||
* @param value The attribute value to decode.
|
||||
* @return The parsed duration in milliseconds.
|
||||
*/
|
||||
public static long parseXsDuration(String value) {
|
||||
@ -378,7 +378,7 @@ public final class Util {
|
||||
* Parses an xs:dateTime attribute value, returning the parsed timestamp in milliseconds since
|
||||
* the epoch.
|
||||
*
|
||||
* @param value The attribute value to parse.
|
||||
* @param value The attribute value to decode.
|
||||
* @return The parsed timestamp in milliseconds since the epoch.
|
||||
*/
|
||||
public static long parseXsDateTime(String value) throws ParseException {
|
||||
|
@ -19,11 +19,11 @@ import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
/**
|
||||
* Parser utility functions.
|
||||
* {@link XmlPullParser} utility methods.
|
||||
*/
|
||||
public final class ParserUtil {
|
||||
public final class XmlPullParserUtil {
|
||||
|
||||
private ParserUtil() {}
|
||||
private XmlPullParserUtil() {}
|
||||
|
||||
public static boolean isEndTag(XmlPullParser xpp, String name) throws XmlPullParserException {
|
||||
return isEndTag(xpp) && xpp.getName().equals(name);
|
@ -13,14 +13,21 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
package com.google.android.exoplayer2.video;
|
||||
|
||||
import com.google.android.exoplayer2.MediaCodecUtil.DecoderQueryException;
|
||||
import com.google.android.exoplayer2.VideoRendererEventListener.EventDispatcher;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.TraceUtil;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import com.google.android.exoplayer2.video.VideoRendererEventListener.EventDispatcher;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
@ -173,7 +180,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
if (!MimeTypes.isVideo(mimeType)) {
|
||||
return FORMAT_UNSUPPORTED_TYPE;
|
||||
}
|
||||
MediaCodecDecoderInfo decoderInfo = mediaCodecSelector.getDecoderInfo(mimeType,
|
||||
MediaCodecInfo decoderInfo = mediaCodecSelector.getDecoderInfo(mimeType,
|
||||
format.requiresSecureDecryption);
|
||||
if (decoderInfo == null) {
|
||||
return FORMAT_UNSUPPORTED_SUBTYPE;
|
||||
@ -205,7 +212,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
@Override
|
||||
protected void onEnabled(boolean joining) throws ExoPlaybackException {
|
||||
super.onEnabled(joining);
|
||||
eventDispatcher.enabled(codecCounters);
|
||||
eventDispatcher.enabled(decoderCounters);
|
||||
frameReleaseTimeHelper.enable();
|
||||
}
|
||||
|
||||
@ -282,8 +289,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
try {
|
||||
super.onDisabled();
|
||||
} finally {
|
||||
codecCounters.ensureUpdated();
|
||||
eventDispatcher.disabled(codecCounters);
|
||||
decoderCounters.ensureUpdated();
|
||||
eventDispatcher.disabled(decoderCounters);
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,7 +314,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
this.surface = surface;
|
||||
this.reportedDrawnToSurface = false;
|
||||
int state = getState();
|
||||
if (state == Renderer.STATE_ENABLED || state == Renderer.STATE_STARTED) {
|
||||
if (state == STATE_ENABLED || state == STATE_STARTED) {
|
||||
releaseCodec();
|
||||
maybeInitCodec();
|
||||
}
|
||||
@ -403,7 +410,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getState() != Renderer.STATE_STARTED) {
|
||||
if (getState() != STATE_STARTED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -458,18 +465,18 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
TraceUtil.beginSection("skipVideoBuffer");
|
||||
codec.releaseOutputBuffer(bufferIndex, false);
|
||||
TraceUtil.endSection();
|
||||
codecCounters.skippedOutputBufferCount++;
|
||||
decoderCounters.skippedOutputBufferCount++;
|
||||
}
|
||||
|
||||
protected void dropOutputBuffer(MediaCodec codec, int bufferIndex) {
|
||||
TraceUtil.beginSection("dropVideoBuffer");
|
||||
codec.releaseOutputBuffer(bufferIndex, false);
|
||||
TraceUtil.endSection();
|
||||
codecCounters.droppedOutputBufferCount++;
|
||||
decoderCounters.droppedOutputBufferCount++;
|
||||
droppedFrameCount++;
|
||||
consecutiveDroppedFrameCount++;
|
||||
codecCounters.maxConsecutiveDroppedOutputBufferCount = Math.max(consecutiveDroppedFrameCount,
|
||||
codecCounters.maxConsecutiveDroppedOutputBufferCount);
|
||||
decoderCounters.maxConsecutiveDroppedOutputBufferCount = Math.max(consecutiveDroppedFrameCount,
|
||||
decoderCounters.maxConsecutiveDroppedOutputBufferCount);
|
||||
if (droppedFrameCount == maxDroppedFrameCountToNotify) {
|
||||
maybeNotifyDroppedFrameCount();
|
||||
}
|
||||
@ -480,7 +487,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
TraceUtil.beginSection("releaseOutputBuffer");
|
||||
codec.releaseOutputBuffer(bufferIndex, true);
|
||||
TraceUtil.endSection();
|
||||
codecCounters.renderedOutputBufferCount++;
|
||||
decoderCounters.renderedOutputBufferCount++;
|
||||
consecutiveDroppedFrameCount = 0;
|
||||
renderedFirstFrame = true;
|
||||
maybeNotifyDrawnToSurface();
|
||||
@ -492,7 +499,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
TraceUtil.beginSection("releaseOutputBuffer");
|
||||
codec.releaseOutputBuffer(bufferIndex, releaseTimeNs);
|
||||
TraceUtil.endSection();
|
||||
codecCounters.renderedOutputBufferCount++;
|
||||
decoderCounters.renderedOutputBufferCount++;
|
||||
consecutiveDroppedFrameCount = 0;
|
||||
renderedFirstFrame = true;
|
||||
maybeNotifyDrawnToSurface();
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
package com.google.android.exoplayer2.video;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user