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:
olly 2016-07-13 09:43:42 -07:00 committed by Oliver Woodman
parent 4f718363b5
commit f758082d40
105 changed files with 798 additions and 704 deletions

View File

@ -15,12 +15,12 @@
*/ */
package com.google.android.exoplayer2.demo; package com.google.android.exoplayer2.demo;
import com.google.android.exoplayer2.CodecCounters;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.SimpleExoPlayer; 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.drm.StreamingDrmSessionManager;
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener;
import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.ExtractorMediaSource;
@ -145,7 +145,7 @@ public class EventLogger implements ExoPlayer.EventListener, SimpleExoPlayer.Deb
// SimpleExoPlayer.DebugListener // SimpleExoPlayer.DebugListener
@Override @Override
public void onAudioEnabled(CodecCounters counters) { public void onAudioEnabled(DecoderCounters counters) {
Log.d(TAG, "audioEnabled [" + getSessionTimeString() + "]"); Log.d(TAG, "audioEnabled [" + getSessionTimeString() + "]");
} }
@ -167,12 +167,12 @@ public class EventLogger implements ExoPlayer.EventListener, SimpleExoPlayer.Deb
} }
@Override @Override
public void onAudioDisabled(CodecCounters counters) { public void onAudioDisabled(DecoderCounters counters) {
Log.d(TAG, "audioDisabled [" + getSessionTimeString() + "]"); Log.d(TAG, "audioDisabled [" + getSessionTimeString() + "]");
} }
@Override @Override
public void onVideoEnabled(CodecCounters counters) { public void onVideoEnabled(DecoderCounters counters) {
Log.d(TAG, "videoEnabled [" + getSessionTimeString() + "]"); Log.d(TAG, "videoEnabled [" + getSessionTimeString() + "]");
} }
@ -189,7 +189,7 @@ public class EventLogger implements ExoPlayer.EventListener, SimpleExoPlayer.Deb
} }
@Override @Override
public void onVideoDisabled(CodecCounters counters) { public void onVideoDisabled(DecoderCounters counters) {
Log.d(TAG, "videoDisabled [" + getSessionTimeString() + "]"); Log.d(TAG, "videoDisabled [" + getSessionTimeString() + "]");
} }

View File

@ -20,13 +20,13 @@ import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory; 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.SimpleExoPlayer;
import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.StreamingDrmSessionManager; import com.google.android.exoplayer2.drm.StreamingDrmSessionManager;
import com.google.android.exoplayer2.drm.UnsupportedDrmException; import com.google.android.exoplayer2.drm.UnsupportedDrmException;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; 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.ApicFrame;
import com.google.android.exoplayer2.metadata.id3.GeobFrame; import com.google.android.exoplayer2.metadata.id3.GeobFrame;
import com.google.android.exoplayer2.metadata.id3.Id3Frame; 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.DataSourceFactory;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; 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 com.google.android.exoplayer2.util.Util;
import android.Manifest.permission; import android.Manifest.permission;

View File

@ -15,12 +15,12 @@
*/ */
package com.google.android.exoplayer2.ext.ffmpeg; package com.google.android.exoplayer2.ext.ffmpeg;
import com.google.android.exoplayer2.AudioRendererEventListener;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.audio.AudioCapabilities; 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.AudioTrack;
import com.google.android.exoplayer2.extensions.AudioDecoderRenderer; import com.google.android.exoplayer2.audio.SimpleDecoderAudioRenderer;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import android.os.Handler; import android.os.Handler;
@ -28,7 +28,7 @@ import android.os.Handler;
/** /**
* Decodes and renders audio using FFmpeg. * 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 NUM_BUFFERS = 16;
private static final int INITIAL_INPUT_BUFFER_SIZE = 960 * 6; private static final int INITIAL_INPUT_BUFFER_SIZE = 960 * 6;

View File

@ -15,9 +15,9 @@
*/ */
package com.google.android.exoplayer2.ext.ffmpeg; package com.google.android.exoplayer2.ext.ffmpeg;
import com.google.android.exoplayer2.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.extensions.SimpleDecoder; import com.google.android.exoplayer2.decoder.SimpleDecoder;
import com.google.android.exoplayer2.extensions.SimpleOutputBuffer; import com.google.android.exoplayer2.decoder.SimpleOutputBuffer;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;

View File

@ -15,7 +15,7 @@
*/ */
package com.google.android.exoplayer2.ext.ffmpeg; 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. * Thrown when an FFmpeg decoder error occurs.

View File

@ -15,9 +15,9 @@
*/ */
package com.google.android.exoplayer2.ext.flac; package com.google.android.exoplayer2.ext.flac;
import com.google.android.exoplayer2.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.extensions.SimpleDecoder; import com.google.android.exoplayer2.decoder.SimpleDecoder;
import com.google.android.exoplayer2.extensions.SimpleOutputBuffer; import com.google.android.exoplayer2.decoder.SimpleOutputBuffer;
import com.google.android.exoplayer2.util.FlacStreamInfo; import com.google.android.exoplayer2.util.FlacStreamInfo;
import java.io.IOException; import java.io.IOException;

View File

@ -15,7 +15,7 @@
*/ */
package com.google.android.exoplayer2.ext.flac; 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. * Thrown when an Flac decoder error occurs.

View File

@ -15,11 +15,11 @@
*/ */
package com.google.android.exoplayer2.ext.flac; package com.google.android.exoplayer2.ext.flac;
import com.google.android.exoplayer2.AudioRendererEventListener;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.audio.AudioCapabilities; 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.AudioTrack;
import com.google.android.exoplayer2.extensions.AudioDecoderRenderer; import com.google.android.exoplayer2.audio.SimpleDecoderAudioRenderer;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import android.os.Handler; import android.os.Handler;
@ -27,7 +27,7 @@ import android.os.Handler;
/** /**
* Decodes and renders audio using the native Flac decoder. * 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; private static final int NUM_BUFFERS = 16;

View File

@ -15,11 +15,11 @@
*/ */
package com.google.android.exoplayer2.ext.opus; package com.google.android.exoplayer2.ext.opus;
import com.google.android.exoplayer2.AudioRendererEventListener;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.audio.AudioCapabilities; 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.AudioTrack;
import com.google.android.exoplayer2.extensions.AudioDecoderRenderer; import com.google.android.exoplayer2.audio.SimpleDecoderAudioRenderer;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import android.os.Handler; import android.os.Handler;
@ -27,7 +27,7 @@ import android.os.Handler;
/** /**
* Decodes and renders audio using the native Opus decoder. * 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 NUM_BUFFERS = 16;
private static final int INITIAL_INPUT_BUFFER_SIZE = 960 * 6; private static final int INITIAL_INPUT_BUFFER_SIZE = 960 * 6;

View File

@ -16,9 +16,9 @@
package com.google.android.exoplayer2.ext.opus; package com.google.android.exoplayer2.ext.opus;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.extensions.SimpleDecoder; import com.google.android.exoplayer2.decoder.SimpleDecoder;
import com.google.android.exoplayer2.extensions.SimpleOutputBuffer; import com.google.android.exoplayer2.decoder.SimpleOutputBuffer;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;

View File

@ -15,7 +15,7 @@
*/ */
package com.google.android.exoplayer2.ext.opus; 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. * Thrown when an Opus decoder error occurs.

View File

@ -16,17 +16,17 @@
package com.google.android.exoplayer2.ext.vp9; package com.google.android.exoplayer2.ext.vp9;
import com.google.android.exoplayer2.C; 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.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.VideoRendererEventListener; import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.VideoRendererEventListener.EventDispatcher; import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.TraceUtil; 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.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
@ -60,7 +60,7 @@ public final class LibvpxVideoRenderer extends Renderer {
private final EventDispatcher eventDispatcher; private final EventDispatcher eventDispatcher;
private final FormatHolder formatHolder; private final FormatHolder formatHolder;
private CodecCounters codecCounters; private DecoderCounters decoderCounters;
private Format format; private Format format;
private VpxDecoder decoder; private VpxDecoder decoder;
private DecoderInputBuffer inputBuffer; private DecoderInputBuffer inputBuffer;
@ -172,7 +172,7 @@ public final class LibvpxVideoRenderer extends Renderer {
long codecInitializedTimestamp = SystemClock.elapsedRealtime(); long codecInitializedTimestamp = SystemClock.elapsedRealtime();
eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp, eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp,
codecInitializedTimestamp - codecInitializingTimestamp); codecInitializedTimestamp - codecInitializingTimestamp);
codecCounters.codecInitCount++; decoderCounters.codecInitCount++;
} }
TraceUtil.beginSection("drainAndFeed"); TraceUtil.beginSection("drainAndFeed");
while (drainOutputBuffer(positionUs)) {} while (drainOutputBuffer(positionUs)) {}
@ -181,7 +181,7 @@ public final class LibvpxVideoRenderer extends Renderer {
} catch (VpxDecoderException e) { } catch (VpxDecoderException e) {
throw ExoPlaybackException.createForRenderer(e, getIndex()); throw ExoPlaybackException.createForRenderer(e, getIndex());
} }
codecCounters.ensureUpdated(); decoderCounters.ensureUpdated();
} }
private boolean drainOutputBuffer(long positionUs) throws VpxDecoderException { private boolean drainOutputBuffer(long positionUs) throws VpxDecoderException {
@ -200,7 +200,7 @@ public final class LibvpxVideoRenderer extends Renderer {
if (outputBuffer == null) { if (outputBuffer == null) {
return false; return false;
} }
codecCounters.skippedOutputBufferCount += outputBuffer.skippedOutputBufferCount; decoderCounters.skippedOutputBufferCount += outputBuffer.skippedOutputBufferCount;
} }
if (nextOutputBuffer == null) { if (nextOutputBuffer == null) {
@ -219,11 +219,12 @@ public final class LibvpxVideoRenderer extends Renderer {
if ((joiningDeadlineMs != -1 && outputBuffer.timestampUs < positionUs - 30000) if ((joiningDeadlineMs != -1 && outputBuffer.timestampUs < positionUs - 30000)
|| (nextOutputBuffer != null && !nextOutputBuffer.isEndOfStream() || (nextOutputBuffer != null && !nextOutputBuffer.isEndOfStream()
&& nextOutputBuffer.timestampUs < positionUs)) { && nextOutputBuffer.timestampUs < positionUs)) {
codecCounters.droppedOutputBufferCount++; decoderCounters.droppedOutputBufferCount++;
droppedFrameCount++; droppedFrameCount++;
consecutiveDroppedFrameCount++; consecutiveDroppedFrameCount++;
codecCounters.maxConsecutiveDroppedOutputBufferCount = Math.max(consecutiveDroppedFrameCount, decoderCounters.maxConsecutiveDroppedOutputBufferCount = Math.max(
codecCounters.maxConsecutiveDroppedOutputBufferCount); consecutiveDroppedFrameCount,
decoderCounters.maxConsecutiveDroppedOutputBufferCount);
if (droppedFrameCount == maxDroppedFrameCountToNotify) { if (droppedFrameCount == maxDroppedFrameCountToNotify) {
maybeNotifyDroppedFrameCount(); maybeNotifyDroppedFrameCount();
} }
@ -248,7 +249,7 @@ public final class LibvpxVideoRenderer extends Renderer {
} }
private void renderBuffer() { private void renderBuffer() {
codecCounters.renderedOutputBufferCount++; decoderCounters.renderedOutputBufferCount++;
consecutiveDroppedFrameCount = 0; consecutiveDroppedFrameCount = 0;
maybeNotifyVideoSizeChanged(outputBuffer.width, outputBuffer.height); maybeNotifyVideoSizeChanged(outputBuffer.width, outputBuffer.height);
if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_RGB && surface != null) { if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_RGB && surface != null) {
@ -310,7 +311,7 @@ public final class LibvpxVideoRenderer extends Renderer {
} }
inputBuffer.flip(); inputBuffer.flip();
decoder.queueInputBuffer(inputBuffer); decoder.queueInputBuffer(inputBuffer);
codecCounters.inputBufferCount++; decoderCounters.inputBufferCount++;
inputBuffer = null; inputBuffer = null;
return true; return true;
} }
@ -354,8 +355,8 @@ public final class LibvpxVideoRenderer extends Renderer {
@Override @Override
protected void onEnabled(boolean joining) throws ExoPlaybackException { protected void onEnabled(boolean joining) throws ExoPlaybackException {
codecCounters = new CodecCounters(); decoderCounters = new DecoderCounters();
eventDispatcher.enabled(codecCounters); eventDispatcher.enabled(decoderCounters);
} }
@Override @Override
@ -392,11 +393,11 @@ public final class LibvpxVideoRenderer extends Renderer {
if (decoder != null) { if (decoder != null) {
decoder.release(); decoder.release();
decoder = null; decoder = null;
codecCounters.codecReleaseCount++; decoderCounters.codecReleaseCount++;
} }
} finally { } finally {
codecCounters.ensureUpdated(); decoderCounters.ensureUpdated();
eventDispatcher.disabled(codecCounters); eventDispatcher.disabled(decoderCounters);
} }
} }

View File

@ -16,8 +16,8 @@
package com.google.android.exoplayer2.ext.vp9; package com.google.android.exoplayer2.ext.vp9;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.extensions.SimpleDecoder; import com.google.android.exoplayer2.decoder.SimpleDecoder;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;

View File

@ -15,7 +15,7 @@
*/ */
package com.google.android.exoplayer2.ext.vp9; 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; import java.nio.ByteBuffer;

View File

@ -16,7 +16,7 @@
package com.google.android.exoplayer2.extractor.mp3; package com.google.android.exoplayer2.extractor.mp3;
import com.google.android.exoplayer2.C; 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.ParsableByteArray;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;

View File

@ -15,36 +15,37 @@
*/ */
package com.google.android.exoplayer2.metadata.id3; 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 android.test.MoreAsserts;
import junit.framework.TestCase; import junit.framework.TestCase;
import java.util.List; 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, 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, 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}; 54, 52, 95, 115, 116, 97, 114, 116, 0};
Id3Parser parser = new Id3Parser(); Id3Decoder decoder = new Id3Decoder();
List<Id3Frame> id3Frames = parser.parse(rawId3, rawId3.length); List<Id3Frame> id3Frames = decoder.decode(rawId3, rawId3.length);
assertEquals(1, id3Frames.size()); assertEquals(1, id3Frames.size());
TxxxFrame txxxFrame = (TxxxFrame) id3Frames.get(0); TxxxFrame txxxFrame = (TxxxFrame) id3Frames.get(0);
assertEquals("", txxxFrame.description); assertEquals("", txxxFrame.description);
assertEquals("mdialog_VINDICO1527664_start", txxxFrame.value); 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, 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, 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}; 111, 114, 108, 100, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
Id3Parser parser = new Id3Parser(); Id3Decoder decoder = new Id3Decoder();
List<Id3Frame> id3Frames = parser.parse(rawId3, rawId3.length); List<Id3Frame> id3Frames = decoder.decode(rawId3, rawId3.length);
assertEquals(1, id3Frames.size()); assertEquals(1, id3Frames.size());
ApicFrame apicFrame = (ApicFrame) id3Frames.get(0); ApicFrame apicFrame = (ApicFrame) id3Frames.get(0);
assertEquals("image/jpeg", apicFrame.mimeType); 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); 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, 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}; 3, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 0};
Id3Parser parser = new Id3Parser(); Id3Decoder decoder = new Id3Decoder();
List<Id3Frame> id3Frames = parser.parse(rawId3, rawId3.length); List<Id3Frame> id3Frames = decoder.decode(rawId3, rawId3.length);
assertEquals(1, id3Frames.size()); assertEquals(1, id3Frames.size());
TextInformationFrame textInformationFrame = (TextInformationFrame) id3Frames.get(0); TextInformationFrame textInformationFrame = (TextInformationFrame) id3Frames.get(0);
assertEquals("TIT2", textInformationFrame.id); assertEquals("TIT2", textInformationFrame.id);

View File

@ -22,9 +22,9 @@ import android.test.InstrumentationTestCase;
import java.io.IOException; 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 EMPTY_FILE = "subrip/empty";
private static final String TYPICAL_FILE = "subrip/typical"; 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 TYPICAL_MISSING_SEQUENCE = "subrip/typical_missing_sequence";
private static final String NO_END_TIMECODES_FILE = "subrip/no_end_timecodes"; private static final String NO_END_TIMECODES_FILE = "subrip/no_end_timecodes";
public void testParseEmpty() throws IOException { public void testDecodeEmpty() throws IOException {
SubripParser parser = new SubripParser(); SubripDecoder decoder = new SubripDecoder();
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), EMPTY_FILE); 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. // Assert that the subtitle is empty.
assertEquals(0, subtitle.getEventTimeCount()); assertEquals(0, subtitle.getEventTimeCount());
assertTrue(subtitle.getCues(0).isEmpty()); assertTrue(subtitle.getCues(0).isEmpty());
} }
public void testParseTypical() throws IOException { public void testDecodeTypical() throws IOException {
SubripParser parser = new SubripParser(); SubripDecoder decoder = new SubripDecoder();
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_FILE); 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()); assertEquals(6, subtitle.getEventTimeCount());
assertTypicalCue1(subtitle, 0); assertTypicalCue1(subtitle, 0);
assertTypicalCue2(subtitle, 2); assertTypicalCue2(subtitle, 2);
assertTypicalCue3(subtitle, 4); assertTypicalCue3(subtitle, 4);
} }
public void testParseTypicalWithByteOrderMark() throws IOException { public void testDecodeTypicalWithByteOrderMark() throws IOException {
SubripParser parser = new SubripParser(); SubripDecoder decoder = new SubripDecoder();
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_WITH_BYTE_ORDER_MARK); 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()); assertEquals(6, subtitle.getEventTimeCount());
assertTypicalCue1(subtitle, 0); assertTypicalCue1(subtitle, 0);
assertTypicalCue2(subtitle, 2); assertTypicalCue2(subtitle, 2);
assertTypicalCue3(subtitle, 4); assertTypicalCue3(subtitle, 4);
} }
public void testParseTypicalExtraBlankLine() throws IOException { public void testDecodeTypicalExtraBlankLine() throws IOException {
SubripParser parser = new SubripParser(); SubripDecoder decoder = new SubripDecoder();
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_EXTRA_BLANK_LINE); 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()); assertEquals(6, subtitle.getEventTimeCount());
assertTypicalCue1(subtitle, 0); assertTypicalCue1(subtitle, 0);
assertTypicalCue2(subtitle, 2); assertTypicalCue2(subtitle, 2);
assertTypicalCue3(subtitle, 4); assertTypicalCue3(subtitle, 4);
} }
public void testParseTypicalMissingTimecode() throws IOException { public void testDecodeTypicalMissingTimecode() throws IOException {
// Parsing should succeed, parsing the first and third cues only. // 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); 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()); assertEquals(4, subtitle.getEventTimeCount());
assertTypicalCue1(subtitle, 0); assertTypicalCue1(subtitle, 0);
assertTypicalCue3(subtitle, 2); assertTypicalCue3(subtitle, 2);
} }
public void testParseTypicalMissingSequence() throws IOException { public void testDecodeTypicalMissingSequence() throws IOException {
// Parsing should succeed, parsing the first and third cues only. // 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); 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()); assertEquals(4, subtitle.getEventTimeCount());
assertTypicalCue1(subtitle, 0); assertTypicalCue1(subtitle, 0);
assertTypicalCue3(subtitle, 2); assertTypicalCue3(subtitle, 2);
} }
public void testParseNoEndTimecodes() throws IOException { public void testDecodeNoEndTimecodes() throws IOException {
SubripParser parser = new SubripParser(); SubripDecoder decoder = new SubripDecoder();
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), NO_END_TIMECODES_FILE); 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. // Test event count.
assertEquals(3, subtitle.getEventTimeCount()); assertEquals(3, subtitle.getEventTimeCount());

View File

@ -17,6 +17,7 @@ package com.google.android.exoplayer2.text.ttml;
import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.TextDecoderException;
import com.google.android.exoplayer2.util.ColorParser; import com.google.android.exoplayer2.util.ColorParser;
import android.test.InstrumentationTestCase; import android.test.InstrumentationTestCase;
@ -38,9 +39,9 @@ import java.util.List;
import java.util.Map; 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 INLINE_ATTRIBUTES_TTML_FILE = "ttml/inline_style_attributes.xml";
private static final String INHERIT_STYLE_TTML_FILE = "ttml/inherit_style.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 FONT_SIZE_EMPTY_TTML_FILE = "ttml/font_size_empty.xml";
private static final String FRAME_RATE_TTML_FILE = "ttml/frame_rate.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); TtmlSubtitle subtitle = getSubtitle(INLINE_ATTRIBUTES_TTML_FILE);
assertEquals(4, subtitle.getEventTimeCount()); assertEquals(4, subtitle.getEventTimeCount());
@ -76,7 +77,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
assertTrue(firstPStyle.isUnderline()); assertTrue(firstPStyle.isUnderline());
} }
public void testInheritInlineAttributes() throws IOException { public void testInheritInlineAttributes() throws IOException, TextDecoderException {
TtmlSubtitle subtitle = getSubtitle(INLINE_ATTRIBUTES_TTML_FILE); TtmlSubtitle subtitle = getSubtitle(INLINE_ATTRIBUTES_TTML_FILE);
assertEquals(4, subtitle.getEventTimeCount()); assertEquals(4, subtitle.getEventTimeCount());
assertSpans(subtitle, 20, "text 2", "sansSerif", TtmlStyle.STYLE_ITALIC, assertSpans(subtitle, 20, "text 2", "sansSerif", TtmlStyle.STYLE_ITALIC,
@ -94,21 +95,22 @@ public final class TtmlParserTest extends InstrumentationTestCase {
* Kitkat Color</a> * Kitkat Color</a>
* @throws IOException thrown if reading subtitle file fails. * @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); TtmlSubtitle subtitle = getSubtitle(INLINE_ATTRIBUTES_TTML_FILE);
assertEquals(4, subtitle.getEventTimeCount()); assertEquals(4, subtitle.getEventTimeCount());
assertSpans(subtitle, 20, "text 2", "sansSerif", TtmlStyle.STYLE_ITALIC, 0xFF00FFFF, 0xFF00FF00, assertSpans(subtitle, 20, "text 2", "sansSerif", TtmlStyle.STYLE_ITALIC, 0xFF00FFFF, 0xFF00FF00,
false, true, null); false, true, null);
} }
public void testInheritGlobalStyle() throws IOException { public void testInheritGlobalStyle() throws IOException, TextDecoderException {
TtmlSubtitle subtitle = getSubtitle(INHERIT_STYLE_TTML_FILE); TtmlSubtitle subtitle = getSubtitle(INHERIT_STYLE_TTML_FILE);
assertEquals(2, subtitle.getEventTimeCount()); assertEquals(2, subtitle.getEventTimeCount());
assertSpans(subtitle, 10, "text 1", "serif", TtmlStyle.STYLE_BOLD_ITALIC, 0xFF0000FF, assertSpans(subtitle, 10, "text 1", "serif", TtmlStyle.STYLE_BOLD_ITALIC, 0xFF0000FF,
0xFFFFFF00, true, false, null); 0xFFFFFF00, true, false, null);
} }
public void testInheritGlobalStyleOverriddenByInlineAttributes() throws IOException { public void testInheritGlobalStyleOverriddenByInlineAttributes() throws IOException,
TextDecoderException {
TtmlSubtitle subtitle = getSubtitle(INHERIT_STYLE_OVERRIDE_TTML_FILE); TtmlSubtitle subtitle = getSubtitle(INHERIT_STYLE_OVERRIDE_TTML_FILE);
assertEquals(4, subtitle.getEventTimeCount()); assertEquals(4, subtitle.getEventTimeCount());
@ -118,7 +120,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
true, false, null); true, false, null);
} }
public void testInheritGlobalAndParent() throws IOException { public void testInheritGlobalAndParent() throws IOException, TextDecoderException {
TtmlSubtitle subtitle = getSubtitle(INHERIT_GLOBAL_AND_PARENT_TTML_FILE); TtmlSubtitle subtitle = getSubtitle(INHERIT_GLOBAL_AND_PARENT_TTML_FILE);
assertEquals(4, subtitle.getEventTimeCount()); assertEquals(4, subtitle.getEventTimeCount());
@ -128,28 +130,29 @@ public final class TtmlParserTest extends InstrumentationTestCase {
0xFFFFFF00, true, true, Layout.Alignment.ALIGN_CENTER); 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); TtmlSubtitle subtitle = getSubtitle(INHERIT_MULTIPLE_STYLES_TTML_FILE);
assertEquals(12, subtitle.getEventTimeCount()); assertEquals(12, subtitle.getEventTimeCount());
assertSpans(subtitle, 10, "text 1", "sansSerif", TtmlStyle.STYLE_BOLD_ITALIC, 0xFF0000FF, assertSpans(subtitle, 10, "text 1", "sansSerif", TtmlStyle.STYLE_BOLD_ITALIC, 0xFF0000FF,
0xFFFFFF00, false, true, null); 0xFFFFFF00, false, true, null);
} }
public void testInheritMultipleStylesWithoutLocalAttributes() throws IOException { public void testInheritMultipleStylesWithoutLocalAttributes() throws IOException,
TextDecoderException {
TtmlSubtitle subtitle = getSubtitle(INHERIT_MULTIPLE_STYLES_TTML_FILE); TtmlSubtitle subtitle = getSubtitle(INHERIT_MULTIPLE_STYLES_TTML_FILE);
assertEquals(12, subtitle.getEventTimeCount()); assertEquals(12, subtitle.getEventTimeCount());
assertSpans(subtitle, 20, "text 2", "sansSerif", TtmlStyle.STYLE_BOLD_ITALIC, 0xFF0000FF, assertSpans(subtitle, 20, "text 2", "sansSerif", TtmlStyle.STYLE_BOLD_ITALIC, 0xFF0000FF,
0xFF000000, false, true, null); 0xFF000000, false, true, null);
} }
public void testMergeMultipleStylesWithParentStyle() throws IOException { public void testMergeMultipleStylesWithParentStyle() throws IOException, TextDecoderException {
TtmlSubtitle subtitle = getSubtitle(INHERIT_MULTIPLE_STYLES_TTML_FILE); TtmlSubtitle subtitle = getSubtitle(INHERIT_MULTIPLE_STYLES_TTML_FILE);
assertEquals(12, subtitle.getEventTimeCount()); assertEquals(12, subtitle.getEventTimeCount());
assertSpans(subtitle, 30, "text 2.5", "sansSerifInline", TtmlStyle.STYLE_ITALIC, 0xFFFF0000, assertSpans(subtitle, 30, "text 2.5", "sansSerifInline", TtmlStyle.STYLE_ITALIC, 0xFFFF0000,
0xFFFFFF00, true, true, null); 0xFFFFFF00, true, true, null);
} }
public void testMultipleRegions() throws IOException { public void testMultipleRegions() throws IOException, TextDecoderException {
TtmlSubtitle subtitle = getSubtitle(MULTIPLE_REGIONS_TTML_FILE); TtmlSubtitle subtitle = getSubtitle(MULTIPLE_REGIONS_TTML_FILE);
List<Cue> output = subtitle.getCues(1000000); List<Cue> output = subtitle.getCues(1000000);
assertEquals(2, output.size()); assertEquals(2, output.size());
@ -199,7 +202,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
assertEquals(45.f / 100.f, ttmlCue.line); 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); TtmlSubtitle subtitle = getSubtitle(INHERIT_MULTIPLE_STYLES_TTML_FILE);
assertEquals(12, subtitle.getEventTimeCount()); assertEquals(12, subtitle.getEventTimeCount());
@ -210,7 +213,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
assertNull(queryChildrenForTag(fourthDiv, TtmlNode.TAG_P, 0).getStyleIds()); 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); TtmlSubtitle subtitle = getSubtitle(INHERIT_MULTIPLE_STYLES_TTML_FILE);
assertEquals(12, subtitle.getEventTimeCount()); assertEquals(12, subtitle.getEventTimeCount());
@ -221,7 +224,8 @@ public final class TtmlParserTest extends InstrumentationTestCase {
assertEquals(1, queryChildrenForTag(fifthDiv, TtmlNode.TAG_P, 0).getStyleIds().length); 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); TtmlSubtitle subtitle = getSubtitle(INHERIT_MULTIPLE_STYLES_TTML_FILE);
assertEquals(12, subtitle.getEventTimeCount()); assertEquals(12, subtitle.getEventTimeCount());
@ -233,7 +237,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
assertEquals(2, styleIds.length); assertEquals(2, styleIds.length);
} }
public void testMultipleChaining() throws IOException { public void testMultipleChaining() throws IOException, TextDecoderException {
TtmlSubtitle subtitle = getSubtitle(CHAIN_MULTIPLE_STYLES_TTML_FILE); TtmlSubtitle subtitle = getSubtitle(CHAIN_MULTIPLE_STYLES_TTML_FILE);
assertEquals(2, subtitle.getEventTimeCount()); assertEquals(2, subtitle.getEventTimeCount());
@ -255,7 +259,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
assertTrue(style.isLinethrough()); assertTrue(style.isLinethrough());
} }
public void testNoUnderline() throws IOException { public void testNoUnderline() throws IOException, TextDecoderException {
TtmlSubtitle subtitle = getSubtitle(NO_UNDERLINE_LINETHROUGH_TTML_FILE); TtmlSubtitle subtitle = getSubtitle(NO_UNDERLINE_LINETHROUGH_TTML_FILE);
assertEquals(4, subtitle.getEventTimeCount()); assertEquals(4, subtitle.getEventTimeCount());
@ -267,7 +271,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
assertFalse("noUnderline from inline attribute expected", style.isUnderline()); 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); TtmlSubtitle subtitle = getSubtitle(NO_UNDERLINE_LINETHROUGH_TTML_FILE);
assertEquals(4, subtitle.getEventTimeCount()); assertEquals(4, subtitle.getEventTimeCount());
@ -280,7 +284,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
style.isLinethrough()); style.isLinethrough());
} }
public void testFontSizeSpans() throws IOException { public void testFontSizeSpans() throws IOException, TextDecoderException {
TtmlSubtitle subtitle = getSubtitle(FONT_SIZE_TTML_FILE); TtmlSubtitle subtitle = getSubtitle(FONT_SIZE_TTML_FILE);
assertEquals(10, subtitle.getEventTimeCount()); assertEquals(10, subtitle.getEventTimeCount());
@ -315,7 +319,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
assertRelativeFontSize(spannable, 0.5f); assertRelativeFontSize(spannable, 0.5f);
} }
public void testFontSizeWithMissingUnitIsIgnored() throws IOException { public void testFontSizeWithMissingUnitIsIgnored() throws IOException, TextDecoderException {
TtmlSubtitle subtitle = getSubtitle(FONT_SIZE_MISSING_UNIT_TTML_FILE); TtmlSubtitle subtitle = getSubtitle(FONT_SIZE_MISSING_UNIT_TTML_FILE);
assertEquals(2, subtitle.getEventTimeCount()); assertEquals(2, subtitle.getEventTimeCount());
List<Cue> cues = subtitle.getCues(10 * 1000000); 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); 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); TtmlSubtitle subtitle = getSubtitle(FONT_SIZE_INVALID_TTML_FILE);
assertEquals(6, subtitle.getEventTimeCount()); assertEquals(6, subtitle.getEventTimeCount());
@ -354,7 +358,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
assertEquals(0, spannable.getSpans(0, spannable.length(), AbsoluteSizeSpan.class).length); 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); TtmlSubtitle subtitle = getSubtitle(FONT_SIZE_EMPTY_TTML_FILE);
assertEquals(2, subtitle.getEventTimeCount()); assertEquals(2, subtitle.getEventTimeCount());
List<Cue> cues = subtitle.getCues(10 * 1000000); 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); 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); TtmlSubtitle subtitle = getSubtitle(FRAME_RATE_TTML_FILE);
assertEquals(4, subtitle.getEventTimeCount()); assertEquals(4, subtitle.getEventTimeCount());
assertEquals(1_000_000, subtitle.getEventTime(0)); assertEquals(1_000_000, subtitle.getEventTime(0));
@ -476,9 +480,10 @@ public final class TtmlParserTest extends InstrumentationTestCase {
throw new IllegalStateException("tag not found"); throw new IllegalStateException("tag not found");
} }
private TtmlSubtitle getSubtitle(String file) throws IOException { private TtmlSubtitle getSubtitle(String file) throws IOException, TextDecoderException {
TtmlParser ttmlParser = new TtmlParser(); TtmlDecoder ttmlDecoder = new TtmlDecoder();
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), file); byte[] bytes = TestUtil.getByteArray(getInstrumentation(), file);
return ttmlParser.decode(bytes, bytes.length); return ttmlDecoder.decode(bytes, bytes.length);
} }
} }

View File

@ -15,20 +15,18 @@
*/ */
package com.google.android.exoplayer2.text.webvtt; 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.Cue;
import com.google.android.exoplayer2.text.Subtitle; import com.google.android.exoplayer2.text.Subtitle;
import com.google.android.exoplayer2.text.TextDecoderException;
import junit.framework.TestCase; import junit.framework.TestCase;
import java.io.IOException;
import java.util.List; import java.util.List;
/** /**
* Unit test for {@link Mp4WebvttParser}. * Unit test for {@link Mp4WebvttDecoder}.
* As a side effect, it also involves the {@link Mp4WebvttSubtitle}.
*/ */
public final class Mp4WebvttParserTest extends TestCase { public final class Mp4WebvttDecoderTest extends TestCase {
private static final byte[] SINGLE_CUE_SAMPLE = { private static final byte[] SINGLE_CUE_SAMPLE = {
0x00, 0x00, 0x00, 0x1C, // Size 0x00, 0x00, 0x00, 0x1C, // Size
@ -81,42 +79,39 @@ public final class Mp4WebvttParserTest extends TestCase {
0x76, 0x74, 0x74 0x76, 0x74, 0x74
}; };
private Mp4WebvttParser parser;
@Override
public void setUp() {
parser = new Mp4WebvttParser();
}
// Positive tests. // Positive tests.
public void testSingleCueSample() throws ParserException { public void testSingleCueSample() throws TextDecoderException {
Subtitle result = parser.decode(SINGLE_CUE_SAMPLE, SINGLE_CUE_SAMPLE.length); Mp4WebvttDecoder decoder = new Mp4WebvttDecoder();
Cue expectedCue = new Cue("Hello World"); // Line feed must be trimmed by the parser 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); assertMp4WebvttSubtitleEquals(result, expectedCue);
} }
public void testTwoCuesSample() throws ParserException { public void testTwoCuesSample() throws TextDecoderException {
Subtitle result = parser.decode(DOUBLE_CUE_SAMPLE, DOUBLE_CUE_SAMPLE.length); Mp4WebvttDecoder decoder = new Mp4WebvttDecoder();
Subtitle result = decoder.decode(DOUBLE_CUE_SAMPLE, DOUBLE_CUE_SAMPLE.length);
Cue firstExpectedCue = new Cue("Hello World"); Cue firstExpectedCue = new Cue("Hello World");
Cue secondExpectedCue = new Cue("Bye Bye"); Cue secondExpectedCue = new Cue("Bye Bye");
assertMp4WebvttSubtitleEquals(result, firstExpectedCue, secondExpectedCue); assertMp4WebvttSubtitleEquals(result, firstExpectedCue, secondExpectedCue);
} }
public void testNoCueSample() throws IOException { public void testNoCueSample() throws TextDecoderException {
Subtitle result = parser.decode(NO_CUE_SAMPLE, NO_CUE_SAMPLE.length); Mp4WebvttDecoder decoder = new Mp4WebvttDecoder();
Subtitle result = decoder.decode(NO_CUE_SAMPLE, NO_CUE_SAMPLE.length);
assertMp4WebvttSubtitleEquals(result, new Cue[0]); assertMp4WebvttSubtitleEquals(result, new Cue[0]);
} }
// Negative tests. // Negative tests.
public void testSampleWithIncompleteHeader() { public void testSampleWithIncompleteHeader() {
Mp4WebvttDecoder decoder = new Mp4WebvttDecoder();
try { try {
parser.decode(INCOMPLETE_HEADER_SAMPLE, INCOMPLETE_HEADER_SAMPLE.length); decoder.decode(INCOMPLETE_HEADER_SAMPLE, INCOMPLETE_HEADER_SAMPLE.length);
} catch (ParserException e) { } catch (TextDecoderException e) {
return; return;
} }
fail("The parser should have failed, no payload was included in the VTTCue."); fail();
} }
// Util methods // Util methods

View File

@ -15,9 +15,9 @@
*/ */
package com.google.android.exoplayer2.text.webvtt; 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.testutil.TestUtil;
import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.TextDecoderException;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.test.InstrumentationTestCase; import android.test.InstrumentationTestCase;
@ -33,9 +33,9 @@ import java.io.IOException;
import java.util.List; 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_FILE = "webvtt/typical";
private static final String TYPICAL_WITH_IDS_FILE = "webvtt/typical_with_identifiers"; 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 WITH_CSS_COMPLEX_SELECTORS = "webvtt/with_css_complex_selectors";
private static final String EMPTY_FILE = "webvtt/empty"; private static final String EMPTY_FILE = "webvtt/empty";
public void testParseEmpty() throws IOException { public void testDecodeEmpty() throws IOException {
WebvttParser parser = new WebvttParser(); WebvttDecoder decoder = new WebvttDecoder();
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), EMPTY_FILE); byte[] bytes = TestUtil.getByteArray(getInstrumentation(), EMPTY_FILE);
try { try {
parser.decode(bytes, bytes.length); decoder.decode(bytes, bytes.length);
fail("Expected ParserException"); fail();
} catch (ParserException expected) { } catch (TextDecoderException expected) {
// Do nothing. // Do nothing.
} }
} }
public void testParseTypical() throws IOException { public void testDecodeTypical() throws IOException, TextDecoderException {
WebvttSubtitle subtitle = getSubtitleForTestAsset(TYPICAL_FILE); WebvttSubtitle subtitle = getSubtitleForTestAsset(TYPICAL_FILE);
// Test event count. // Test event count.
@ -69,7 +69,7 @@ public class WebvttParserTest extends InstrumentationTestCase {
assertCue(subtitle, 2, 2345000, 3456000, "This is the second subtitle."); 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); WebvttSubtitle subtitle = getSubtitleForTestAsset(TYPICAL_WITH_IDS_FILE);
// Test event count. // Test event count.
@ -80,7 +80,7 @@ public class WebvttParserTest extends InstrumentationTestCase {
assertCue(subtitle, 2, 2345000, 3456000, "This is the second subtitle."); 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); WebvttSubtitle subtitle = getSubtitleForTestAsset(TYPICAL_WITH_COMMENTS_FILE);
// test event count // test event count
@ -91,7 +91,7 @@ public class WebvttParserTest extends InstrumentationTestCase {
assertCue(subtitle, 2, 2345000, 3456000, "This is the second subtitle."); 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); WebvttSubtitle subtitle = getSubtitleForTestAsset(WITH_TAGS_FILE);
// Test event count. // Test event count.
@ -104,7 +104,7 @@ public class WebvttParserTest extends InstrumentationTestCase {
assertCue(subtitle, 6, 6000000, 7000000, "This is the <fourth> &subtitle."); 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); WebvttSubtitle subtitle = getSubtitleForTestAsset(WITH_POSITIONING_FILE);
// Test event count. // Test event count.
assertEquals(12, subtitle.getEventTimeCount()); assertEquals(12, subtitle.getEventTimeCount());
@ -128,7 +128,7 @@ public class WebvttParserTest extends InstrumentationTestCase {
Cue.TYPE_UNSET, 0.35f); Cue.TYPE_UNSET, 0.35f);
} }
public void testParseWithBadCueHeader() throws IOException { public void testDecodeWithBadCueHeader() throws IOException, TextDecoderException {
WebvttSubtitle subtitle = getSubtitleForTestAsset(WITH_BAD_CUE_HEADER_FILE); WebvttSubtitle subtitle = getSubtitleForTestAsset(WITH_BAD_CUE_HEADER_FILE);
// Test event count. // Test event count.
@ -139,7 +139,7 @@ public class WebvttParserTest extends InstrumentationTestCase {
assertCue(subtitle, 2, 4000000, 5000000, "This is the third subtitle."); 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); WebvttSubtitle subtitle = getSubtitleForTestAsset(WITH_CSS_STYLES);
// Test event count. // Test event count.
@ -162,7 +162,7 @@ public class WebvttParserTest extends InstrumentationTestCase {
assertEquals(Typeface.BOLD, s4.getSpans(17, s4.length(), StyleSpan.class)[0].getStyle()); 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); WebvttSubtitle subtitle = getSubtitleForTestAsset(WITH_CSS_COMPLEX_SELECTORS);
Spanned text = getUniqueSpanTextAt(subtitle, 0); Spanned text = getUniqueSpanTextAt(subtitle, 0);
assertEquals(1, text.getSpans(30, text.length(), ForegroundColorSpan.class).length); 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()); assertEquals(Typeface.ITALIC, text.getSpans(19, text.length(), StyleSpan.class)[0].getStyle());
} }
private WebvttSubtitle getSubtitleForTestAsset(String asset) throws IOException { private WebvttSubtitle getSubtitleForTestAsset(String asset) throws IOException,
WebvttParser parser = new WebvttParser(); TextDecoderException {
WebvttDecoder decoder = new WebvttDecoder();
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), asset); 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) { private Spanned getUniqueSpanTextAt(WebvttSubtitle sub, long timeUs) {

View File

@ -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 * is created. Hence {@link ExoPlayer} is capable of loading and playing any media for which a
* {@link Renderer} implementation can be provided. * {@link Renderer} implementation can be provided.
* *
* <p>{@link MediaCodecAudioRenderer} and {@link MediaCodecVideoRenderer} can be used for * <p>{@link com.google.android.exoplayer2.audio.MediaCodecAudioRenderer} and
* the common cases of rendering audio and video. These components in turn require an * {@link com.google.android.exoplayer2.video.MediaCodecVideoRenderer} can be used for the common
* <i>upstream</i> {@link MediaPeriod} to be injected through their constructors, where upstream * cases of rendering audio and video. These components in turn require an <i>upstream</i>
* is defined to denote a component that is closer to the source of the media. This pattern of * {@link MediaPeriod} to be injected through their constructors, where upstream is defined to
* upstream dependency injection is actively encouraged, since it means that the functionality of * denote a component that is closer to the source of the media. This pattern of upstream dependency
* the player is built up through the composition of components that can easily be exchanged for * injection is actively encouraged, since it means that the functionality of the player is built up
* alternate implementations. For example a {@link MediaPeriod} implementation may require a * through the composition of components that can easily be exchanged for alternate implementations.
* further upstream data loading component to be injected through its constructor, with different * For example a {@link MediaPeriod} implementation may require a further upstream data loading
* implementations enabling the loading of data from various sources. * component to be injected through its constructor, with different implementations enabling the
* loading of data from various sources.
* *
* <a name="Threading"></a> * <a name="Threading"></a>
* <h3>Threading model</h3> * <h3>Threading model</h3>

View File

@ -24,7 +24,9 @@ import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelector.InvalidationListener; 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.PriorityHandlerThread;
import com.google.android.exoplayer2.util.StandaloneMediaClock;
import com.google.android.exoplayer2.util.TraceUtil; import com.google.android.exoplayer2.util.TraceUtil;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;

View File

@ -18,7 +18,7 @@ package com.google.android.exoplayer2;
import java.io.IOException; 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 { public class ParserException extends IOException {

View File

@ -16,8 +16,10 @@
package com.google.android.exoplayer2; package com.google.android.exoplayer2;
import com.google.android.exoplayer2.ExoPlayer.ExoPlayerComponent; 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.source.SampleStream;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MediaClock;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import java.io.IOException; import java.io.IOException;

View File

@ -16,16 +16,21 @@
package com.google.android.exoplayer2; package com.google.android.exoplayer2;
import com.google.android.exoplayer2.audio.AudioCapabilities; 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.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.drm.DrmSessionManager;
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
import com.google.android.exoplayer2.metadata.MetadataRenderer; 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.Id3Frame;
import com.google.android.exoplayer2.metadata.id3.Id3Parser;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.TextRenderer; import com.google.android.exoplayer2.text.TextRenderer;
import com.google.android.exoplayer2.trackselection.TrackSelector; 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.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import android.media.AudioManager; import android.media.AudioManager;
@ -60,17 +65,17 @@ public final class SimpleExoPlayer implements ExoPlayer {
* A listener for debugging information. * A listener for debugging information.
*/ */
public interface DebugListener { public interface DebugListener {
void onAudioEnabled(CodecCounters counters); void onAudioEnabled(DecoderCounters counters);
void onAudioSessionId(int audioSessionId); void onAudioSessionId(int audioSessionId);
void onAudioDecoderInitialized(String decoderName, long elapsedRealtimeMs, void onAudioDecoderInitialized(String decoderName, long elapsedRealtimeMs,
long initializationDurationMs); long initializationDurationMs);
void onAudioFormatChanged(Format format); void onAudioFormatChanged(Format format);
void onAudioDisabled(CodecCounters counters); void onAudioDisabled(DecoderCounters counters);
void onVideoEnabled(CodecCounters counters); void onVideoEnabled(DecoderCounters counters);
void onVideoDecoderInitialized(String decoderName, long elapsedRealtimeMs, void onVideoDecoderInitialized(String decoderName, long elapsedRealtimeMs,
long initializationDurationMs); long initializationDurationMs);
void onVideoFormatChanged(Format format); void onVideoFormatChanged(Format format);
void onVideoDisabled(CodecCounters counters); void onVideoDisabled(DecoderCounters counters);
void onDroppedFrames(int count, long elapsed); void onDroppedFrames(int count, long elapsed);
void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs); void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs);
} }
@ -106,8 +111,8 @@ public final class SimpleExoPlayer implements ExoPlayer {
private Id3MetadataListener id3MetadataListener; private Id3MetadataListener id3MetadataListener;
private VideoListener videoListener; private VideoListener videoListener;
private DebugListener debugListener; private DebugListener debugListener;
private CodecCounters videoCodecCounters; private DecoderCounters videoDecoderCounters;
private CodecCounters audioCodecCounters; private DecoderCounters audioDecoderCounters;
private int audioSessionId; private int audioSessionId;
/* package */ SimpleExoPlayer(Context context, TrackSelector trackSelector, /* 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. * current media.
*/ */
public CodecCounters getVideoCodecCounters() { public DecoderCounters getVideoDecoderCounters() {
return videoCodecCounters; 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. * current media.
*/ */
public CodecCounters getAudioCodecCounters() { public DecoderCounters getAudioDecoderCounters() {
return audioCodecCounters; return audioDecoderCounters;
} }
/** /**
@ -413,7 +418,7 @@ public final class SimpleExoPlayer implements ExoPlayer {
renderersList.add(textRenderer); renderersList.add(textRenderer);
MetadataRenderer<List<Id3Frame>> id3Renderer = new MetadataRenderer<>(componentListener, MetadataRenderer<List<Id3Frame>> id3Renderer = new MetadataRenderer<>(componentListener,
mainHandler.getLooper(), new Id3Parser()); mainHandler.getLooper(), new Id3Decoder());
renderersList.add(id3Renderer); renderersList.add(id3Renderer);
} }
@ -482,8 +487,8 @@ public final class SimpleExoPlayer implements ExoPlayer {
// VideoRendererEventListener implementation // VideoRendererEventListener implementation
@Override @Override
public void onVideoEnabled(CodecCounters counters) { public void onVideoEnabled(DecoderCounters counters) {
videoCodecCounters = counters; videoDecoderCounters = counters;
if (debugListener != null) { if (debugListener != null) {
debugListener.onVideoEnabled(counters); debugListener.onVideoEnabled(counters);
} }
@ -530,19 +535,19 @@ public final class SimpleExoPlayer implements ExoPlayer {
} }
@Override @Override
public void onVideoDisabled(CodecCounters counters) { public void onVideoDisabled(DecoderCounters counters) {
if (debugListener != null) { if (debugListener != null) {
debugListener.onVideoDisabled(counters); debugListener.onVideoDisabled(counters);
} }
videoFormat = null; videoFormat = null;
videoCodecCounters = null; videoDecoderCounters = null;
} }
// AudioRendererEventListener implementation // AudioRendererEventListener implementation
@Override @Override
public void onAudioEnabled(CodecCounters counters) { public void onAudioEnabled(DecoderCounters counters) {
audioCodecCounters = counters; audioDecoderCounters = counters;
if (debugListener != null) { if (debugListener != null) {
debugListener.onAudioEnabled(counters); debugListener.onAudioEnabled(counters);
} }
@ -582,12 +587,12 @@ public final class SimpleExoPlayer implements ExoPlayer {
} }
@Override @Override
public void onAudioDisabled(CodecCounters counters) { public void onAudioDisabled(DecoderCounters counters) {
if (debugListener != null) { if (debugListener != null) {
debugListener.onAudioDisabled(counters); debugListener.onAudioDisabled(counters);
} }
audioFormat = null; audioFormat = null;
audioCodecCounters = null; audioDecoderCounters = null;
audioSessionId = AudioTrack.SESSION_ID_NOT_SET; audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
} }

View File

@ -13,10 +13,13 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.Format;
import com.google.android.exoplayer2.drm.DrmInitData; 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; import java.nio.ByteBuffer;

View File

@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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) { public AudioDecoderException(String detailMessage) {
super(detailMessage); super(detailMessage);

View File

@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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 com.google.android.exoplayer2.util.Assertions;
import android.os.Handler; import android.os.Handler;
@ -29,10 +31,10 @@ public interface AudioRendererEventListener {
/** /**
* Invoked when the renderer is enabled. * 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. * remains enabled.
*/ */
void onAudioEnabled(CodecCounters counters); void onAudioEnabled(DecoderCounters counters);
/** /**
* Invoked when the audio session is set. * Invoked when the audio session is set.
@ -73,9 +75,9 @@ public interface AudioRendererEventListener {
/** /**
* Invoked when the renderer is disabled. * 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}. * Dispatches events to a {@link AudioRendererEventListener}.
@ -90,12 +92,12 @@ public interface AudioRendererEventListener {
this.listener = listener; this.listener = listener;
} }
public void enabled(final CodecCounters codecCounters) { public void enabled(final DecoderCounters decoderCounters) {
if (listener != null) { if (listener != null) {
handler.post(new Runnable() { handler.post(new Runnable() {
@Override @Override
public void run() { 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) { if (listener != null) {
handler.post(new Runnable() { handler.post(new Runnable() {
@Override @Override

View File

@ -16,9 +16,7 @@
package com.google.android.exoplayer2.audio; package com.google.android.exoplayer2.audio;
import com.google.android.exoplayer2.C; 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.Assertions;
import com.google.android.exoplayer2.util.DtsUtil;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;

View File

@ -13,10 +13,12 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.Format;
import com.google.android.exoplayer2.drm.DrmInitData; 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; import java.nio.ByteBuffer;

View File

@ -13,13 +13,19 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.C;
import com.google.android.exoplayer2.MediaCodecUtil.DecoderQueryException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.audio.AudioCapabilities; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.audio.AudioTrack; 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.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.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
@ -148,7 +154,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
if (allowPassthrough(mimeType) && mediaCodecSelector.getPassthroughDecoderInfo() != null) { if (allowPassthrough(mimeType) && mediaCodecSelector.getPassthroughDecoderInfo() != null) {
return ADAPTIVE_NOT_SEAMLESS | FORMAT_HANDLED; return ADAPTIVE_NOT_SEAMLESS | FORMAT_HANDLED;
} }
MediaCodecDecoderInfo decoderInfo = mediaCodecSelector.getDecoderInfo(mimeType, MediaCodecInfo decoderInfo = mediaCodecSelector.getDecoderInfo(mimeType,
format.requiresSecureDecryption); format.requiresSecureDecryption);
if (decoderInfo == null) { if (decoderInfo == null) {
return FORMAT_UNSUPPORTED_SUBTYPE; return FORMAT_UNSUPPORTED_SUBTYPE;
@ -164,10 +170,10 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
} }
@Override @Override
protected MediaCodecDecoderInfo getDecoderInfo(MediaCodecSelector mediaCodecSelector, protected MediaCodecInfo getDecoderInfo(MediaCodecSelector mediaCodecSelector,
Format format, boolean requiresSecureDecoder) throws DecoderQueryException { Format format, boolean requiresSecureDecoder) throws DecoderQueryException {
if (allowPassthrough(format.sampleMimeType)) { if (allowPassthrough(format.sampleMimeType)) {
MediaCodecDecoderInfo passthroughDecoderInfo = mediaCodecSelector.getPassthroughDecoderInfo(); MediaCodecInfo passthroughDecoderInfo = mediaCodecSelector.getPassthroughDecoderInfo();
if (passthroughDecoderInfo != null) { if (passthroughDecoderInfo != null) {
passthroughEnabled = true; passthroughEnabled = true;
return passthroughDecoderInfo; return passthroughDecoderInfo;
@ -255,7 +261,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
@Override @Override
protected void onEnabled(boolean joining) throws ExoPlaybackException { protected void onEnabled(boolean joining) throws ExoPlaybackException {
super.onEnabled(joining); super.onEnabled(joining);
eventDispatcher.enabled(codecCounters); eventDispatcher.enabled(decoderCounters);
} }
@Override @Override
@ -287,8 +293,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
try { try {
super.onDisabled(); super.onDisabled();
} finally { } finally {
codecCounters.ensureUpdated(); decoderCounters.ensureUpdated();
eventDispatcher.disabled(codecCounters); eventDispatcher.disabled(decoderCounters);
} }
} }
} }
@ -326,7 +332,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
if (shouldSkip) { if (shouldSkip) {
codec.releaseOutputBuffer(bufferIndex, false); codec.releaseOutputBuffer(bufferIndex, false);
codecCounters.skippedOutputBufferCount++; decoderCounters.skippedOutputBufferCount++;
audioTrack.handleDiscontinuity(); audioTrack.handleDiscontinuity();
return true; return true;
} }
@ -378,7 +384,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
// Release the buffer if it was consumed. // Release the buffer if it was consumed.
if ((handleBufferResult & AudioTrack.RESULT_BUFFER_CONSUMED) != 0) { if ((handleBufferResult & AudioTrack.RESULT_BUFFER_CONSUMED) != 0) {
codec.releaseOutputBuffer(bufferIndex, false); codec.releaseOutputBuffer(bufferIndex, false);
codecCounters.renderedOutputBufferCount++; decoderCounters.renderedOutputBufferCount++;
return true; return true;
} }

View File

@ -13,20 +13,19 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.C;
import com.google.android.exoplayer2.CodecCounters;
import com.google.android.exoplayer2.DecoderInputBuffer;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.MediaClock;
import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.audio.AudioCapabilities; import com.google.android.exoplayer2.audio.AudioRendererEventListener.EventDispatcher;
import com.google.android.exoplayer2.audio.AudioTrack; 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.MimeTypes;
import com.google.android.exoplayer2.util.TraceUtil; import com.google.android.exoplayer2.util.TraceUtil;
@ -38,12 +37,12 @@ import android.os.SystemClock;
/** /**
* Decodes and renders audio using a {@link SimpleDecoder}. * 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 EventDispatcher eventDispatcher;
private final FormatHolder formatHolder; private final FormatHolder formatHolder;
private CodecCounters codecCounters; private DecoderCounters decoderCounters;
private Format inputFormat; private Format inputFormat;
private SimpleDecoder<DecoderInputBuffer, ? extends SimpleOutputBuffer, private SimpleDecoder<DecoderInputBuffer, ? extends SimpleOutputBuffer,
? extends AudioDecoderException> decoder; ? extends AudioDecoderException> decoder;
@ -61,7 +60,7 @@ public abstract class AudioDecoderRenderer extends Renderer implements MediaCloc
private boolean audioTrackHasData; private boolean audioTrackHasData;
private long lastFeedElapsedRealtimeMs; private long lastFeedElapsedRealtimeMs;
public AudioDecoderRenderer() { public SimpleDecoderAudioRenderer() {
this(null, null); this(null, null);
} }
@ -70,7 +69,7 @@ public abstract class AudioDecoderRenderer extends Renderer implements MediaCloc
* null if delivery of events is not required. * null if delivery of events is not required.
* @param eventListener A listener of events. May be 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) { AudioRendererEventListener eventListener) {
this (eventHandler, eventListener, null, AudioManager.STREAM_MUSIC); 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. * default capabilities (no encoded audio passthrough support) should be assumed.
* @param streamType The type of audio stream for the {@link AudioTrack}. * @param streamType The type of audio stream for the {@link AudioTrack}.
*/ */
public AudioDecoderRenderer(Handler eventHandler, public SimpleDecoderAudioRenderer(Handler eventHandler,
AudioRendererEventListener eventListener, AudioCapabilities audioCapabilities, AudioRendererEventListener eventListener, AudioCapabilities audioCapabilities,
int streamType) { int streamType) {
eventDispatcher = new EventDispatcher(eventHandler, eventListener); eventDispatcher = new EventDispatcher(eventHandler, eventListener);
@ -119,7 +118,7 @@ public abstract class AudioDecoderRenderer extends Renderer implements MediaCloc
long codecInitializedTimestamp = SystemClock.elapsedRealtime(); long codecInitializedTimestamp = SystemClock.elapsedRealtime();
eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp, eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp,
codecInitializedTimestamp - codecInitializingTimestamp); codecInitializedTimestamp - codecInitializingTimestamp);
codecCounters.codecInitCount++; decoderCounters.codecInitCount++;
} catch (AudioDecoderException e) { } catch (AudioDecoderException e) {
throw ExoPlaybackException.createForRenderer(e, getIndex()); throw ExoPlaybackException.createForRenderer(e, getIndex());
} }
@ -135,7 +134,7 @@ public abstract class AudioDecoderRenderer extends Renderer implements MediaCloc
| AudioDecoderException e) { | AudioDecoderException e) {
throw ExoPlaybackException.createForRenderer(e, getIndex()); throw ExoPlaybackException.createForRenderer(e, getIndex());
} }
codecCounters.ensureUpdated(); decoderCounters.ensureUpdated();
} }
@Override @Override
@ -170,7 +169,7 @@ public abstract class AudioDecoderRenderer extends Renderer implements MediaCloc
if (outputBuffer == null) { if (outputBuffer == null) {
return false; return false;
} }
codecCounters.skippedOutputBufferCount += outputBuffer.skippedOutputBufferCount; decoderCounters.skippedOutputBufferCount += outputBuffer.skippedOutputBufferCount;
} }
if (outputBuffer.isEndOfStream()) { if (outputBuffer.isEndOfStream()) {
@ -219,7 +218,7 @@ public abstract class AudioDecoderRenderer extends Renderer implements MediaCloc
// Release the buffer if it was consumed. // Release the buffer if it was consumed.
if ((handleBufferResult & AudioTrack.RESULT_BUFFER_CONSUMED) != 0) { if ((handleBufferResult & AudioTrack.RESULT_BUFFER_CONSUMED) != 0) {
codecCounters.renderedOutputBufferCount++; decoderCounters.renderedOutputBufferCount++;
outputBuffer.release(); outputBuffer.release();
outputBuffer = null; outputBuffer = null;
return true; return true;
@ -256,7 +255,7 @@ public abstract class AudioDecoderRenderer extends Renderer implements MediaCloc
} }
inputBuffer.flip(); inputBuffer.flip();
decoder.queueInputBuffer(inputBuffer); decoder.queueInputBuffer(inputBuffer);
codecCounters.inputBufferCount++; decoderCounters.inputBufferCount++;
inputBuffer = null; inputBuffer = null;
return true; return true;
} }
@ -307,8 +306,8 @@ public abstract class AudioDecoderRenderer extends Renderer implements MediaCloc
@Override @Override
protected void onEnabled(boolean joining) throws ExoPlaybackException { protected void onEnabled(boolean joining) throws ExoPlaybackException {
codecCounters = new CodecCounters(); decoderCounters = new DecoderCounters();
eventDispatcher.enabled(codecCounters); eventDispatcher.enabled(decoderCounters);
} }
@Override @Override
@ -343,12 +342,12 @@ public abstract class AudioDecoderRenderer extends Renderer implements MediaCloc
if (decoder != null) { if (decoder != null) {
decoder.release(); decoder.release();
decoder = null; decoder = null;
codecCounters.codecReleaseCount++; decoderCounters.codecReleaseCount++;
} }
audioTrack.release(); audioTrack.release();
} finally { } finally {
codecCounters.ensureUpdated(); decoderCounters.ensureUpdated();
eventDispatcher.disabled(codecCounters); eventDispatcher.disabled(decoderCounters);
} }
} }

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.android.exoplayer2.util; package com.google.android.exoplayer2.decoder;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.android.exoplayer2; package com.google.android.exoplayer2.decoder;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.android.exoplayer2.extensions; package com.google.android.exoplayer2.decoder;
/** /**
* A media decoder. * A media decoder.

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.android.exoplayer2; package com.google.android.exoplayer2.decoder;
/** /**
* Maintains codec event counts, for debugging purposes only. * 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 * 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. * 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. * 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. * 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; codecInitCount += other.codecInitCount;
codecReleaseCount += other.codecReleaseCount; codecReleaseCount += other.codecReleaseCount;
inputBufferCount += other.inputBufferCount; inputBufferCount += other.inputBufferCount;

View File

@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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; import java.nio.ByteBuffer;

View File

@ -13,9 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.android.exoplayer2.extensions; package com.google.android.exoplayer2.decoder;
import com.google.android.exoplayer2.util.Buffer;
/** /**
* Output buffer decoded by a {@link Decoder}. * Output buffer decoded by a {@link Decoder}.

View File

@ -13,10 +13,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.C;
import com.google.android.exoplayer2.DecoderInputBuffer;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import java.util.LinkedList; import java.util.LinkedList;
@ -27,20 +26,6 @@ import java.util.LinkedList;
public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends OutputBuffer, public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends OutputBuffer,
E extends Exception> implements Decoder<I, O, E> { 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 Thread decodeThread;
private final Object lock; private final Object lock;

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.android.exoplayer2.extensions; package com.google.android.exoplayer2.decoder;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;

View File

@ -16,9 +16,9 @@
package com.google.android.exoplayer2.extractor; package com.google.android.exoplayer2.extractor;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DecoderInputBuffer;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder; 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.Allocation;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;

View File

@ -52,7 +52,7 @@ public final class GaplessInfoHolder {
/** /**
* Populates the holder with data from an MP3 Xing header, if valid and non-zero. * 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. * @return Whether the holder was populated.
*/ */
public boolean setFromXingHeaderValue(int value) { public boolean setFromXingHeaderValue(int value) {

View File

@ -13,10 +13,12 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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 { public final class MpegAudioHeader {

View File

@ -101,7 +101,7 @@ import java.util.List;
hasOutputFormat = true; hasOutputFormat = true;
} else if (packetType == AVC_PACKET_TYPE_AVC_NALU) { } else if (packetType == AVC_PACKET_TYPE_AVC_NALU) {
// TODO: Deduplicate with Mp4Extractor. // 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. // they're only 1 or 2 bytes long.
byte[] nalLengthData = nalLength.data; byte[] nalLengthData = nalLength.data;
nalLengthData[0] = 0; nalLengthData[0] = 0;

View File

@ -920,7 +920,7 @@ public final class MatroskaExtractor implements Extractor {
if (CODEC_ID_H264.equals(track.codecId) || CODEC_ID_H265.equals(track.codecId)) { if (CODEC_ID_H264.equals(track.codecId) || CODEC_ID_H265.equals(track.codecId)) {
// TODO: Deduplicate with Mp4Extractor. // 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. // they're only 1 or 2 bytes long.
byte[] nalLengthData = nalLength.data; byte[] nalLengthData = nalLength.data;
nalLengthData[0] = 0; nalLengthData[0] = 0;

View File

@ -26,7 +26,7 @@ import com.google.android.exoplayer2.extractor.GaplessInfoHolder;
import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.extractor.TrackOutput; 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.ParsableByteArray;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;

View File

@ -16,7 +16,7 @@
package com.google.android.exoplayer2.extractor.mp3; package com.google.android.exoplayer2.extractor.mp3;
import com.google.android.exoplayer2.C; 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.ParsableByteArray;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;

View File

@ -16,7 +16,7 @@
package com.google.android.exoplayer2.extractor.mp3; package com.google.android.exoplayer2.extractor.mp3;
import com.google.android.exoplayer2.C; 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.ParsableByteArray;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;

View File

@ -18,9 +18,9 @@ package com.google.android.exoplayer2.extractor.mp4;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.ParserException; 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.drm.DrmInitData;
import com.google.android.exoplayer2.extractor.GaplessInfoHolder; 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.Assertions;
import com.google.android.exoplayer2.util.CodecSpecificDataUtil; import com.google.android.exoplayer2.util.CodecSpecificDataUtil;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
@ -49,7 +49,7 @@ import java.util.List;
/** /**
* Parses a trak atom (defined in 14496-12). * 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 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 * @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. * duration should be parsed from the tkhd atom.
@ -93,7 +93,7 @@ import java.util.List;
* Parses an stbl atom (defined in 14496-12). * Parses an stbl atom (defined in 14496-12).
* *
* @param track Track to which this sample table corresponds. * @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. * @param gaplessInfoHolder Holder to populate with gapless playback information.
* @return Sample table described by the stbl atom. * @return Sample table described by the stbl atom.
* @throws ParserException If the resulting sample sequence does not contain a sync sample. * @throws ParserException If the resulting sample sequence does not contain a sync sample.
@ -191,7 +191,7 @@ import java.util.List;
remainingSamplesAtTimestampOffset = ctts.readUnsignedIntToInt(); remainingSamplesAtTimestampOffset = ctts.readUnsignedIntToInt();
// The BMFF spec (ISO 14496-12) states that sample offsets should be unsigned integers // 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 // 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 // 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). // set, which is never true in practice because sample offsets are always small).
timestampOffset = ctts.readInt(); timestampOffset = ctts.readInt();
@ -378,14 +378,14 @@ import java.util.List;
/** /**
* Parses a udta atom. * 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 isQuickTime True for QuickTime media. False otherwise.
* @param out {@link GaplessInfoHolder} to populate with gapless playback information. * @param out {@link GaplessInfoHolder} to populate with gapless playback information.
*/ */
public static void parseUdta(Atom.LeafAtom udtaAtom, boolean isQuickTime, GaplessInfoHolder out) { public static void parseUdta(Atom.LeafAtom udtaAtom, boolean isQuickTime, GaplessInfoHolder out) {
if (isQuickTime) { if (isQuickTime) {
// Meta boxes are regular boxes rather than full boxes in QuickTime. For now, don't try and // Meta boxes are regular boxes rather than full boxes in QuickTime. For now, don't try and
// parse one. // decode one.
return; return;
} }
ParsableByteArray udtaData = udtaAtom.data; ParsableByteArray udtaData = udtaAtom.data;
@ -535,7 +535,7 @@ import java.util.List;
/** /**
* Parses an hdlr atom. * Parses an hdlr atom.
* *
* @param hdlr The hdlr atom to parse. * @param hdlr The hdlr atom to decode.
* @return The track type. * @return The track type.
*/ */
private static int parseHdlr(ParsableByteArray hdlr) { private static int parseHdlr(ParsableByteArray hdlr) {
@ -556,7 +556,7 @@ import java.util.List;
/** /**
* Parses an mdhd atom (defined in 14496-12). * 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 * @return A pair consisting of the media timescale defined as the number of time units that pass
* in one second, and the language code. * in one second, and the language code.
*/ */
@ -577,7 +577,7 @@ import java.util.List;
/** /**
* Parses a stsd atom (defined in 14496-12). * 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 trackId The track's identifier in its container.
* @param rotationDegrees The rotation of the track in degrees. * @param rotationDegrees The rotation of the track in degrees.
* @param language The language of the track. * @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). * 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 * @return Pair of edit list durations and edit list media times, or a pair of nulls if they are
* not present. * not present.
*/ */

View File

@ -538,7 +538,7 @@ public final class FragmentedMp4Extractor implements Extractor {
/** /**
* Parses a saio atom (defined in 14496-12). * 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. * @param out The {@link TrackFragment} to populate with data from the saio atom.
*/ */
private static void parseSaio(ParsableByteArray saio, TrackFragment out) throws ParserException { 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 * 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. * 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. * @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 * @return The {@link TrackBundle} to which the {@link TrackFragment} belongs, or null if the tfhd
* does not refer to any {@link TrackBundle}. * does not refer to any {@link TrackBundle}.
@ -621,7 +621,7 @@ public final class FragmentedMp4Extractor implements Extractor {
* which parsed data should be placed. * which parsed data should be placed.
* @param decodeTime The decode time of the first sample in the fragment run. * @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 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, private static void parseTrun(TrackBundle trackBundle, long decodeTime, int flags,
ParsableByteArray trun) { ParsableByteArray trun) {
@ -681,7 +681,7 @@ public final class FragmentedMp4Extractor implements Extractor {
if (sampleCompositionTimeOffsetsPresent) { if (sampleCompositionTimeOffsetsPresent) {
// The BMFF spec (ISO 14496-12) states that sample offsets should be unsigned integers in // 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 // 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 // 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). // set, which is never true in practice because sample offsets are always small).
int sampleOffset = trun.readInt(); int sampleOffset = trun.readInt();
@ -926,7 +926,7 @@ public final class FragmentedMp4Extractor implements Extractor {
TrackOutput output = currentTrackBundle.output; TrackOutput output = currentTrackBundle.output;
int sampleIndex = currentTrackBundle.currentSampleIndex; int sampleIndex = currentTrackBundle.currentSampleIndex;
if (track.nalUnitLengthFieldLength != -1) { 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. // they're only 1 or 2 bytes long.
byte[] nalLengthData = nalLength.data; byte[] nalLengthData = nalLength.data;
nalLengthData[0] = 0; nalLengthData[0] = 0;
@ -1044,7 +1044,7 @@ public final class FragmentedMp4Extractor implements Extractor {
return 1 + vectorSize + subsampleDataLength; 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) { private static boolean shouldParseLeafAtom(int atom) {
return atom == Atom.TYPE_hdlr || atom == Atom.TYPE_mdhd || atom == Atom.TYPE_mvhd 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 || 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; || 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) { private static boolean shouldParseContainerAtom(int atom) {
return atom == Atom.TYPE_moov || atom == Atom.TYPE_trak || atom == Atom.TYPE_mdia 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 || atom == Atom.TYPE_minf || atom == Atom.TYPE_stbl || atom == Atom.TYPE_moof

View File

@ -401,7 +401,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
} }
input.skipFully((int) skipAmount); input.skipFully((int) skipAmount);
if (track.track.nalUnitLengthFieldLength != -1) { 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. // they're only 1 or 2 bytes long.
byte[] nalLengthData = nalLength.data; byte[] nalLengthData = nalLength.data;
nalLengthData[0] = 0; 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) { private static boolean shouldParseLeafAtom(int atom) {
return atom == Atom.TYPE_mdhd || atom == Atom.TYPE_mvhd || atom == Atom.TYPE_hdlr 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) { private static boolean shouldParseContainerAtom(int atom) {
return atom == Atom.TYPE_moov || atom == Atom.TYPE_trak || atom == Atom.TYPE_mdia return atom == Atom.TYPE_moov || atom == Atom.TYPE_trak || atom == Atom.TYPE_mdia

View File

@ -17,8 +17,8 @@ package com.google.android.exoplayer2.extractor.ts;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; 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.extractor.TrackOutput;
import com.google.android.exoplayer2.util.Ac3Util;
import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray; 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 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 * @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) { public Ac3Reader(TrackOutput output, boolean isEac3) {
super(output); super(output);

View File

@ -17,8 +17,8 @@ package com.google.android.exoplayer2.extractor.ts;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; 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.extractor.TrackOutput;
import com.google.android.exoplayer2.util.DtsUtil;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
/** /**

View File

@ -119,7 +119,7 @@ import java.util.Collections;
// passed to csdDataTargetBuffer. // passed to csdDataTargetBuffer.
int bytesAlreadyPassed = lengthToStartCode < 0 ? -lengthToStartCode : 0; int bytesAlreadyPassed = lengthToStartCode < 0 ? -lengthToStartCode : 0;
if (csdBuffer.onStartCode(startCodeValue, bytesAlreadyPassed)) { 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); Pair<Format, Long> result = parseCsdBuffer(csdBuffer);
output.format(result.first); output.format(result.first);
frameDurationUs = result.second; frameDurationUs = result.second;

View File

@ -330,7 +330,7 @@ import java.util.List;
} }
int picParameterSetId = bitArray.readUnsignedExpGolombCodedInt(); int picParameterSetId = bitArray.readUnsignedExpGolombCodedInt();
if (pps.indexOfKey(picParameterSetId) < 0) { 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; isFilling = false;
return; return;
} }

View File

@ -18,7 +18,7 @@ package com.google.android.exoplayer2.extractor.ts;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.TrackOutput; 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.ParsableByteArray;
/** /**

View File

@ -18,7 +18,7 @@ package com.google.android.exoplayer2.extractor.ts;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.TrackOutput; 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.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
@ -51,7 +51,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
payloadSize += b; payloadSize += b;
} while (b == 0xFF); } while (b == 0xFF);
// Process the payload. // 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) // Ignore country_code (1) + provider_code (2) + user_identifier (4)
// + user_data_type_code (1). // + user_data_type_code (1).
seiBuffer.skipBytes(8); seiBuffer.skipBytes(8);

View File

@ -13,57 +13,59 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.Assertions;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import android.annotation.TargetApi; 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.CodecCapabilities;
import android.media.MediaCodecInfo.CodecProfileLevel; import android.media.MediaCodecInfo.CodecProfileLevel;
import android.media.MediaCodecInfo.VideoCapabilities;
import android.util.Pair; import android.util.Pair;
/** /**
* Contains information about a media decoder. * Contains information about a {@link MediaCodec} decoder.
*/ */
@TargetApi(16) @TargetApi(16)
public final class MediaCodecDecoderInfo { public final class MediaCodecInfo {
/** /**
* The name of the decoder. * The name of the decoder.
* <p> * <p>
* May be passed to {@link android.media.MediaCodec#createByCodecName(String)} to create an * May be passed to {@link MediaCodec#createByCodecName(String)} to create an instance of the
* instance of the decoder. * decoder.
*/ */
public final String name; public final String name;
/** /**
* Whether the decoder supports seamless resolution switches. * Whether the decoder supports seamless resolution switches.
* *
* @see MediaCodecInfo.CodecCapabilities#isFeatureSupported(String) * @see CodecCapabilities#isFeatureSupported(String)
* @see MediaCodecInfo.CodecCapabilities#FEATURE_AdaptivePlayback * @see CodecCapabilities#FEATURE_AdaptivePlayback
*/ */
public final boolean adaptive; public final boolean adaptive;
private final String mimeType; private final String mimeType;
private final CodecCapabilities capabilities; private final CodecCapabilities capabilities;
public static MediaCodecDecoderInfo newPassthroughInstance(String name) { public static MediaCodecInfo newPassthroughInstance(String name) {
return new MediaCodecDecoderInfo(name, null, null); return new MediaCodecInfo(name, null, null);
} }
public static MediaCodecDecoderInfo newInstance(String name, String mimeType, public static MediaCodecInfo newInstance(String name, String mimeType,
CodecCapabilities capabilities) { CodecCapabilities capabilities) {
return new MediaCodecDecoderInfo(name, mimeType, capabilities); return new MediaCodecInfo(name, mimeType, capabilities);
} }
/** /**
* @param name The name of the decoder. * @param name The name of the decoder.
* @param capabilities The capabilities 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.name = Assertions.checkNotNull(name);
this.mimeType = mimeType; this.mimeType = mimeType;
this.capabilities = capabilities; this.capabilities = capabilities;
@ -101,13 +103,12 @@ public final class MediaCodecDecoderInfo {
if (!codecMimeType.equals(MimeTypes.VIDEO_H265)) { if (!codecMimeType.equals(MimeTypes.VIDEO_H265)) {
return true; return true;
} }
Pair<Integer, Integer> codecProfileAndLevel = Pair<Integer, Integer> codecProfileAndLevel = MediaCodecUtil.getHevcProfileAndLevel(codec);
MediaCodecUtil.getHevcProfileAndLevel(codec);
if (codecProfileAndLevel == null) { if (codecProfileAndLevel == null) {
// If we don't know any better, we assume that the profile and level are supported. // If we don't know any better, we assume that the profile and level are supported.
return true; return true;
} }
for (MediaCodecInfo.CodecProfileLevel capabilities : getProfileLevels()) { for (CodecProfileLevel capabilities : getProfileLevels()) {
if (capabilities.profile == codecProfileAndLevel.first if (capabilities.profile == codecProfileAndLevel.first
&& capabilities.level >= codecProfileAndLevel.second) { && capabilities.level >= codecProfileAndLevel.second) {
return true; return true;
@ -130,7 +131,7 @@ public final class MediaCodecDecoderInfo {
if (capabilities == null) { if (capabilities == null) {
return false; return false;
} }
MediaCodecInfo.VideoCapabilities videoCapabilities = capabilities.getVideoCapabilities(); VideoCapabilities videoCapabilities = capabilities.getVideoCapabilities();
return videoCapabilities != null && videoCapabilities.isSizeSupported(width, height); return videoCapabilities != null && videoCapabilities.isSizeSupported(width, height);
} }
@ -149,7 +150,7 @@ public final class MediaCodecDecoderInfo {
if (capabilities == null) { if (capabilities == null) {
return false; return false;
} }
MediaCodecInfo.VideoCapabilities videoCapabilities = capabilities.getVideoCapabilities(); VideoCapabilities videoCapabilities = capabilities.getVideoCapabilities();
return videoCapabilities != null && videoCapabilities.areSizeAndRateSupported(width, height, return videoCapabilities != null && videoCapabilities.areSizeAndRateSupported(width, height,
frameRate); frameRate);
} }
@ -167,7 +168,7 @@ public final class MediaCodecDecoderInfo {
if (capabilities == null) { if (capabilities == null) {
return false; return false;
} }
MediaCodecInfo.AudioCapabilities audioCapabilities = capabilities.getAudioCapabilities(); AudioCapabilities audioCapabilities = capabilities.getAudioCapabilities();
return audioCapabilities != null && audioCapabilities.isSampleRateSupported(sampleRate); return audioCapabilities != null && audioCapabilities.isSampleRateSupported(sampleRate);
} }
@ -184,7 +185,7 @@ public final class MediaCodecDecoderInfo {
if (capabilities == null) { if (capabilities == null) {
return false; return false;
} }
MediaCodecInfo.AudioCapabilities audioCapabilities = capabilities.getAudioCapabilities(); AudioCapabilities audioCapabilities = capabilities.getAudioCapabilities();
return audioCapabilities != null && audioCapabilities.getMaxInputChannelCount() >= channelCount; return audioCapabilities != null && audioCapabilities.getMaxInputChannelCount() >= channelCount;
} }

View File

@ -13,11 +13,18 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.DrmSession;
import com.google.android.exoplayer2.drm.DrmSessionManager; 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.source.MediaPeriod;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.NalUnitUtil; import com.google.android.exoplayer2.util.NalUnitUtil;
@ -195,7 +202,7 @@ public abstract class MediaCodecRenderer extends Renderer {
private boolean outputStreamEnded; private boolean outputStreamEnded;
private boolean waitingForKeys; private boolean waitingForKeys;
protected CodecCounters codecCounters; protected DecoderCounters decoderCounters;
/** /**
* @param mediaCodecSelector A decoder selector. * @param mediaCodecSelector A decoder selector.
@ -248,16 +255,16 @@ public abstract class MediaCodecRenderer extends Renderer {
throws DecoderQueryException; throws DecoderQueryException;
/** /**
* Returns a {@link MediaCodecDecoderInfo} for a given format. * Returns a {@link MediaCodecInfo} for a given format.
* *
* @param mediaCodecSelector The decoder selector. * @param mediaCodecSelector The decoder selector.
* @param format The format for which a decoder is required. * @param format The format for which a decoder is required.
* @param requiresSecureDecoder Whether a secure 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. * suitable decoder exists.
* @throws DecoderQueryException Thrown if there was an error querying decoders. * @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 { Format format, boolean requiresSecureDecoder) throws DecoderQueryException {
return mediaCodecSelector.getDecoderInfo(format.sampleMimeType, requiresSecureDecoder); return mediaCodecSelector.getDecoderInfo(format.sampleMimeType, requiresSecureDecoder);
} }
@ -301,7 +308,7 @@ public abstract class MediaCodecRenderer extends Renderer {
} }
} }
MediaCodecDecoderInfo decoderInfo = null; MediaCodecInfo decoderInfo = null;
try { try {
decoderInfo = getDecoderInfo(mediaCodecSelector, format, drmSessionRequiresSecureDecoder); decoderInfo = getDecoderInfo(mediaCodecSelector, format, drmSessionRequiresSecureDecoder);
if (decoderInfo == null && drmSessionRequiresSecureDecoder) { if (decoderInfo == null && drmSessionRequiresSecureDecoder) {
@ -358,7 +365,7 @@ public abstract class MediaCodecRenderer extends Renderer {
? (SystemClock.elapsedRealtime() + MAX_CODEC_HOTSWAP_TIME_MS) : -1; ? (SystemClock.elapsedRealtime() + MAX_CODEC_HOTSWAP_TIME_MS) : -1;
inputIndex = -1; inputIndex = -1;
outputIndex = -1; outputIndex = -1;
codecCounters.codecInitCount++; decoderCounters.codecInitCount++;
} }
private void throwDecoderInitError(DecoderInitializationException e) private void throwDecoderInitError(DecoderInitializationException e)
@ -372,7 +379,7 @@ public abstract class MediaCodecRenderer extends Renderer {
@Override @Override
protected void onEnabled(boolean joining) throws ExoPlaybackException { protected void onEnabled(boolean joining) throws ExoPlaybackException {
codecCounters = new CodecCounters(); decoderCounters = new DecoderCounters();
} }
@Override @Override
@ -421,7 +428,7 @@ public abstract class MediaCodecRenderer extends Renderer {
codecReceivedEos = false; codecReceivedEos = false;
codecReconfigurationState = RECONFIGURATION_STATE_NONE; codecReconfigurationState = RECONFIGURATION_STATE_NONE;
codecReinitializationState = REINITIALIZATION_STATE_NONE; codecReinitializationState = REINITIALIZATION_STATE_NONE;
codecCounters.codecReleaseCount++; decoderCounters.codecReleaseCount++;
try { try {
codec.stop(); codec.stop();
} finally { } finally {
@ -456,7 +463,7 @@ public abstract class MediaCodecRenderer extends Renderer {
while (feedInputBuffer()) {} while (feedInputBuffer()) {}
TraceUtil.endSection(); TraceUtil.endSection();
} }
codecCounters.ensureUpdated(); decoderCounters.ensureUpdated();
} }
private void readFormat() throws ExoPlaybackException { private void readFormat() throws ExoPlaybackException {
@ -631,7 +638,7 @@ public abstract class MediaCodecRenderer extends Renderer {
inputIndex = -1; inputIndex = -1;
codecReceivedBuffers = true; codecReceivedBuffers = true;
codecReconfigurationState = RECONFIGURATION_STATE_NONE; codecReconfigurationState = RECONFIGURATION_STATE_NONE;
codecCounters.inputBufferCount++; decoderCounters.inputBufferCount++;
} catch (CryptoException e) { } catch (CryptoException e) {
throw ExoPlaybackException.createForRenderer(e, getIndex()); throw ExoPlaybackException.createForRenderer(e, getIndex());
} }

View File

@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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; import android.media.MediaCodec;
@ -30,13 +30,13 @@ public interface MediaCodecSelector {
MediaCodecSelector DEFAULT = new MediaCodecSelector() { MediaCodecSelector DEFAULT = new MediaCodecSelector() {
@Override @Override
public MediaCodecDecoderInfo getDecoderInfo(String mimeType, boolean requiresSecureDecoder) public MediaCodecInfo getDecoderInfo(String mimeType, boolean requiresSecureDecoder)
throws DecoderQueryException { throws DecoderQueryException {
return MediaCodecUtil.getDecoderInfo(mimeType, requiresSecureDecoder); return MediaCodecUtil.getDecoderInfo(mimeType, requiresSecureDecoder);
} }
@Override @Override
public MediaCodecDecoderInfo getPassthroughDecoderInfo() throws DecoderQueryException { public MediaCodecInfo getPassthroughDecoderInfo() throws DecoderQueryException {
return MediaCodecUtil.getPassthroughDecoderInfo(); return MediaCodecUtil.getPassthroughDecoderInfo();
} }
@ -47,20 +47,20 @@ public interface MediaCodecSelector {
* *
* @param mimeType The mime type for which a decoder is required. * @param mimeType The mime type for which a decoder is required.
* @param requiresSecureDecoder Whether a secure 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. * exists.
* @throws DecoderQueryException Thrown if there was an error querying decoders. * @throws DecoderQueryException Thrown if there was an error querying decoders.
*/ */
MediaCodecDecoderInfo getDecoderInfo(String mimeType, boolean requiresSecureDecoder) MediaCodecInfo getDecoderInfo(String mimeType, boolean requiresSecureDecoder)
throws DecoderQueryException; throws DecoderQueryException;
/** /**
* Selects a decoder to instantiate for audio passthrough. * 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. * exists.
* @throws DecoderQueryException Thrown if there was an error querying decoders. * @throws DecoderQueryException Thrown if there was an error querying decoders.
*/ */
MediaCodecDecoderInfo getPassthroughDecoderInfo() throws DecoderQueryException; MediaCodecInfo getPassthroughDecoderInfo() throws DecoderQueryException;
} }

View File

@ -13,14 +13,13 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecCapabilities; import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCodecInfo.CodecProfileLevel; import android.media.MediaCodecInfo.CodecProfileLevel;
import android.media.MediaCodecList; import android.media.MediaCodecList;
@ -58,15 +57,14 @@ public final class MediaCodecUtil {
} }
private static final String TAG = "MediaCodecUtil"; private static final String TAG = "MediaCodecUtil";
private static final MediaCodecDecoderInfo PASSTHROUGH_DECODER_INFO = private static final MediaCodecInfo PASSTHROUGH_DECODER_INFO =
MediaCodecDecoderInfo.newPassthroughInstance("OMX.google.raw.decoder"); MediaCodecInfo.newPassthroughInstance("OMX.google.raw.decoder");
private static final Map<String, Integer> HEVC_CODEC_STRING_TO_PROFILE_LEVEL; 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_HEV1 = "hev1";
private static final String CODEC_ID_HVC1 = "hvc1"; private static final String CODEC_ID_HVC1 = "hvc1";
private static final Pattern PROFILE_PATTERN = Pattern.compile("^\\D?(\\d+)$"); private static final Pattern PROFILE_PATTERN = Pattern.compile("^\\D?(\\d+)$");
private static final HashMap<CodecKey, List<MediaCodecDecoderInfo>> decoderInfosCache = private static final HashMap<CodecKey, List<MediaCodecInfo>> decoderInfosCache = new HashMap<>();
new HashMap<>();
// Lazily initialized. // Lazily initialized.
private static int maxH264DecodableFrameSize = -1; private static int maxH264DecodableFrameSize = -1;
@ -94,10 +92,10 @@ public final class MediaCodecUtil {
/** /**
* Returns information about a decoder suitable for audio passthrough. * 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. * exists.
*/ */
public static MediaCodecDecoderInfo getPassthroughDecoderInfo() { public static MediaCodecInfo getPassthroughDecoderInfo() {
// TODO: Return null if the raw decoder doesn't exist. // TODO: Return null if the raw decoder doesn't exist.
return PASSTHROUGH_DECODER_INFO; return PASSTHROUGH_DECODER_INFO;
} }
@ -108,29 +106,29 @@ public final class MediaCodecUtil {
* @param mimeType The mime type. * @param mimeType The mime type.
* @param secure Whether the decoder is required to support secure decryption. Always pass false * @param secure Whether the decoder is required to support secure decryption. Always pass false
* unless secure decryption really is required. * 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. * exists.
*/ */
public static MediaCodecDecoderInfo getDecoderInfo(String mimeType, boolean secure) public static MediaCodecInfo getDecoderInfo(String mimeType, boolean secure)
throws DecoderQueryException { throws DecoderQueryException {
List<MediaCodecDecoderInfo> decoderInfos = getDecoderInfos(mimeType, secure); List<MediaCodecInfo> decoderInfos = getDecoderInfos(mimeType, secure);
return decoderInfos.isEmpty() ? null : decoderInfos.get(0); 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}. * {@link MediaCodecList}.
* *
* @param mimeType The mime type. * @param mimeType The mime type.
* @param secure Whether the decoder is required to support secure decryption. Always pass false * @param secure Whether the decoder is required to support secure decryption. Always pass false
* unless secure decryption really is required. * 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}. * given by {@link MediaCodecList}.
*/ */
public static synchronized List<MediaCodecDecoderInfo> getDecoderInfos(String mimeType, public static synchronized List<MediaCodecInfo> getDecoderInfos(String mimeType,
boolean secure) throws DecoderQueryException { boolean secure) throws DecoderQueryException {
CodecKey key = new CodecKey(mimeType, secure); CodecKey key = new CodecKey(mimeType, secure);
List<MediaCodecDecoderInfo> decoderInfos = decoderInfosCache.get(key); List<MediaCodecInfo> decoderInfos = decoderInfosCache.get(key);
if (decoderInfos != null) { if (decoderInfos != null) {
return decoderInfos; return decoderInfos;
} }
@ -152,16 +150,16 @@ public final class MediaCodecUtil {
return decoderInfos; return decoderInfos;
} }
private static List<MediaCodecDecoderInfo> getDecoderInfosInternal( private static List<MediaCodecInfo> getDecoderInfosInternal(
CodecKey key, MediaCodecListCompat mediaCodecList) throws DecoderQueryException { CodecKey key, MediaCodecListCompat mediaCodecList) throws DecoderQueryException {
try { try {
List<MediaCodecDecoderInfo> decoderInfos = new ArrayList<>(); List<MediaCodecInfo> decoderInfos = new ArrayList<>();
String mimeType = key.mimeType; String mimeType = key.mimeType;
int numberOfCodecs = mediaCodecList.getCodecCount(); int numberOfCodecs = mediaCodecList.getCodecCount();
boolean secureDecodersExplicit = mediaCodecList.secureDecodersExplicit(); boolean secureDecodersExplicit = mediaCodecList.secureDecodersExplicit();
// Note: MediaCodecList is sorted by the framework such that the best decoders come first. // Note: MediaCodecList is sorted by the framework such that the best decoders come first.
for (int i = 0; i < numberOfCodecs; i++) { for (int i = 0; i < numberOfCodecs; i++) {
MediaCodecInfo codecInfo = mediaCodecList.getCodecInfoAt(i); android.media.MediaCodecInfo codecInfo = mediaCodecList.getCodecInfoAt(i);
String codecName = codecInfo.getName(); String codecName = codecInfo.getName();
if (isCodecUsableDecoder(codecInfo, codecName, secureDecodersExplicit)) { if (isCodecUsableDecoder(codecInfo, codecName, secureDecodersExplicit)) {
for (String supportedType : codecInfo.getSupportedTypes()) { for (String supportedType : codecInfo.getSupportedTypes()) {
@ -172,9 +170,9 @@ public final class MediaCodecUtil {
if ((secureDecodersExplicit && key.secure == secure) if ((secureDecodersExplicit && key.secure == secure)
|| (!secureDecodersExplicit && !key.secure)) { || (!secureDecodersExplicit && !key.secure)) {
decoderInfos.add( decoderInfos.add(
MediaCodecDecoderInfo.newInstance(codecName, mimeType, capabilities)); MediaCodecInfo.newInstance(codecName, mimeType, capabilities));
} else if (!secureDecodersExplicit && secure) { } else if (!secureDecodersExplicit && secure) {
decoderInfos.add(MediaCodecDecoderInfo.newInstance(codecName + ".secure", decoderInfos.add(MediaCodecInfo.newInstance(codecName + ".secure",
mimeType, capabilities)); mimeType, capabilities));
// It only makes sense to have one synthesized secure decoder, return immediately. // It only makes sense to have one synthesized secure decoder, return immediately.
return decoderInfos; return decoderInfos;
@ -205,7 +203,7 @@ public final class MediaCodecUtil {
/** /**
* Returns whether the specified codec is usable for decoding on the current device. * 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) { boolean secureDecodersExplicit) {
if (info.isEncoder() || (!secureDecodersExplicit && name.endsWith(".secure"))) { if (info.isEncoder() || (!secureDecodersExplicit && name.endsWith(".secure"))) {
return false; return false;
@ -277,7 +275,7 @@ public final class MediaCodecUtil {
public static int maxH264DecodableFrameSize() throws DecoderQueryException { public static int maxH264DecodableFrameSize() throws DecoderQueryException {
if (maxH264DecodableFrameSize == -1) { if (maxH264DecodableFrameSize == -1) {
int result = 0; int result = 0;
MediaCodecDecoderInfo decoderInfo = getDecoderInfo(MimeTypes.VIDEO_H264, false); MediaCodecInfo decoderInfo = getDecoderInfo(MimeTypes.VIDEO_H264, false);
if (decoderInfo != null) { if (decoderInfo != null) {
for (CodecProfileLevel profileLevel : decoderInfo.getProfileLevels()) { for (CodecProfileLevel profileLevel : decoderInfo.getProfileLevels()) {
result = Math.max(avcLevelToMaxFrameSize(profileLevel.level), result); 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}) * Returns the HEVC profile and level (as defined by {@link CodecProfileLevel}) corresponding to
* corresponding to the given codec description string (as defined by RFC 6381). * the given codec description string (as defined by RFC 6381).
* *
* @param codec An HEVC 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 * @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. * @param index The index.
*/ */
MediaCodecInfo getCodecInfoAt(int index); android.media.MediaCodecInfo getCodecInfoAt(int index);
/** /**
* @return Returns whether secure decoders are explicitly listed, if present. * @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 * 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); boolean isSecurePlaybackSupported(String mimeType, CodecCapabilities capabilities);
@ -395,7 +393,7 @@ public final class MediaCodecUtil {
private final int codecKind; private final int codecKind;
private MediaCodecInfo[] mediaCodecInfos; private android.media.MediaCodecInfo[] mediaCodecInfos;
public MediaCodecListCompatV21(boolean includeSecure) { public MediaCodecListCompatV21(boolean includeSecure) {
codecKind = includeSecure ? MediaCodecList.ALL_CODECS : MediaCodecList.REGULAR_CODECS; codecKind = includeSecure ? MediaCodecList.ALL_CODECS : MediaCodecList.REGULAR_CODECS;
@ -408,7 +406,7 @@ public final class MediaCodecUtil {
} }
@Override @Override
public MediaCodecInfo getCodecInfoAt(int index) { public android.media.MediaCodecInfo getCodecInfoAt(int index) {
ensureMediaCodecInfosInitialized(); ensureMediaCodecInfosInitialized();
return mediaCodecInfos[index]; return mediaCodecInfos[index];
} }
@ -440,7 +438,7 @@ public final class MediaCodecUtil {
} }
@Override @Override
public MediaCodecInfo getCodecInfoAt(int index) { public android.media.MediaCodecInfo getCodecInfoAt(int index) {
return MediaCodecList.getCodecInfoAt(index); return MediaCodecList.getCodecInfoAt(index);
} }

View File

@ -15,31 +15,30 @@
*/ */
package com.google.android.exoplayer2.metadata; 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. * Parses objects of type <T> from binary data.
* *
* @param <T> The type of the metadata. * @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. * @param mimeType A metadata mime type.
* @return Whether the mime type is supported. * @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. * @param size The size of the input data.
* @return @return A parsed metadata object of type <T>. * @return @return A decoded metadata object of type <T>.
* @throws ParserException If a problem occurred parsing the data. * @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;
} }

View File

@ -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);
}
}

View File

@ -16,11 +16,11 @@
package com.google.android.exoplayer2.metadata; package com.google.android.exoplayer2.metadata;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DecoderInputBuffer;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import android.os.Handler; import android.os.Handler;
@ -28,7 +28,6 @@ import android.os.Handler.Callback;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import java.io.IOException;
import java.nio.ByteBuffer; 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 static final int MSG_INVOKE_RENDERER = 0;
private final MetadataParser<T> metadataParser; private final MetadataDecoder<T> metadataDecoder;
private final Output<T> output; private final Output<T> output;
private final Handler outputHandler; private final Handler outputHandler;
private final FormatHolder formatHolder; 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 * 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 * using {@link android.app.Activity#getMainLooper()}. Null may be passed if the output
* should be invoked directly on the player's internal rendering thread. * 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.output = Assertions.checkNotNull(output);
this.outputHandler = outputLooper == null ? null : new Handler(outputLooper, this); this.outputHandler = outputLooper == null ? null : new Handler(outputLooper, this);
this.metadataParser = Assertions.checkNotNull(metadataParser); this.metadataDecoder = Assertions.checkNotNull(metadataDecoder);
formatHolder = new FormatHolder(); formatHolder = new FormatHolder();
buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL); buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
} }
@ -90,7 +90,7 @@ public final class MetadataRenderer<T> extends Renderer implements Callback {
@Override @Override
public int supportsFormat(Format format) { 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; : Renderer.FORMAT_UNSUPPORTED_TYPE;
} }
@ -113,8 +113,8 @@ public final class MetadataRenderer<T> extends Renderer implements Callback {
try { try {
buffer.flip(); buffer.flip();
ByteBuffer bufferData = buffer.data; ByteBuffer bufferData = buffer.data;
pendingMetadata = metadataParser.parse(bufferData.array(), bufferData.limit()); pendingMetadata = metadataDecoder.decode(bufferData.array(), bufferData.limit());
} catch (IOException e) { } catch (MetadataDecoderException e) {
throw ExoPlaybackException.createForRenderer(e, getIndex()); throw ExoPlaybackException.createForRenderer(e, getIndex());
} }
} }

View File

@ -16,7 +16,8 @@
package com.google.android.exoplayer2.metadata.id3; package com.google.android.exoplayer2.metadata.id3;
import com.google.android.exoplayer2.ParserException; 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.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
@ -28,9 +29,9 @@ import java.util.List;
import java.util.Locale; 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_ISO_8859_1 = 0;
private static final int ID3_TEXT_ENCODING_UTF_16 = 1; 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; private static final int ID3_TEXT_ENCODING_UTF_8 = 3;
@Override @Override
public boolean canParse(String mimeType) { public boolean canDecode(String mimeType) {
return mimeType.equals(MimeTypes.APPLICATION_ID3); return mimeType.equals(MimeTypes.APPLICATION_ID3);
} }
@Override @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<>(); List<Id3Frame> id3Frames = new ArrayList<>();
ParsableByteArray id3Data = new ParsableByteArray(data, size); ParsableByteArray id3Data = new ParsableByteArray(data, size);
int id3Size = parseId3Header(id3Data); int id3Size = decodeId3Header(id3Data);
while (id3Size > 0) { while (id3Size > 0) {
int frameId0 = id3Data.readUnsignedByte(); int frameId0 = id3Data.readUnsignedByte();
@ -64,24 +65,24 @@ public final class Id3Parser implements MetadataParser<List<Id3Frame>> {
try { try {
Id3Frame frame; Id3Frame frame;
if (frameId0 == 'T' && frameId1 == 'X' && frameId2 == 'X' && frameId3 == 'X') { 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') { } 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') { } 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') { } else if (frameId0 == 'A' && frameId1 == 'P' && frameId2 == 'I' && frameId3 == 'C') {
frame = parseApicFrame(id3Data, frameSize); frame = decodeApicFrame(id3Data, frameSize);
} else if (frameId0 == 'T') { } else if (frameId0 == 'T') {
String id = String.format(Locale.US, "%c%c%c%c", frameId0, frameId1, frameId2, frameId3); 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 { } else {
String id = String.format(Locale.US, "%c%c%c%c", frameId0, frameId1, frameId2, frameId3); 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); id3Frames.add(frame);
id3Size -= frameSize + 10 /* header size */; id3Size -= frameSize + 10 /* header size */;
} catch (UnsupportedEncodingException e) { } 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. * @param id3Buffer A {@link ParsableByteArray} from which data should be read.
* @return The size of ID3 frames in bytes, excluding the header and footer. * @return The size of ID3 frames in bytes, excluding the header and footer.
* @throws ParserException If ID3 file identifier != "ID3". * @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 id1 = id3Buffer.readUnsignedByte();
int id2 = id3Buffer.readUnsignedByte(); int id2 = id3Buffer.readUnsignedByte();
int id3 = id3Buffer.readUnsignedByte(); int id3 = id3Buffer.readUnsignedByte();
if (id1 != 'I' || id2 != 'D' || id3 != '3') { 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)); "Unexpected ID3 file identifier, expected \"ID3\", actual \"%c%c%c\".", id1, id2, id3));
} }
id3Buffer.skipBytes(2); // Skip version. id3Buffer.skipBytes(2); // Skip version.
@ -158,7 +157,7 @@ public final class Id3Parser implements MetadataParser<List<Id3Frame>> {
return id3Size; return id3Size;
} }
private static TxxxFrame parseTxxxFrame(ParsableByteArray id3Data, int frameSize) private static TxxxFrame decodeTxxxFrame(ParsableByteArray id3Data, int frameSize)
throws UnsupportedEncodingException { throws UnsupportedEncodingException {
int encoding = id3Data.readUnsignedByte(); int encoding = id3Data.readUnsignedByte();
String charset = getCharsetName(encoding); String charset = getCharsetName(encoding);
@ -176,7 +175,7 @@ public final class Id3Parser implements MetadataParser<List<Id3Frame>> {
return new TxxxFrame(description, value); return new TxxxFrame(description, value);
} }
private static PrivFrame parsePrivFrame(ParsableByteArray id3Data, int frameSize) private static PrivFrame decodePrivFrame(ParsableByteArray id3Data, int frameSize)
throws UnsupportedEncodingException { throws UnsupportedEncodingException {
byte[] data = new byte[frameSize]; byte[] data = new byte[frameSize];
id3Data.readBytes(data, 0, frameSize); id3Data.readBytes(data, 0, frameSize);
@ -190,7 +189,7 @@ public final class Id3Parser implements MetadataParser<List<Id3Frame>> {
return new PrivFrame(owner, privateData); return new PrivFrame(owner, privateData);
} }
private static GeobFrame parseGeobFrame(ParsableByteArray id3Data, int frameSize) private static GeobFrame decodeGeobFrame(ParsableByteArray id3Data, int frameSize)
throws UnsupportedEncodingException { throws UnsupportedEncodingException {
int encoding = id3Data.readUnsignedByte(); int encoding = id3Data.readUnsignedByte();
String charset = getCharsetName(encoding); String charset = getCharsetName(encoding);
@ -217,7 +216,7 @@ public final class Id3Parser implements MetadataParser<List<Id3Frame>> {
return new GeobFrame(mimeType, filename, description, objectData); 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 { throws UnsupportedEncodingException {
int encoding = id3Data.readUnsignedByte(); int encoding = id3Data.readUnsignedByte();
String charset = getCharsetName(encoding); String charset = getCharsetName(encoding);
@ -241,7 +240,7 @@ public final class Id3Parser implements MetadataParser<List<Id3Frame>> {
return new ApicFrame(mimeType, description, pictureType, pictureData); 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 frameSize, String id) throws UnsupportedEncodingException {
int encoding = id3Data.readUnsignedByte(); int encoding = id3Data.readUnsignedByte();
String charset = getCharsetName(encoding); String charset = getCharsetName(encoding);
@ -255,7 +254,8 @@ public final class Id3Parser implements MetadataParser<List<Id3Frame>> {
return new TextInformationFrame(id, description); 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]; byte[] frame = new byte[frameSize];
id3Data.readBytes(frame, 0, frameSize); id3Data.readBytes(frame, 0, frameSize);

View File

@ -16,10 +16,10 @@
package com.google.android.exoplayer2.source; package com.google.android.exoplayer2.source;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DecoderInputBuffer;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.ParserException; 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.DefaultExtractorInput;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
import com.google.android.exoplayer2.extractor.DefaultTrackOutput; import com.google.android.exoplayer2.extractor.DefaultTrackOutput;

View File

@ -16,8 +16,8 @@
package com.google.android.exoplayer2.source; package com.google.android.exoplayer2.source;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DecoderInputBuffer;
import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import java.io.IOException; import java.io.IOException;

View File

@ -16,9 +16,9 @@
package com.google.android.exoplayer2.source; package com.google.android.exoplayer2.source;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DecoderInputBuffer;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder; 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.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;

View File

@ -16,9 +16,9 @@
package com.google.android.exoplayer2.source.chunk; package com.google.android.exoplayer2.source.chunk;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DecoderInputBuffer;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder; 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;
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.source.SampleStream;

View File

@ -29,7 +29,7 @@ import com.google.android.exoplayer2.util.Util;
import java.io.IOException; 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 { public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMetadataOutput {
@ -109,7 +109,7 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe
trackOutput.formatWithOffset(sampleFormat, sampleOffsetUs); trackOutput.formatWithOffset(sampleFormat, sampleOffsetUs);
extractorWrapper.init(this, trackOutput); extractorWrapper.init(this, trackOutput);
} }
// Load and parse the sample data. // Load and decode the sample data.
try { try {
int result = Extractor.RESULT_CONTINUE; int result = Extractor.RESULT_CONTINUE;
while (result == Extractor.RESULT_CONTINUE && !loadCanceled) { while (result == Extractor.RESULT_CONTINUE && !loadCanceled) {

View File

@ -31,7 +31,7 @@ import com.google.android.exoplayer2.util.Util;
import java.io.IOException; 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, public final class InitializationChunk extends Chunk implements SingleTrackMetadataOutput,
TrackOutput { TrackOutput {
@ -140,7 +140,7 @@ public final class InitializationChunk extends Chunk implements SingleTrackMetad
// Set the target to ourselves. // Set the target to ourselves.
extractorWrapper.init(this, this); extractorWrapper.init(this, this);
} }
// Load and parse the initialization data. // Load and decode the initialization data.
try { try {
int result = Extractor.RESULT_CONTINUE; int result = Extractor.RESULT_CONTINUE;
while (result == Extractor.RESULT_CONTINUE && !loadCanceled) { while (result == Extractor.RESULT_CONTINUE && !loadCanceled) {

View File

@ -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.upstream.ParsingLoadable;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes; 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.UriUtil;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.util.XmlPullParserUtil;
import android.net.Uri; import android.net.Uri;
import android.text.TextUtils; import android.text.TextUtils;
@ -120,16 +120,16 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
boolean seenFirstBaseUrl = false; boolean seenFirstBaseUrl = false;
do { do {
xpp.next(); xpp.next();
if (ParserUtil.isStartTag(xpp, "BaseURL")) { if (XmlPullParserUtil.isStartTag(xpp, "BaseURL")) {
if (!seenFirstBaseUrl) { if (!seenFirstBaseUrl) {
baseUrl = parseBaseUrl(xpp, baseUrl); baseUrl = parseBaseUrl(xpp, baseUrl);
seenFirstBaseUrl = true; seenFirstBaseUrl = true;
} }
} else if (ParserUtil.isStartTag(xpp, "UTCTiming")) { } else if (XmlPullParserUtil.isStartTag(xpp, "UTCTiming")) {
utcTiming = parseUtcTiming(xpp); utcTiming = parseUtcTiming(xpp);
} else if (ParserUtil.isStartTag(xpp, "Location")) { } else if (XmlPullParserUtil.isStartTag(xpp, "Location")) {
location = Uri.parse(xpp.nextText()); 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); Pair<Period, Long> periodWithDurationMs = parsePeriod(xpp, baseUrl, nextPeriodStartMs);
Period period = periodWithDurationMs.first; Period period = periodWithDurationMs.first;
if (period.startMs == -1) { if (period.startMs == -1) {
@ -146,7 +146,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
periods.add(period); periods.add(period);
} }
} }
} while (!ParserUtil.isEndTag(xpp, "MPD")); } while (!XmlPullParserUtil.isEndTag(xpp, "MPD"));
if (durationMs == -1) { if (durationMs == -1) {
if (nextPeriodStartMs != -1) { if (nextPeriodStartMs != -1) {
@ -193,21 +193,21 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
boolean seenFirstBaseUrl = false; boolean seenFirstBaseUrl = false;
do { do {
xpp.next(); xpp.next();
if (ParserUtil.isStartTag(xpp, "BaseURL")) { if (XmlPullParserUtil.isStartTag(xpp, "BaseURL")) {
if (!seenFirstBaseUrl) { if (!seenFirstBaseUrl) {
baseUrl = parseBaseUrl(xpp, baseUrl); baseUrl = parseBaseUrl(xpp, baseUrl);
seenFirstBaseUrl = true; seenFirstBaseUrl = true;
} }
} else if (ParserUtil.isStartTag(xpp, "AdaptationSet")) { } else if (XmlPullParserUtil.isStartTag(xpp, "AdaptationSet")) {
adaptationSets.add(parseAdaptationSet(xpp, baseUrl, segmentBase)); adaptationSets.add(parseAdaptationSet(xpp, baseUrl, segmentBase));
} else if (ParserUtil.isStartTag(xpp, "SegmentBase")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) {
segmentBase = parseSegmentBase(xpp, baseUrl, null); segmentBase = parseSegmentBase(xpp, baseUrl, null);
} else if (ParserUtil.isStartTag(xpp, "SegmentList")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentList")) {
segmentBase = parseSegmentList(xpp, baseUrl, null); segmentBase = parseSegmentList(xpp, baseUrl, null);
} else if (ParserUtil.isStartTag(xpp, "SegmentTemplate")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) {
segmentBase = parseSegmentTemplate(xpp, baseUrl, null); segmentBase = parseSegmentTemplate(xpp, baseUrl, null);
} }
} while (!ParserUtil.isEndTag(xpp, "Period")); } while (!XmlPullParserUtil.isEndTag(xpp, "Period"));
return Pair.create(buildPeriod(id, startMs, adaptationSets), durationMs); return Pair.create(buildPeriod(id, startMs, adaptationSets), durationMs);
} }
@ -237,37 +237,37 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
boolean seenFirstBaseUrl = false; boolean seenFirstBaseUrl = false;
do { do {
xpp.next(); xpp.next();
if (ParserUtil.isStartTag(xpp, "BaseURL")) { if (XmlPullParserUtil.isStartTag(xpp, "BaseURL")) {
if (!seenFirstBaseUrl) { if (!seenFirstBaseUrl) {
baseUrl = parseBaseUrl(xpp, baseUrl); baseUrl = parseBaseUrl(xpp, baseUrl);
seenFirstBaseUrl = true; seenFirstBaseUrl = true;
} }
} else if (ParserUtil.isStartTag(xpp, "ContentProtection")) { } else if (XmlPullParserUtil.isStartTag(xpp, "ContentProtection")) {
SchemeData contentProtection = parseContentProtection(xpp); SchemeData contentProtection = parseContentProtection(xpp);
if (contentProtection != null) { if (contentProtection != null) {
drmSchemeDatas.add(contentProtection); drmSchemeDatas.add(contentProtection);
} }
} else if (ParserUtil.isStartTag(xpp, "ContentComponent")) { } else if (XmlPullParserUtil.isStartTag(xpp, "ContentComponent")) {
language = checkLanguageConsistency(language, xpp.getAttributeValue(null, "lang")); language = checkLanguageConsistency(language, xpp.getAttributeValue(null, "lang"));
contentType = checkContentTypeConsistency(contentType, parseContentType(xpp)); contentType = checkContentTypeConsistency(contentType, parseContentType(xpp));
} else if (ParserUtil.isStartTag(xpp, "Representation")) { } else if (XmlPullParserUtil.isStartTag(xpp, "Representation")) {
RepresentationInfo representationInfo = parseRepresentation(xpp, baseUrl, mimeType, codecs, RepresentationInfo representationInfo = parseRepresentation(xpp, baseUrl, mimeType, codecs,
width, height, frameRate, audioChannels, audioSamplingRate, language, segmentBase); width, height, frameRate, audioChannels, audioSamplingRate, language, segmentBase);
contentType = checkContentTypeConsistency(contentType, contentType = checkContentTypeConsistency(contentType,
getContentType(representationInfo.format)); getContentType(representationInfo.format));
representationInfos.add(representationInfo); representationInfos.add(representationInfo);
} else if (ParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) { } else if (XmlPullParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) {
audioChannels = parseAudioChannelConfiguration(xpp); audioChannels = parseAudioChannelConfiguration(xpp);
} else if (ParserUtil.isStartTag(xpp, "SegmentBase")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) {
segmentBase = parseSegmentBase(xpp, baseUrl, (SingleSegmentBase) 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); segmentBase = parseSegmentList(xpp, baseUrl, (SegmentList) segmentBase);
} else if (ParserUtil.isStartTag(xpp, "SegmentTemplate")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) {
segmentBase = parseSegmentTemplate(xpp, baseUrl, (SegmentTemplate) segmentBase); segmentBase = parseSegmentTemplate(xpp, baseUrl, (SegmentTemplate) segmentBase);
} else if (ParserUtil.isStartTag(xpp)) { } else if (XmlPullParserUtil.isStartTag(xpp)) {
parseAdaptationSetChild(xpp); parseAdaptationSetChild(xpp);
} }
} while (!ParserUtil.isEndTag(xpp, "AdaptationSet")); } while (!XmlPullParserUtil.isEndTag(xpp, "AdaptationSet"));
List<Representation> representations = new ArrayList<>(representationInfos.size()); List<Representation> representations = new ArrayList<>(representationInfos.size());
for (int i = 0; i < representationInfos.size(); i++) { for (int i = 0; i < representationInfos.size(); i++) {
@ -321,7 +321,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
do { do {
xpp.next(); xpp.next();
// The cenc:pssh element is defined in 23001-7:2015. // 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; seenPsshElement = true;
byte[] data = Base64.decode(xpp.getText(), Base64.DEFAULT); byte[] data = Base64.decode(xpp.getText(), Base64.DEFAULT);
UUID uuid = PsshAtomUtil.parseUuid(data); UUID uuid = PsshAtomUtil.parseUuid(data);
@ -329,7 +329,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
schemeData = new SchemeData(uuid, MimeTypes.VIDEO_MP4, data); schemeData = new SchemeData(uuid, MimeTypes.VIDEO_MP4, data);
} }
} }
} while (!ParserUtil.isEndTag(xpp, "ContentProtection")); } while (!XmlPullParserUtil.isEndTag(xpp, "ContentProtection"));
if (seenPsshElement && schemeData == null) { if (seenPsshElement && schemeData == null) {
Log.w(TAG, "Skipped unsupported ContentProtection element"); Log.w(TAG, "Skipped unsupported ContentProtection element");
return null; return null;
@ -371,26 +371,26 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
boolean seenFirstBaseUrl = false; boolean seenFirstBaseUrl = false;
do { do {
xpp.next(); xpp.next();
if (ParserUtil.isStartTag(xpp, "BaseURL")) { if (XmlPullParserUtil.isStartTag(xpp, "BaseURL")) {
if (!seenFirstBaseUrl) { if (!seenFirstBaseUrl) {
baseUrl = parseBaseUrl(xpp, baseUrl); baseUrl = parseBaseUrl(xpp, baseUrl);
seenFirstBaseUrl = true; seenFirstBaseUrl = true;
} }
} else if (ParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) { } else if (XmlPullParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) {
audioChannels = parseAudioChannelConfiguration(xpp); audioChannels = parseAudioChannelConfiguration(xpp);
} else if (ParserUtil.isStartTag(xpp, "SegmentBase")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) {
segmentBase = parseSegmentBase(xpp, baseUrl, (SingleSegmentBase) 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); segmentBase = parseSegmentList(xpp, baseUrl, (SegmentList) segmentBase);
} else if (ParserUtil.isStartTag(xpp, "SegmentTemplate")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) {
segmentBase = parseSegmentTemplate(xpp, baseUrl, (SegmentTemplate) segmentBase); segmentBase = parseSegmentTemplate(xpp, baseUrl, (SegmentTemplate) segmentBase);
} else if (ParserUtil.isStartTag(xpp, "ContentProtection")) { } else if (XmlPullParserUtil.isStartTag(xpp, "ContentProtection")) {
SchemeData contentProtection = parseContentProtection(xpp); SchemeData contentProtection = parseContentProtection(xpp);
if (contentProtection != null) { if (contentProtection != null) {
drmSchemeDatas.add(contentProtection); drmSchemeDatas.add(contentProtection);
} }
} }
} while (!ParserUtil.isEndTag(xpp, "Representation")); } while (!XmlPullParserUtil.isEndTag(xpp, "Representation"));
Format format = buildFormat(id, mimeType, width, height, frameRate, audioChannels, Format format = buildFormat(id, mimeType, width, height, frameRate, audioChannels,
audioSamplingRate, bandwidth, adaptationSetLanguage, codecs); audioSamplingRate, bandwidth, adaptationSetLanguage, codecs);
@ -453,10 +453,10 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
RangedUri initialization = parent != null ? parent.initialization : null; RangedUri initialization = parent != null ? parent.initialization : null;
do { do {
xpp.next(); xpp.next();
if (ParserUtil.isStartTag(xpp, "Initialization")) { if (XmlPullParserUtil.isStartTag(xpp, "Initialization")) {
initialization = parseInitialization(xpp, baseUrl); initialization = parseInitialization(xpp, baseUrl);
} }
} while (!ParserUtil.isEndTag(xpp, "SegmentBase")); } while (!XmlPullParserUtil.isEndTag(xpp, "SegmentBase"));
return buildSingleSegmentBase(initialization, timescale, presentationTimeOffset, baseUrl, return buildSingleSegmentBase(initialization, timescale, presentationTimeOffset, baseUrl,
indexStart, indexLength); indexStart, indexLength);
@ -483,17 +483,17 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
do { do {
xpp.next(); xpp.next();
if (ParserUtil.isStartTag(xpp, "Initialization")) { if (XmlPullParserUtil.isStartTag(xpp, "Initialization")) {
initialization = parseInitialization(xpp, baseUrl); initialization = parseInitialization(xpp, baseUrl);
} else if (ParserUtil.isStartTag(xpp, "SegmentTimeline")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTimeline")) {
timeline = parseSegmentTimeline(xpp); timeline = parseSegmentTimeline(xpp);
} else if (ParserUtil.isStartTag(xpp, "SegmentURL")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentURL")) {
if (segments == null) { if (segments == null) {
segments = new ArrayList<>(); segments = new ArrayList<>();
} }
segments.add(parseSegmentUrl(xpp, baseUrl)); segments.add(parseSegmentUrl(xpp, baseUrl));
} }
} while (!ParserUtil.isEndTag(xpp, "SegmentList")); } while (!XmlPullParserUtil.isEndTag(xpp, "SegmentList"));
if (parent != null) { if (parent != null) {
initialization = initialization != null ? initialization : parent.initialization; initialization = initialization != null ? initialization : parent.initialization;
@ -530,12 +530,12 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
do { do {
xpp.next(); xpp.next();
if (ParserUtil.isStartTag(xpp, "Initialization")) { if (XmlPullParserUtil.isStartTag(xpp, "Initialization")) {
initialization = parseInitialization(xpp, baseUrl); initialization = parseInitialization(xpp, baseUrl);
} else if (ParserUtil.isStartTag(xpp, "SegmentTimeline")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTimeline")) {
timeline = parseSegmentTimeline(xpp); timeline = parseSegmentTimeline(xpp);
} }
} while (!ParserUtil.isEndTag(xpp, "SegmentTemplate")); } while (!XmlPullParserUtil.isEndTag(xpp, "SegmentTemplate"));
if (parent != null) { if (parent != null) {
initialization = initialization != null ? initialization : parent.initialization; initialization = initialization != null ? initialization : parent.initialization;
@ -560,7 +560,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
long elapsedTime = 0; long elapsedTime = 0;
do { do {
xpp.next(); xpp.next();
if (ParserUtil.isStartTag(xpp, "S")) { if (XmlPullParserUtil.isStartTag(xpp, "S")) {
elapsedTime = parseLong(xpp, "t", elapsedTime); elapsedTime = parseLong(xpp, "t", elapsedTime);
long duration = parseLong(xpp, "d"); long duration = parseLong(xpp, "d");
int count = 1 + parseInt(xpp, "r", 0); int count = 1 + parseInt(xpp, "r", 0);
@ -569,7 +569,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
elapsedTime += duration; elapsedTime += duration;
} }
} }
} while (!ParserUtil.isEndTag(xpp, "SegmentTimeline")); } while (!XmlPullParserUtil.isEndTag(xpp, "SegmentTimeline"));
return segmentTimeline; return segmentTimeline;
} }
@ -628,7 +628,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
} }
do { do {
xpp.next(); xpp.next();
} while (!ParserUtil.isEndTag(xpp, "AudioChannelConfiguration")); } while (!XmlPullParserUtil.isEndTag(xpp, "AudioChannelConfiguration"));
return audioChannels; return audioChannels;
} }

View File

@ -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 endTimeUs The end time of the media contained by the chunk, in microseconds.
* @param chunkIndex The index of the chunk. * @param chunkIndex The index of the chunk.
* @param discontinuitySequenceNumber The discontinuity sequence number 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 * @param extractorNeedsInit Whether the extractor needs initializing with the target
* {@link HlsSampleStreamWrapper}. * {@link HlsSampleStreamWrapper}.
* @param shouldSpliceIn Whether the samples parsed from this chunk should be spliced into any * @param shouldSpliceIn Whether the samples parsed from this chunk should be spliced into any

View File

@ -16,9 +16,9 @@
package com.google.android.exoplayer2.source.hls; package com.google.android.exoplayer2.source.hls;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DecoderInputBuffer;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder; 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;
import com.google.android.exoplayer2.extractor.DefaultTrackOutput.UpstreamFormatChangedListener; import com.google.android.exoplayer2.extractor.DefaultTrackOutput.UpstreamFormatChangedListener;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;

View File

@ -25,6 +25,7 @@ import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.PtsTimestampAdjuster; 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.text.webvtt.WebvttParserUtil;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
@ -119,7 +120,11 @@ import java.util.regex.Pattern;
ParsableByteArray webvttData = new ParsableByteArray(sampleData); ParsableByteArray webvttData = new ParsableByteArray(sampleData);
// Validate the first line of the header. // Validate the first line of the header.
try {
WebvttParserUtil.validateWebvttHeaderLine(webvttData); WebvttParserUtil.validateWebvttHeaderLine(webvttData);
} catch (TextDecoderException e) {
throw new ParserException(e);
}
// Defaults to use if the header doesn't contain an X-TIMESTAMP-MAP header. // Defaults to use if the header doesn't contain an X-TIMESTAMP-MAP header.
long vttTimestampUs = 0; long vttTimestampUs = 0;

View File

@ -15,21 +15,20 @@
*/ */
package com.google.android.exoplayer2.text; package com.google.android.exoplayer2.text;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.decoder.SimpleDecoder;
import com.google.android.exoplayer2.extensions.SimpleDecoder;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
/** /**
* Base class for subtitle parsers that use their own decode thread. * Base class for subtitle parsers that use their own decode thread.
*/ */
public abstract class SimpleSubtitleParser extends public abstract class SimpleSubtitleDecoder extends
SimpleDecoder<SubtitleInputBuffer, SubtitleOutputBuffer, ParserException> implements SimpleDecoder<SubtitleInputBuffer, SubtitleOutputBuffer, TextDecoderException> implements
SubtitleParser { SubtitleDecoder {
private final String name; private final String name;
protected SimpleSubtitleParser(String name) { protected SimpleSubtitleDecoder(String name) {
super(new SubtitleInputBuffer[2], new SubtitleOutputBuffer[2]); super(new SubtitleInputBuffer[2], new SubtitleOutputBuffer[2]);
this.name = name; this.name = name;
setInitialInputBufferSize(1024); setInitialInputBufferSize(1024);
@ -61,14 +60,14 @@ public abstract class SimpleSubtitleParser extends
} }
@Override @Override
protected final ParserException decode(SubtitleInputBuffer inputBuffer, protected final TextDecoderException decode(SubtitleInputBuffer inputBuffer,
SubtitleOutputBuffer outputBuffer, boolean reset) { SubtitleOutputBuffer outputBuffer, boolean reset) {
try { try {
ByteBuffer inputData = inputBuffer.data; ByteBuffer inputData = inputBuffer.data;
Subtitle subtitle = decode(inputData.array(), inputData.limit()); Subtitle subtitle = decode(inputData.array(), inputData.limit());
outputBuffer.setOutput(inputBuffer.timeUs, subtitle, inputBuffer.subsampleOffsetUs); outputBuffer.setOutput(inputBuffer.timeUs, subtitle, inputBuffer.subsampleOffsetUs);
return null; return null;
} catch (ParserException e) { } catch (TextDecoderException e) {
return e; return e;
} }
} }
@ -79,8 +78,8 @@ public abstract class SimpleSubtitleParser extends
* @param data The data to be decoded. * @param data The data to be decoded.
* @param size The size of the data. * @param size The size of the data.
* @return A {@link Subtitle} to rendered. * @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;
} }

View File

@ -16,13 +16,13 @@
package com.google.android.exoplayer2.text; 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 { public final class SimpleSubtitleOutputBuffer extends SubtitleOutputBuffer {
private SimpleSubtitleParser owner; private SimpleSubtitleDecoder owner;
public SimpleSubtitleOutputBuffer(SimpleSubtitleParser owner) { public SimpleSubtitleOutputBuffer(SimpleSubtitleDecoder owner) {
super(); super();
this.owner = owner; this.owner = owner;
} }

View File

@ -15,14 +15,13 @@
*/ */
package com.google.android.exoplayer2.text; package com.google.android.exoplayer2.text;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.decoder.Decoder;
import com.google.android.exoplayer2.extensions.Decoder;
/** /**
* Parses {@link Subtitle}s from {@link SubtitleInputBuffer}s. * Parses {@link Subtitle}s from {@link SubtitleInputBuffer}s.
*/ */
public interface SubtitleParser extends public interface SubtitleDecoder extends
Decoder<SubtitleInputBuffer, SubtitleOutputBuffer, ParserException> { Decoder<SubtitleInputBuffer, SubtitleOutputBuffer, TextDecoderException> {
/** /**
* Informs the parser of the current playback position. * Informs the parser of the current playback position.

View File

@ -16,13 +16,19 @@
package com.google.android.exoplayer2.text; package com.google.android.exoplayer2.text;
import com.google.android.exoplayer2.Format; 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; 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 * 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}. * @return A new {@link Decoder}.
* @throws IllegalArgumentException If the {@link Format} is not supported. * @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> * <p>
* The formats supported by this factory are: * The formats supported by this factory are:
* <ul> * <ul>
* <li>WebVTT ({@link com.google.android.exoplayer2.text.webvtt.WebvttParser})</li> * <li>WebVTT ({@link WebvttDecoder})</li>
* <li>WebVTT (MP4) ({@link com.google.android.exoplayer2.text.webvtt.Mp4WebvttParser})</li> * <li>WebVTT (MP4) ({@link Mp4WebvttDecoder})</li>
* <li>TTML ({@link com.google.android.exoplayer2.text.ttml.TtmlParser})</li> * <li>TTML ({@link TtmlDecoder})</li>
* <li>SubRip ({@link com.google.android.exoplayer2.text.subrip.SubripParser})</li> * <li>SubRip ({@link SubripDecoder})</li>
* <li>TX3G ({@link com.google.android.exoplayer2.text.tx3g.Tx3gParser})</li> * <li>TX3G ({@link Tx3gDecoder})</li>
* <li>Eia608 ({@link com.google.android.exoplayer2.text.eia608.Eia608Parser})</li> * <li>Eia608 ({@link Eia608Decoder})</li>
* </ul> * </ul>
*/ */
SubtitleParserFactory DEFAULT = new SubtitleParserFactory() { SubtitleDecoderFactory DEFAULT = new SubtitleDecoderFactory() {
@Override @Override
public boolean supportsFormat(Format format) { public boolean supportsFormat(Format format) {
return getParserClass(format.sampleMimeType) != null; return getDecoderClass(format.sampleMimeType) != null;
} }
@Override @Override
public SubtitleParser createParser(Format format) { public SubtitleDecoder createDecoder(Format format) {
try { try {
Class<?> clazz = getParserClass(format.sampleMimeType); Class<?> clazz = getDecoderClass(format.sampleMimeType);
if (clazz == null) { 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) { } 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 { try {
switch (mimeType) { switch (mimeType) {
case MimeTypes.TEXT_VTT: 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: 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: 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: 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: 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: 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: default:
return null; return null;
} }

View File

@ -15,7 +15,7 @@
*/ */
package com.google.android.exoplayer2.text; 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. * An input buffer for a subtitle parser.

View File

@ -16,7 +16,7 @@
package com.google.android.exoplayer2.text; package com.google.android.exoplayer2.text;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extensions.OutputBuffer; import com.google.android.exoplayer2.decoder.OutputBuffer;
import java.util.List; import java.util.List;

View File

@ -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);
}
}

View File

@ -19,9 +19,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.Renderer; 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.Assertions;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
@ -31,15 +29,14 @@ import android.os.Handler.Callback;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
* A {@link Renderer} for subtitles. * A {@link Renderer} for subtitles.
* <p> * <p>
* Text is parsed from sample data using {@link Decoder} instances obtained from a * Text is parsed from sample data using {@link SubtitleDecoder} instances obtained from a
* {@link SubtitleParserFactory}. The actual rendering of each line of text is delegated to a * {@link SubtitleDecoderFactory}. The actual rendering of each line of text is delegated to a
* {@link Output}. * {@link Output}.
*/ */
@TargetApi(16) @TargetApi(16)
@ -63,12 +60,12 @@ public final class TextRenderer extends Renderer implements Callback {
private final Handler outputHandler; private final Handler outputHandler;
private final Output output; private final Output output;
private final SubtitleParserFactory parserFactory; private final SubtitleDecoderFactory decoderFactory;
private final FormatHolder formatHolder; private final FormatHolder formatHolder;
private boolean inputStreamEnded; private boolean inputStreamEnded;
private boolean outputStreamEnded; private boolean outputStreamEnded;
private SubtitleParser parser; private SubtitleDecoder decoder;
private SubtitleInputBuffer nextInputBuffer; private SubtitleInputBuffer nextInputBuffer;
private SubtitleOutputBuffer subtitle; private SubtitleOutputBuffer subtitle;
private SubtitleOutputBuffer nextSubtitle; 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. * should be invoked directly on the player's internal rendering thread.
*/ */
public TextRenderer(Output output, Looper outputLooper) { 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 * 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 * using {@link android.app.Activity#getMainLooper()}. Null may be passed if the output
* should be invoked directly on the player's internal rendering thread. * 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.output = Assertions.checkNotNull(output);
this.outputHandler = outputLooper == null ? null : new Handler(outputLooper, this); this.outputHandler = outputLooper == null ? null : new Handler(outputLooper, this);
this.parserFactory = parserFactory; this.decoderFactory = decoderFactory;
formatHolder = new FormatHolder(); formatHolder = new FormatHolder();
} }
@ -109,17 +106,17 @@ public final class TextRenderer extends Renderer implements Callback {
@Override @Override
public int supportsFormat(Format format) { 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 : (MimeTypes.isText(format.sampleMimeType) ? FORMAT_UNSUPPORTED_SUBTYPE
: FORMAT_UNSUPPORTED_TYPE); : FORMAT_UNSUPPORTED_TYPE);
} }
@Override @Override
protected void onStreamChanged(Format[] formats) throws ExoPlaybackException { protected void onStreamChanged(Format[] formats) throws ExoPlaybackException {
if (parser != null) { if (decoder != null) {
parser.release(); decoder.release();
} }
parser = parserFactory.createParser(formats[0]); decoder = decoderFactory.createDecoder(formats[0]);
} }
@Override @Override
@ -136,7 +133,7 @@ public final class TextRenderer extends Renderer implements Callback {
} }
nextInputBuffer = null; nextInputBuffer = null;
clearOutput(); clearOutput();
parser.flush(); decoder.flush();
} }
@Override @Override
@ -146,10 +143,10 @@ public final class TextRenderer extends Renderer implements Callback {
} }
if (nextSubtitle == null) { if (nextSubtitle == null) {
parser.setPositionUs(positionUs); decoder.setPositionUs(positionUs);
try { try {
nextSubtitle = parser.dequeueOutputBuffer(); nextSubtitle = decoder.dequeueOutputBuffer();
} catch (IOException e) { } catch (TextDecoderException e) {
throw ExoPlaybackException.createForRenderer(e, getIndex()); throw ExoPlaybackException.createForRenderer(e, getIndex());
} }
} }
@ -195,7 +192,7 @@ public final class TextRenderer extends Renderer implements Callback {
try { try {
while (!inputStreamEnded) { while (!inputStreamEnded) {
if (nextInputBuffer == null) { if (nextInputBuffer == null) {
nextInputBuffer = parser.dequeueInputBuffer(); nextInputBuffer = decoder.dequeueInputBuffer();
if (nextInputBuffer == null) { if (nextInputBuffer == null) {
return; return;
} }
@ -211,13 +208,13 @@ public final class TextRenderer extends Renderer implements Callback {
nextInputBuffer.subsampleOffsetUs = formatHolder.format.subsampleOffsetUs; nextInputBuffer.subsampleOffsetUs = formatHolder.format.subsampleOffsetUs;
nextInputBuffer.flip(); nextInputBuffer.flip();
} }
parser.queueInputBuffer(nextInputBuffer); decoder.queueInputBuffer(nextInputBuffer);
nextInputBuffer = null; nextInputBuffer = null;
} else if (result == C.RESULT_NOTHING_READ) { } else if (result == C.RESULT_NOTHING_READ) {
break; break;
} }
} }
} catch (ParserException e) { } catch (TextDecoderException e) {
throw ExoPlaybackException.createForRenderer(e, getIndex()); throw ExoPlaybackException.createForRenderer(e, getIndex());
} }
} }
@ -232,8 +229,8 @@ public final class TextRenderer extends Renderer implements Callback {
nextSubtitle.release(); nextSubtitle.release();
nextSubtitle = null; nextSubtitle = null;
} }
parser.release(); decoder.release();
parser = null; decoder = null;
nextInputBuffer = null; nextInputBuffer = null;
clearOutput(); clearOutput();
super.onDisabled(); super.onDisabled();

View File

@ -16,10 +16,10 @@
package com.google.android.exoplayer2.text.eia608; package com.google.android.exoplayer2.text.eia608;
import com.google.android.exoplayer2.C; 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.SubtitleInputBuffer;
import com.google.android.exoplayer2.text.SubtitleOutputBuffer; 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.Assertions;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
@ -29,10 +29,9 @@ import java.util.LinkedList;
import java.util.TreeSet; import java.util.TreeSet;
/** /**
* Facilitates the extraction and parsing of EIA-608 (a.k.a. "line 21 captions" and "CEA-608") * A {@link SubtitleDecoder} for EIA-608 (also known as "line 21 captions" and "CEA-608").
* Closed Captions from the SEI data block from H.264.
*/ */
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_INPUT_BUFFERS = 10;
private static final int NUM_OUTPUT_BUFFERS = 2; private static final int NUM_OUTPUT_BUFFERS = 2;
@ -184,7 +183,7 @@ public final class Eia608Parser implements SubtitleParser {
private byte repeatableControlCc1; private byte repeatableControlCc1;
private byte repeatableControlCc2; private byte repeatableControlCc2;
public Eia608Parser() { public Eia608Decoder() {
availableInputBuffers = new LinkedList<>(); availableInputBuffers = new LinkedList<>();
for (int i = 0; i < NUM_INPUT_BUFFERS; i++) { for (int i = 0; i < NUM_INPUT_BUFFERS; i++) {
availableInputBuffers.add(new SubtitleInputBuffer()); availableInputBuffers.add(new SubtitleInputBuffer());
@ -205,7 +204,7 @@ public final class Eia608Parser implements SubtitleParser {
@Override @Override
public String getName() { public String getName() {
return "Eia608Parser"; return "Eia608Decoder";
} }
@Override @Override
@ -214,7 +213,7 @@ public final class Eia608Parser implements SubtitleParser {
} }
@Override @Override
public SubtitleInputBuffer dequeueInputBuffer() throws ParserException { public SubtitleInputBuffer dequeueInputBuffer() throws TextDecoderException {
Assertions.checkState(dequeuedInputBuffer == null); Assertions.checkState(dequeuedInputBuffer == null);
if (availableInputBuffers.isEmpty()) { if (availableInputBuffers.isEmpty()) {
return null; return null;
@ -224,7 +223,7 @@ public final class Eia608Parser implements SubtitleParser {
} }
@Override @Override
public void queueInputBuffer(SubtitleInputBuffer inputBuffer) throws ParserException { public void queueInputBuffer(SubtitleInputBuffer inputBuffer) throws TextDecoderException {
Assertions.checkArgument(inputBuffer != null); Assertions.checkArgument(inputBuffer != null);
Assertions.checkArgument(inputBuffer == dequeuedInputBuffer); Assertions.checkArgument(inputBuffer == dequeuedInputBuffer);
queuedInputBuffers.add(inputBuffer); queuedInputBuffers.add(inputBuffer);
@ -232,7 +231,7 @@ public final class Eia608Parser implements SubtitleParser {
} }
@Override @Override
public SubtitleOutputBuffer dequeueOutputBuffer() throws ParserException { public SubtitleOutputBuffer dequeueOutputBuffer() throws TextDecoderException {
if (availableOutputBuffers.isEmpty()) { if (availableOutputBuffers.isEmpty()) {
return null; return null;
} }

View File

@ -19,13 +19,13 @@ import com.google.android.exoplayer2.text.Subtitle;
import com.google.android.exoplayer2.text.SubtitleOutputBuffer; 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 { public final class Eia608SubtitleOutputBuffer extends SubtitleOutputBuffer {
private Eia608Parser owner; private Eia608Decoder owner;
public Eia608SubtitleOutputBuffer(Eia608Parser owner) { public Eia608SubtitleOutputBuffer(Eia608Decoder owner) {
super(); super();
this.owner = owner; this.owner = owner;
} }

View File

@ -16,7 +16,7 @@
package com.google.android.exoplayer2.text.subrip; package com.google.android.exoplayer2.text.subrip;
import com.google.android.exoplayer2.text.Cue; 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.LongArray;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
@ -30,11 +30,11 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; 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_TIMING_LINE = Pattern.compile("(\\S*)\\s*-->\\s*(\\S*)");
private static final Pattern SUBRIP_TIMESTAMP = private static final Pattern SUBRIP_TIMESTAMP =
@ -42,8 +42,8 @@ public final class SubripParser extends SimpleSubtitleParser {
private final StringBuilder textBuilder; private final StringBuilder textBuilder;
public SubripParser() { public SubripDecoder() {
super("SubripParser"); super("SubripDecoder");
textBuilder = new StringBuilder(); textBuilder = new StringBuilder();
} }

View File

@ -16,12 +16,12 @@
package com.google.android.exoplayer2.text.ttml; package com.google.android.exoplayer2.text.ttml;
import com.google.android.exoplayer2.C; 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.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.ColorParser;
import com.google.android.exoplayer2.util.ParserUtil;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.util.XmlPullParserUtil;
import android.text.Layout; import android.text.Layout;
import android.util.Log; import android.util.Log;
@ -40,7 +40,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; 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> * <p>
* Supported features in this parser are: * Supported features in this parser are:
* <ul> * <ul>
@ -60,9 +60,9 @@ import java.util.regex.Pattern;
* </p> * </p>
* @see <a href="http://www.w3.org/TR/ttaf1-dfxp/">TTML specification</a> * @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"; 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; private final XmlPullParserFactory xmlParserFactory;
public TtmlParser() { public TtmlDecoder() {
super("TtmlParser"); super("TtmlDecoder");
try { try {
xmlParserFactory = XmlPullParserFactory.newInstance(); xmlParserFactory = XmlPullParserFactory.newInstance();
xmlParserFactory.setNamespaceAware(true); xmlParserFactory.setNamespaceAware(true);
@ -99,7 +99,7 @@ public final class TtmlParser extends SimpleSubtitleParser {
} }
@Override @Override
protected TtmlSubtitle decode(byte[] bytes, int length) throws ParserException { protected TtmlSubtitle decode(byte[] bytes, int length) throws TextDecoderException {
try { try {
XmlPullParser xmlParser = xmlParserFactory.newPullParser(); XmlPullParser xmlParser = xmlParserFactory.newPullParser();
Map<String, TtmlStyle> globalStyles = new HashMap<>(); Map<String, TtmlStyle> globalStyles = new HashMap<>();
@ -132,7 +132,7 @@ public final class TtmlParser extends SimpleSubtitleParser {
if (parent != null) { if (parent != null) {
parent.addChild(node); parent.addChild(node);
} }
} catch (ParserException e) { } catch (TextDecoderException e) {
Log.w(TAG, "Suppressing parser error", e); Log.w(TAG, "Suppressing parser error", e);
// Treat the node (and by extension, all of its children) as unsupported. // Treat the node (and by extension, all of its children) as unsupported.
unsupportedNodeDepth++; unsupportedNodeDepth++;
@ -158,13 +158,14 @@ public final class TtmlParser extends SimpleSubtitleParser {
} }
return ttmlSubtitle; return ttmlSubtitle;
} catch (XmlPullParserException xppe) { } catch (XmlPullParserException xppe) {
throw new ParserException("Unable to parse source", xppe); throw new TextDecoderException("Unable to decode source", xppe);
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException("Unexpected error when reading input.", 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; int frameRate = DEFAULT_FRAME_RATE;
String frameRateString = xmlParser.getAttributeValue(TTP, "frameRate"); String frameRateString = xmlParser.getAttributeValue(TTP, "frameRate");
if (frameRateString != null) { if (frameRateString != null) {
@ -176,7 +177,7 @@ public final class TtmlParser extends SimpleSubtitleParser {
if (frameRateMultiplierString != null) { if (frameRateMultiplierString != null) {
String[] parts = frameRateMultiplierString.split(" "); String[] parts = frameRateMultiplierString.split(" ");
if (parts.length != 2) { 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 numerator = Integer.parseInt(parts[0]);
float denominator = Integer.parseInt(parts[1]); float denominator = Integer.parseInt(parts[1]);
@ -202,8 +203,8 @@ public final class TtmlParser extends SimpleSubtitleParser {
throws IOException, XmlPullParserException { throws IOException, XmlPullParserException {
do { do {
xmlParser.next(); xmlParser.next();
if (ParserUtil.isStartTag(xmlParser, TtmlNode.TAG_STYLE)) { if (XmlPullParserUtil.isStartTag(xmlParser, TtmlNode.TAG_STYLE)) {
String parentStyleId = ParserUtil.getAttributeValue(xmlParser, ATTR_STYLE); String parentStyleId = XmlPullParserUtil.getAttributeValue(xmlParser, ATTR_STYLE);
TtmlStyle style = parseStyleAttributes(xmlParser, new TtmlStyle()); TtmlStyle style = parseStyleAttributes(xmlParser, new TtmlStyle());
if (parentStyleId != null) { if (parentStyleId != null) {
for (String id : parseStyleIds(parentStyleId)) { for (String id : parseStyleIds(parentStyleId)) {
@ -213,13 +214,13 @@ public final class TtmlParser extends SimpleSubtitleParser {
if (style.getId() != null) { if (style.getId() != null) {
globalStyles.put(style.getId(), style); 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); Pair<String, TtmlRegion> ttmlRegionInfo = parseRegionAttributes(xmlParser);
if (ttmlRegionInfo != null) { if (ttmlRegionInfo != null) {
globalRegions.put(ttmlRegionInfo.first, ttmlRegionInfo.second); globalRegions.put(ttmlRegionInfo.first, ttmlRegionInfo.second);
} }
} }
} while (!ParserUtil.isEndTag(xmlParser, TtmlNode.TAG_HEAD)); } while (!XmlPullParserUtil.isEndTag(xmlParser, TtmlNode.TAG_HEAD));
return globalStyles; 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. * terms of percentage of the viewport. Regions that do not correctly declare origin are ignored.
*/ */
private Pair<String, TtmlRegion> parseRegionAttributes(XmlPullParser xmlParser) { private Pair<String, TtmlRegion> parseRegionAttributes(XmlPullParser xmlParser) {
String regionId = ParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_ID); String regionId = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_ID);
String regionOrigin = ParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_ORIGIN); String regionOrigin = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_ORIGIN);
String regionExtent = ParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_EXTENT); String regionExtent = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_EXTENT);
if (regionOrigin == null || regionId == null) { if (regionOrigin == null || regionId == null) {
return null; return null;
} }
@ -298,7 +299,7 @@ public final class TtmlParser extends SimpleSubtitleParser {
try { try {
style = createIfNull(style); style = createIfNull(style);
parseFontSize(attributeValue, style); parseFontSize(attributeValue, style);
} catch (ParserException e) { } catch (TextDecoderException e) {
Log.w(TAG, "failed parsing fontSize value: '" + attributeValue + "'"); Log.w(TAG, "failed parsing fontSize value: '" + attributeValue + "'");
} }
break; break;
@ -358,7 +359,8 @@ public final class TtmlParser extends SimpleSubtitleParser {
} }
private TtmlNode parseNode(XmlPullParser parser, TtmlNode parent, 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 duration = 0;
long startTime = TtmlNode.UNDEFINED_TIME; long startTime = TtmlNode.UNDEFINED_TIME;
long endTime = TtmlNode.UNDEFINED_TIME; long endTime = TtmlNode.UNDEFINED_TIME;
@ -436,7 +438,7 @@ public final class TtmlParser extends SimpleSubtitleParser {
|| tag.equals(TtmlNode.TAG_SMPTE_INFORMATION); || 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+"); String[] expressions = expression.split("\\s+");
Matcher matcher; Matcher matcher;
if (expressions.length == 1) { 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" Log.w(TAG, "Multiple values in fontSize attribute. Picking the second value for vertical font"
+ " size and ignoring the first."); + " size and ignoring the first.");
} else { } 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); out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_PERCENT);
break; break;
default: default:
throw new ParserException("Invalid unit for fontSize: '" + unit + "'."); throw new TextDecoderException("Invalid unit for fontSize: '" + unit + "'.");
} }
out.setFontSize(Float.valueOf(matcher.group(1))); out.setFontSize(Float.valueOf(matcher.group(1)));
} else { } 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 time A string that includes the time expression.
* @param frameAndTickRate The effective frame and tick rates of the stream. * @param frameAndTickRate The effective frame and tick rates of the stream.
* @return The parsed timestamp in microseconds. * @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) private static long parseTimeExpression(String time, FrameAndTickRate frameAndTickRate)
throws ParserException { throws TextDecoderException {
Matcher matcher = CLOCK_TIME.matcher(time); Matcher matcher = CLOCK_TIME.matcher(time);
if (matcher.matches()) { if (matcher.matches()) {
String hours = matcher.group(1); String hours = matcher.group(1);
@ -531,7 +533,7 @@ public final class TtmlParser extends SimpleSubtitleParser {
} }
return (long) (offsetSeconds * C.MICROS_PER_SECOND); 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 { private static final class FrameAndTickRate {

View File

@ -16,18 +16,18 @@
package com.google.android.exoplayer2.text.tx3g; package com.google.android.exoplayer2.text.tx3g;
import com.google.android.exoplayer2.text.Cue; 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; import com.google.android.exoplayer2.text.Subtitle;
/** /**
* A subtitle parser for tx3g. * A {@link SimpleSubtitleDecoder} for tx3g.
* <p> * <p>
* Currently only supports parsing of a single text track. * Currently only supports parsing of a single text track.
*/ */
public final class Tx3gParser extends SimpleSubtitleParser { public final class Tx3gDecoder extends SimpleSubtitleDecoder {
public Tx3gParser() { public Tx3gDecoder() {
super("Tx3gParser"); super("Tx3gDecoder");
} }
@Override @Override

View File

@ -15,9 +15,9 @@
*/ */
package com.google.android.exoplayer2.text.webvtt; 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.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.ParsableByteArray;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
@ -26,9 +26,9 @@ import java.util.Collections;
import java.util.List; 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; private static final int BOX_HEADER_SIZE = 8;
@ -39,21 +39,21 @@ public final class Mp4WebvttParser extends SimpleSubtitleParser {
private final ParsableByteArray sampleData; private final ParsableByteArray sampleData;
private final WebvttCue.Builder builder; private final WebvttCue.Builder builder;
public Mp4WebvttParser() { public Mp4WebvttDecoder() {
super("Mp4WebvttParser"); super("Mp4WebvttDecoder");
sampleData = new ParsableByteArray(); sampleData = new ParsableByteArray();
builder = new WebvttCue.Builder(); builder = new WebvttCue.Builder();
} }
@Override @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: // 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. // first 4 bytes size and then 4 bytes type.
sampleData.reset(bytes, length); sampleData.reset(bytes, length);
List<Cue> resultingCueList = new ArrayList<>(); List<Cue> resultingCueList = new ArrayList<>();
while (sampleData.bytesLeft() > 0) { while (sampleData.bytesLeft() > 0) {
if (sampleData.bytesLeft() < BOX_HEADER_SIZE) { 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 boxSize = sampleData.readInt();
int boxType = 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, private static Cue parseVttCueBox(ParsableByteArray sampleData, WebvttCue.Builder builder,
int remainingCueBoxBytes) throws ParserException { int remainingCueBoxBytes) throws TextDecoderException {
builder.reset(); builder.reset();
while (remainingCueBoxBytes > 0) { while (remainingCueBoxBytes > 0) {
if (remainingCueBoxBytes < BOX_HEADER_SIZE) { 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 boxSize = sampleData.readInt();
int boxType = sampleData.readInt(); int boxType = sampleData.readInt();

View File

@ -15,8 +15,8 @@
*/ */
package com.google.android.exoplayer2.text.webvtt; package com.google.android.exoplayer2.text.webvtt;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.text.SimpleSubtitleDecoder;
import com.google.android.exoplayer2.text.SimpleSubtitleParser; import com.google.android.exoplayer2.text.TextDecoderException;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import android.text.TextUtils; import android.text.TextUtils;
@ -25,11 +25,11 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* A simple WebVTT parser. * A {@link SimpleSubtitleDecoder} for WebVTT.
* <p> * <p>
* @see <a href="http://dev.w3.org/html5/webvtt">WebVTT specification</a> * @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 NO_EVENT_FOUND = -1;
private static final int END_OF_FILE_FOUND = 0; 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 CssParser cssParser;
private final List<WebvttCssStyle> definedStyles; private final List<WebvttCssStyle> definedStyles;
public WebvttParser() { public WebvttDecoder() {
super("WebvttParser"); super("WebvttDecoder");
cueParser = new WebvttCueParser(); cueParser = new WebvttCueParser();
parsableWebvttData = new ParsableByteArray(); parsableWebvttData = new ParsableByteArray();
webvttCueBuilder = new WebvttCue.Builder(); webvttCueBuilder = new WebvttCue.Builder();
@ -55,7 +55,7 @@ public final class WebvttParser extends SimpleSubtitleParser {
} }
@Override @Override
protected WebvttSubtitle decode(byte[] bytes, int length) throws ParserException { protected WebvttSubtitle decode(byte[] bytes, int length) throws TextDecoderException {
parsableWebvttData.reset(bytes, length); parsableWebvttData.reset(bytes, length);
// Initialization for consistent starting state. // Initialization for consistent starting state.
webvttCueBuilder.reset(); webvttCueBuilder.reset();
@ -72,7 +72,7 @@ public final class WebvttParser extends SimpleSubtitleParser {
skipComment(parsableWebvttData); skipComment(parsableWebvttData);
} else if (eventFound == STYLE_BLOCK_FOUND) { } else if (eventFound == STYLE_BLOCK_FOUND) {
if (!subtitles.isEmpty()) { 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. parsableWebvttData.readLine(); // Consume the "STYLE" header.
WebvttCssStyle styleBlock = cssParser.parseBlock(parsableWebvttData); WebvttCssStyle styleBlock = cssParser.parseBlock(parsableWebvttData);

View File

@ -15,7 +15,7 @@
*/ */
package com.google.android.exoplayer2.text.webvtt; 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 com.google.android.exoplayer2.util.ParsableByteArray;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@ -35,12 +35,12 @@ public final class WebvttParserUtil {
* Reads and validates the first line of a WebVTT file. * Reads and validates the first line of a WebVTT file.
* *
* @param input The input from which the line should be read. * @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(); String line = input.readLine();
if (line == null || !HEADER.matcher(line).matches()) { 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. * Parses a percentage string.
* @param s contains the number to parse. *
* @return a float scaled number. 1.0 represents 100%. * @param s The percentage string.
* @throws NumberFormatException if the number format is invalid or does not end with '%'. * @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 { public static float parsePercentage(String s) throws NumberFormatException {
if (!s.endsWith("%")) { if (!s.endsWith("%")) {

View File

@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.decoder.DecoderCounters;
import android.widget.TextView; import android.widget.TextView;
@ -116,7 +116,8 @@ public final class DebugTextViewHelper implements Runnable, ExoPlayer.EventListe
return ""; return "";
} }
return "\n" + format.sampleMimeType + "(id:" + format.id + " r:" + format.width + "x" return "\n" + format.sampleMimeType + "(id:" + format.id + " r:" + format.width + "x"
+ format.height + getCodecCounterBufferCountString(player.getVideoCodecCounters()) + ")"; + format.height + getDecoderCountersBufferCountString(player.getVideoDecoderCounters())
+ ")";
} }
private String getAudioString() { private String getAudioString() {
@ -125,11 +126,11 @@ public final class DebugTextViewHelper implements Runnable, ExoPlayer.EventListe
return ""; return "";
} }
return "\n" + format.sampleMimeType + "(id:" + format.id + " hz:" + format.sampleRate + " ch:" 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) { if (counters == null) {
return ""; return "";
} }

View File

@ -16,7 +16,7 @@
package com.google.android.exoplayer2.ui; package com.google.android.exoplayer2.ui;
import com.google.android.exoplayer2.ExoPlayer; 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 com.google.android.exoplayer2.util.Util;
import android.widget.MediaController.MediaPlayerControl; import android.widget.MediaController.MediaPlayerControl;

View File

@ -128,10 +128,10 @@ public final class CodecSpecificDataUtil {
*/ */
public static byte[] buildAacAudioSpecificConfig(int audioObjectType, int sampleRateIndex, public static byte[] buildAacAudioSpecificConfig(int audioObjectType, int sampleRateIndex,
int channelConfig) { int channelConfig) {
byte[] audioSpecificConfig = new byte[2]; byte[] specificConfig = new byte[2];
audioSpecificConfig[0] = (byte) ((audioObjectType << 3) & 0xF8 | (sampleRateIndex >> 1) & 0x07); specificConfig[0] = (byte) (((audioObjectType << 3) & 0xF8) | ((sampleRateIndex >> 1) & 0x07));
audioSpecificConfig[1] = (byte) ((sampleRateIndex << 7) & 0x80 | (channelConfig << 3) & 0x78); specificConfig[1] = (byte) (((sampleRateIndex << 7) & 0x80) | ((channelConfig << 3) & 0x78));
return audioSpecificConfig; return specificConfig;
} }
/** /**

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.android.exoplayer2; package com.google.android.exoplayer2.util;
/** /**
* Tracks the progression of media time. * Tracks the progression of media time.

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.android.exoplayer2; package com.google.android.exoplayer2.util;
import android.os.SystemClock; 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 * 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()}. * 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; private boolean started;

View File

@ -49,7 +49,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
* Miscellaneous utility functions. * Miscellaneous utility methods.
*/ */
public final class Util { public final class Util {
@ -346,7 +346,7 @@ public final class Util {
/** /**
* Parses an xs:duration attribute value, returning the parsed duration in milliseconds. * 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. * @return The parsed duration in milliseconds.
*/ */
public static long parseXsDuration(String value) { 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 * Parses an xs:dateTime attribute value, returning the parsed timestamp in milliseconds since
* the epoch. * 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. * @return The parsed timestamp in milliseconds since the epoch.
*/ */
public static long parseXsDateTime(String value) throws ParseException { public static long parseXsDateTime(String value) throws ParseException {

View File

@ -19,11 +19,11 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; 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 { public static boolean isEndTag(XmlPullParser xpp, String name) throws XmlPullParserException {
return isEndTag(xpp) && xpp.getName().equals(name); return isEndTag(xpp) && xpp.getName().equals(name);

View File

@ -13,14 +13,21 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.C;
import com.google.android.exoplayer2.VideoRendererEventListener.EventDispatcher; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.drm.DrmSessionManager; 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.MimeTypes;
import com.google.android.exoplayer2.util.TraceUtil; import com.google.android.exoplayer2.util.TraceUtil;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.VideoRendererEventListener.EventDispatcher;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.annotation.TargetApi; import android.annotation.TargetApi;
@ -173,7 +180,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
if (!MimeTypes.isVideo(mimeType)) { if (!MimeTypes.isVideo(mimeType)) {
return FORMAT_UNSUPPORTED_TYPE; return FORMAT_UNSUPPORTED_TYPE;
} }
MediaCodecDecoderInfo decoderInfo = mediaCodecSelector.getDecoderInfo(mimeType, MediaCodecInfo decoderInfo = mediaCodecSelector.getDecoderInfo(mimeType,
format.requiresSecureDecryption); format.requiresSecureDecryption);
if (decoderInfo == null) { if (decoderInfo == null) {
return FORMAT_UNSUPPORTED_SUBTYPE; return FORMAT_UNSUPPORTED_SUBTYPE;
@ -205,7 +212,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
@Override @Override
protected void onEnabled(boolean joining) throws ExoPlaybackException { protected void onEnabled(boolean joining) throws ExoPlaybackException {
super.onEnabled(joining); super.onEnabled(joining);
eventDispatcher.enabled(codecCounters); eventDispatcher.enabled(decoderCounters);
frameReleaseTimeHelper.enable(); frameReleaseTimeHelper.enable();
} }
@ -282,8 +289,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
try { try {
super.onDisabled(); super.onDisabled();
} finally { } finally {
codecCounters.ensureUpdated(); decoderCounters.ensureUpdated();
eventDispatcher.disabled(codecCounters); eventDispatcher.disabled(decoderCounters);
} }
} }
@ -307,7 +314,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
this.surface = surface; this.surface = surface;
this.reportedDrawnToSurface = false; this.reportedDrawnToSurface = false;
int state = getState(); int state = getState();
if (state == Renderer.STATE_ENABLED || state == Renderer.STATE_STARTED) { if (state == STATE_ENABLED || state == STATE_STARTED) {
releaseCodec(); releaseCodec();
maybeInitCodec(); maybeInitCodec();
} }
@ -403,7 +410,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
return true; return true;
} }
if (getState() != Renderer.STATE_STARTED) { if (getState() != STATE_STARTED) {
return false; return false;
} }
@ -458,18 +465,18 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
TraceUtil.beginSection("skipVideoBuffer"); TraceUtil.beginSection("skipVideoBuffer");
codec.releaseOutputBuffer(bufferIndex, false); codec.releaseOutputBuffer(bufferIndex, false);
TraceUtil.endSection(); TraceUtil.endSection();
codecCounters.skippedOutputBufferCount++; decoderCounters.skippedOutputBufferCount++;
} }
protected void dropOutputBuffer(MediaCodec codec, int bufferIndex) { protected void dropOutputBuffer(MediaCodec codec, int bufferIndex) {
TraceUtil.beginSection("dropVideoBuffer"); TraceUtil.beginSection("dropVideoBuffer");
codec.releaseOutputBuffer(bufferIndex, false); codec.releaseOutputBuffer(bufferIndex, false);
TraceUtil.endSection(); TraceUtil.endSection();
codecCounters.droppedOutputBufferCount++; decoderCounters.droppedOutputBufferCount++;
droppedFrameCount++; droppedFrameCount++;
consecutiveDroppedFrameCount++; consecutiveDroppedFrameCount++;
codecCounters.maxConsecutiveDroppedOutputBufferCount = Math.max(consecutiveDroppedFrameCount, decoderCounters.maxConsecutiveDroppedOutputBufferCount = Math.max(consecutiveDroppedFrameCount,
codecCounters.maxConsecutiveDroppedOutputBufferCount); decoderCounters.maxConsecutiveDroppedOutputBufferCount);
if (droppedFrameCount == maxDroppedFrameCountToNotify) { if (droppedFrameCount == maxDroppedFrameCountToNotify) {
maybeNotifyDroppedFrameCount(); maybeNotifyDroppedFrameCount();
} }
@ -480,7 +487,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
TraceUtil.beginSection("releaseOutputBuffer"); TraceUtil.beginSection("releaseOutputBuffer");
codec.releaseOutputBuffer(bufferIndex, true); codec.releaseOutputBuffer(bufferIndex, true);
TraceUtil.endSection(); TraceUtil.endSection();
codecCounters.renderedOutputBufferCount++; decoderCounters.renderedOutputBufferCount++;
consecutiveDroppedFrameCount = 0; consecutiveDroppedFrameCount = 0;
renderedFirstFrame = true; renderedFirstFrame = true;
maybeNotifyDrawnToSurface(); maybeNotifyDrawnToSurface();
@ -492,7 +499,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
TraceUtil.beginSection("releaseOutputBuffer"); TraceUtil.beginSection("releaseOutputBuffer");
codec.releaseOutputBuffer(bufferIndex, releaseTimeNs); codec.releaseOutputBuffer(bufferIndex, releaseTimeNs);
TraceUtil.endSection(); TraceUtil.endSection();
codecCounters.renderedOutputBufferCount++; decoderCounters.renderedOutputBufferCount++;
consecutiveDroppedFrameCount = 0; consecutiveDroppedFrameCount = 0;
renderedFirstFrame = true; renderedFirstFrame = true;
maybeNotifyDrawnToSurface(); maybeNotifyDrawnToSurface();

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.android.exoplayer2; package com.google.android.exoplayer2.video;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;

Some files were not shown because too many files have changed in this diff Show More