Commit 0b141aee by Oliver Woodman

Revert "Improving handling of atoms with size less than header in FragmentedMp4Extractor."

This reverts commit 71186ef1.
parent 71186ef1
Showing with 314 additions and 86 deletions
# Release notes # # Release notes #
### r2.1.0 ### ### r2.1.1 ###
Bugfix release only. Users of r2.1.0 and r2.0.x should proactively update to
this version.
This release contains important bug fixes. Users of r2.0.x should proactively * Fix some subtitle types (e.g. WebVTT) being displayed out of sync
update to this version. ([#2208](https://github.com/google/ExoPlayer/issues/2208)).
* Fix incorrect position reporting for on-demand HLS media that includes
EXT-X-PROGRAM-DATE-TIME tags
([#2224](https://github.com/google/ExoPlayer/issues/2224)).
* Fix issue where playbacks could get stuck in the initial buffering state if
over 1MB of data needs to be read to initialize the playback.
### r2.1.0 ###
* HLS: Support for seeking in live streams * HLS: Support for seeking in live streams
([#87](https://github.com/google/ExoPlayer/issues/87)). ([#87](https://github.com/google/ExoPlayer/issues/87)).
......
...@@ -35,7 +35,7 @@ allprojects { ...@@ -35,7 +35,7 @@ allprojects {
releaseRepoName = 'exoplayer' releaseRepoName = 'exoplayer'
releaseUserOrg = 'google' releaseUserOrg = 'google'
releaseGroupId = 'com.google.android.exoplayer' releaseGroupId = 'com.google.android.exoplayer'
releaseVersion = 'r2.1.0' releaseVersion = 'r2.1.1'
releaseWebsite = 'https://github.com/google/ExoPlayer' releaseWebsite = 'https://github.com/google/ExoPlayer'
} }
} }
...@@ -16,8 +16,8 @@ ...@@ -16,8 +16,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.exoplayer2.demo" package="com.google.android.exoplayer2.demo"
android:versionCode="2100" android:versionCode="2101"
android:versionName="2.1.0"> android:versionName="2.1.1">
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
......
...@@ -183,30 +183,54 @@ ...@@ -183,30 +183,54 @@
"uri": "https://storage.googleapis.com/wvmedia/clear/vp9/tears/tears_uhd.mpd" "uri": "https://storage.googleapis.com/wvmedia/clear/vp9/tears/tears_uhd.mpd"
}, },
{ {
"name": "WV: Secure SD & HD (WebM,VP9)", "name": "WV: Secure Fullsample SD & HD (WebM,VP9)",
"uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears.mpd", "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears.mpd",
"drm_scheme": "widevine", "drm_scheme": "widevine",
"drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
}, },
{ {
"name": "WV: Secure SD (WebM,VP9)", "name": "WV: Secure Fullsample SD (WebM,VP9)",
"uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears_sd.mpd", "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears_sd.mpd",
"drm_scheme": "widevine", "drm_scheme": "widevine",
"drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
}, },
{ {
"name": "WV: Secure HD (WebM,VP9)", "name": "WV: Secure Fullsample HD (WebM,VP9)",
"uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears_hd.mpd", "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears_hd.mpd",
"drm_scheme": "widevine", "drm_scheme": "widevine",
"drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
}, },
{ {
"name": "WV: Secure UHD (WebM,VP9)", "name": "WV: Secure Fullsample UHD (WebM,VP9)",
"uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears_uhd.mpd", "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears_uhd.mpd",
"drm_scheme": "widevine", "drm_scheme": "widevine",
"drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
}, },
{ {
"name": "WV: Secure Subsample SD & HD (WebM,VP9)",
"uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/subsample/24fps/tears/tears.mpd",
"drm_scheme": "widevine",
"drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
},
{
"name": "WV: Secure Subsample SD (WebM,VP9)",
"uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/subsample/24fps/tears/tears_sd.mpd",
"drm_scheme": "widevine",
"drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
},
{
"name": "WV: Secure Subsample HD (WebM,VP9)",
"uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/subsample/24fps/tears/tears_hd.mpd",
"drm_scheme": "widevine",
"drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
},
{
"name": "WV: Secure Subsample UHD (WebM,VP9)",
"uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/subsample/24fps/tears/tears_uhd.mpd",
"drm_scheme": "widevine",
"drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
},
{
"name": "WV: Secure Subsample (WebM, VP9 with altref)", "name": "WV: Secure Subsample (WebM, VP9 with altref)",
"uri": "https://storage.googleapis.com/widevine_test/vp9/sintel_1080p_vp9_altref_subsample/sintel_1080p_vp9_altref_subsample.mpd", "uri": "https://storage.googleapis.com/widevine_test/vp9/sintel_1080p_vp9_altref_subsample/sintel_1080p_vp9_altref_subsample.mpd",
"drm_scheme": "widevine", "drm_scheme": "widevine",
......
...@@ -31,6 +31,7 @@ import com.google.android.exoplayer2.upstream.Allocator; ...@@ -31,6 +31,7 @@ import com.google.android.exoplayer2.upstream.Allocator;
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 java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
...@@ -48,12 +49,25 @@ public final class ExoPlayerTest extends TestCase { ...@@ -48,12 +49,25 @@ public final class ExoPlayerTest extends TestCase {
*/ */
private static final int TIMEOUT_MS = 10000; private static final int TIMEOUT_MS = 10000;
/**
* Tests playback of a source that exposes a single period.
*/
public void testPlayToEnd() throws Exception { public void testPlayToEnd() throws Exception {
PlayerWrapper playerWrapper = new PlayerWrapper(); PlayerWrapper playerWrapper = new PlayerWrapper();
Format format = Format.createVideoSampleFormat(null, MimeTypes.VIDEO_H264, null, Format format = Format.createVideoSampleFormat(null, MimeTypes.VIDEO_H264, null,
Format.NO_VALUE, Format.NO_VALUE, 1280, 720, Format.NO_VALUE, null, null); Format.NO_VALUE, Format.NO_VALUE, 1280, 720, Format.NO_VALUE, null, null);
playerWrapper.setup(new SinglePeriodTimeline(0, false), new Object(), format); playerWrapper.setup(new SinglePeriodTimeline(0, false), null, format);
playerWrapper.blockUntilEndedOrError(TIMEOUT_MS); playerWrapper.blockUntilEnded(TIMEOUT_MS);
}
/**
* Tests playback of a source that exposes an empty timeline. Playback is expected to end without
* error.
*/
public void testPlayEmptyTimeline() throws Exception {
PlayerWrapper playerWrapper = new PlayerWrapper();
playerWrapper.setup(Timeline.EMPTY, null, null);
playerWrapper.blockUntilEnded(TIMEOUT_MS);
} }
/** /**
...@@ -70,7 +84,6 @@ public final class ExoPlayerTest extends TestCase { ...@@ -70,7 +84,6 @@ public final class ExoPlayerTest extends TestCase {
private Format expectedFormat; private Format expectedFormat;
private ExoPlayer player; private ExoPlayer player;
private Exception exception; private Exception exception;
private boolean seenPositionDiscontinuity;
public PlayerWrapper() { public PlayerWrapper() {
endedCountDownLatch = new CountDownLatch(1); endedCountDownLatch = new CountDownLatch(1);
...@@ -81,12 +94,11 @@ public final class ExoPlayerTest extends TestCase { ...@@ -81,12 +94,11 @@ public final class ExoPlayerTest extends TestCase {
// Called on the test thread. // Called on the test thread.
public void blockUntilEndedOrError(long timeoutMs) throws Exception { public void blockUntilEnded(long timeoutMs) throws Exception {
if (!endedCountDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS)) { if (!endedCountDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS)) {
exception = new TimeoutException("Test playback timed out."); exception = new TimeoutException("Test playback timed out.");
} }
release(); release();
// Throw any pending exception (from playback, timing out or releasing). // Throw any pending exception (from playback, timing out or releasing).
if (exception != null) { if (exception != null) {
throw exception; throw exception;
...@@ -108,7 +120,7 @@ public final class ExoPlayerTest extends TestCase { ...@@ -108,7 +120,7 @@ public final class ExoPlayerTest extends TestCase {
player.setPlayWhenReady(true); player.setPlayWhenReady(true);
player.prepare(new FakeMediaSource(timeline, manifest, format)); player.prepare(new FakeMediaSource(timeline, manifest, format));
} catch (Exception e) { } catch (Exception e) {
handlePlayerException(e); handleError(e);
} }
} }
}); });
...@@ -123,7 +135,7 @@ public final class ExoPlayerTest extends TestCase { ...@@ -123,7 +135,7 @@ public final class ExoPlayerTest extends TestCase {
player.release(); player.release();
} }
} catch (Exception e) { } catch (Exception e) {
handlePlayerException(e); handleError(e);
} finally { } finally {
playerThread.quit(); playerThread.quit();
} }
...@@ -132,7 +144,7 @@ public final class ExoPlayerTest extends TestCase { ...@@ -132,7 +144,7 @@ public final class ExoPlayerTest extends TestCase {
playerThread.join(); playerThread.join();
} }
private void handlePlayerException(Exception exception) { private void handleError(Exception exception) {
if (this.exception == null) { if (this.exception == null) {
this.exception = exception; this.exception = exception;
} }
...@@ -167,20 +179,13 @@ public final class ExoPlayerTest extends TestCase { ...@@ -167,20 +179,13 @@ public final class ExoPlayerTest extends TestCase {
@Override @Override
public void onPlayerError(ExoPlaybackException exception) { public void onPlayerError(ExoPlaybackException exception) {
this.exception = exception; handleError(exception);
endedCountDownLatch.countDown();
} }
@Override @Override
public void onPositionDiscontinuity() { public void onPositionDiscontinuity() {
assertFalse(seenPositionDiscontinuity); // Should never happen.
assertEquals(0, player.getCurrentWindowIndex()); handleError(new IllegalStateException("Received position discontinuity"));
assertEquals(0, player.getCurrentPeriodIndex());
assertEquals(0, player.getCurrentPosition());
assertEquals(0, player.getBufferedPosition());
assertEquals(expectedTimeline, player.getCurrentTimeline());
assertEquals(expectedManifest, player.getCurrentManifest());
seenPositionDiscontinuity = true;
} }
} }
...@@ -194,17 +199,16 @@ public final class ExoPlayerTest extends TestCase { ...@@ -194,17 +199,16 @@ public final class ExoPlayerTest extends TestCase {
private final Timeline timeline; private final Timeline timeline;
private final Object manifest; private final Object manifest;
private final Format format; private final Format format;
private final ArrayList<FakeMediaPeriod> activeMediaPeriods;
private FakeMediaPeriod mediaPeriod;
private boolean preparedSource; private boolean preparedSource;
private boolean releasedPeriod;
private boolean releasedSource; private boolean releasedSource;
public FakeMediaSource(Timeline timeline, Object manifest, Format format) { public FakeMediaSource(Timeline timeline, Object manifest, Format format) {
Assertions.checkArgument(timeline.getPeriodCount() == 1);
this.timeline = timeline; this.timeline = timeline;
this.manifest = manifest; this.manifest = manifest;
this.format = format; this.format = format;
activeMediaPeriods = new ArrayList<>();
} }
@Override @Override
...@@ -221,33 +225,29 @@ public final class ExoPlayerTest extends TestCase { ...@@ -221,33 +225,29 @@ public final class ExoPlayerTest extends TestCase {
@Override @Override
public MediaPeriod createPeriod(int index, Allocator allocator, long positionUs) { public MediaPeriod createPeriod(int index, Allocator allocator, long positionUs) {
Assertions.checkIndex(index, 0, timeline.getPeriodCount());
assertTrue(preparedSource); assertTrue(preparedSource);
assertNull(mediaPeriod);
assertFalse(releasedPeriod);
assertFalse(releasedSource); assertFalse(releasedSource);
assertEquals(0, index); assertEquals(0, index);
assertEquals(0, positionUs); assertEquals(0, positionUs);
mediaPeriod = new FakeMediaPeriod(format); FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(format);
activeMediaPeriods.add(mediaPeriod);
return mediaPeriod; return mediaPeriod;
} }
@Override @Override
public void releasePeriod(MediaPeriod mediaPeriod) { public void releasePeriod(MediaPeriod mediaPeriod) {
assertTrue(preparedSource); assertTrue(preparedSource);
assertNotNull(this.mediaPeriod);
assertFalse(releasedPeriod);
assertFalse(releasedSource); assertFalse(releasedSource);
assertEquals(this.mediaPeriod, mediaPeriod); assertTrue(activeMediaPeriods.remove(mediaPeriod));
this.mediaPeriod.release(); ((FakeMediaPeriod) mediaPeriod).release();
releasedPeriod = true;
} }
@Override @Override
public void releaseSource() { public void releaseSource() {
assertTrue(preparedSource); assertTrue(preparedSource);
assertNotNull(this.mediaPeriod);
assertTrue(releasedPeriod);
assertFalse(releasedSource); assertFalse(releasedSource);
assertTrue(activeMediaPeriods.isEmpty());
releasedSource = true; releasedSource = true;
} }
...@@ -400,7 +400,6 @@ public final class ExoPlayerTest extends TestCase { ...@@ -400,7 +400,6 @@ public final class ExoPlayerTest extends TestCase {
public FakeVideoRenderer(Format expectedFormat) { public FakeVideoRenderer(Format expectedFormat) {
super(C.TRACK_TYPE_VIDEO); super(C.TRACK_TYPE_VIDEO);
Assertions.checkArgument(MimeTypes.isVideo(expectedFormat.sampleMimeType));
this.expectedFormat = expectedFormat; this.expectedFormat = expectedFormat;
} }
......
...@@ -267,6 +267,12 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { ...@@ -267,6 +267,12 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
return streamIsFinal ? C.RESULT_BUFFER_READ : C.RESULT_NOTHING_READ; return streamIsFinal ? C.RESULT_BUFFER_READ : C.RESULT_NOTHING_READ;
} }
buffer.timeUs += streamOffsetUs; buffer.timeUs += streamOffsetUs;
} else if (result == C.RESULT_FORMAT_READ) {
Format format = formatHolder.format;
if (format.subsampleOffsetUs != Format.OFFSET_SAMPLE_RELATIVE) {
format = format.copyWithSubsampleOffsetUs(format.subsampleOffsetUs + streamOffsetUs);
formatHolder.format = format;
}
} }
return result; return result;
} }
......
...@@ -97,6 +97,13 @@ public final class C { ...@@ -97,6 +97,13 @@ public final class C {
public static final int CRYPTO_MODE_AES_CBC = MediaCodec.CRYPTO_MODE_AES_CBC; public static final int CRYPTO_MODE_AES_CBC = MediaCodec.CRYPTO_MODE_AES_CBC;
/** /**
* Represents an unset {@link android.media.AudioTrack} session identifier. Equal to
* {@link AudioManager#AUDIO_SESSION_ID_GENERATE}.
*/
@SuppressWarnings("InlinedApi")
public static final int AUDIO_SESSION_ID_UNSET = AudioManager.AUDIO_SESSION_ID_GENERATE;
/**
* Represents an audio encoding, or an invalid or unset value. * Represents an audio encoding, or an invalid or unset value.
*/ */
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
......
...@@ -332,8 +332,10 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -332,8 +332,10 @@ import java.util.concurrent.CopyOnWriteArraySet;
case ExoPlayerImplInternal.MSG_SEEK_ACK: { case ExoPlayerImplInternal.MSG_SEEK_ACK: {
if (--pendingSeekAcks == 0) { if (--pendingSeekAcks == 0) {
playbackInfo = (ExoPlayerImplInternal.PlaybackInfo) msg.obj; playbackInfo = (ExoPlayerImplInternal.PlaybackInfo) msg.obj;
for (EventListener listener : listeners) { if (msg.arg1 != 0) {
listener.onPositionDiscontinuity(); for (EventListener listener : listeners) {
listener.onPositionDiscontinuity();
}
} }
} }
break; break;
......
...@@ -559,7 +559,7 @@ import java.io.IOException; ...@@ -559,7 +559,7 @@ import java.io.IOException;
// The seek position was valid for the timeline that it was performed into, but the // The seek position was valid for the timeline that it was performed into, but the
// timeline has changed and a suitable seek position could not be resolved in the new one. // timeline has changed and a suitable seek position could not be resolved in the new one.
playbackInfo = new PlaybackInfo(0, 0); playbackInfo = new PlaybackInfo(0, 0);
eventHandler.obtainMessage(MSG_SEEK_ACK, playbackInfo).sendToTarget(); eventHandler.obtainMessage(MSG_SEEK_ACK, 1, 0, playbackInfo).sendToTarget();
// Set the internal position to (0,TIME_UNSET) so that a subsequent seek to (0,0) isn't // Set the internal position to (0,TIME_UNSET) so that a subsequent seek to (0,0) isn't
// ignored. // ignored.
playbackInfo = new PlaybackInfo(0, C.TIME_UNSET); playbackInfo = new PlaybackInfo(0, C.TIME_UNSET);
...@@ -569,6 +569,7 @@ import java.io.IOException; ...@@ -569,6 +569,7 @@ import java.io.IOException;
return; return;
} }
boolean seekPositionAdjusted = seekPosition.windowPositionUs == C.TIME_UNSET;
int periodIndex = periodPosition.first; int periodIndex = periodPosition.first;
long periodPositionUs = periodPosition.second; long periodPositionUs = periodPosition.second;
...@@ -578,10 +579,13 @@ import java.io.IOException; ...@@ -578,10 +579,13 @@ import java.io.IOException;
// Seek position equals the current position. Do nothing. // Seek position equals the current position. Do nothing.
return; return;
} }
periodPositionUs = seekToPeriodPosition(periodIndex, periodPositionUs); long newPeriodPositionUs = seekToPeriodPosition(periodIndex, periodPositionUs);
seekPositionAdjusted |= periodPositionUs != newPeriodPositionUs;
periodPositionUs = newPeriodPositionUs;
} finally { } finally {
playbackInfo = new PlaybackInfo(periodIndex, periodPositionUs); playbackInfo = new PlaybackInfo(periodIndex, periodPositionUs);
eventHandler.obtainMessage(MSG_SEEK_ACK, playbackInfo).sendToTarget(); eventHandler.obtainMessage(MSG_SEEK_ACK, seekPositionAdjusted ? 1 : 0, 0, playbackInfo)
.sendToTarget();
} }
} }
...@@ -676,6 +680,7 @@ import java.io.IOException; ...@@ -676,6 +680,7 @@ import java.io.IOException;
standaloneMediaClock.stop(); standaloneMediaClock.stop();
rendererMediaClock = null; rendererMediaClock = null;
rendererMediaClockSource = null; rendererMediaClockSource = null;
rendererPositionUs = RENDERER_TIMESTAMP_OFFSET_US;
for (Renderer renderer : enabledRenderers) { for (Renderer renderer : enabledRenderers) {
try { try {
ensureStopped(renderer); ensureStopped(renderer);
...@@ -823,9 +828,6 @@ import java.io.IOException; ...@@ -823,9 +828,6 @@ import java.io.IOException;
} }
private boolean haveSufficientBuffer(boolean rebuffering) { private boolean haveSufficientBuffer(boolean rebuffering) {
if (loadingPeriodHolder == null) {
return false;
}
long loadingPeriodBufferedPositionUs = !loadingPeriodHolder.prepared long loadingPeriodBufferedPositionUs = !loadingPeriodHolder.prepared
? loadingPeriodHolder.startPositionUs ? loadingPeriodHolder.startPositionUs
: loadingPeriodHolder.mediaPeriod.getBufferedPositionUs(); : loadingPeriodHolder.mediaPeriod.getBufferedPositionUs();
...@@ -1287,7 +1289,8 @@ import java.io.IOException; ...@@ -1287,7 +1289,8 @@ import java.io.IOException;
} }
private void maybeContinueLoading() { private void maybeContinueLoading() {
long nextLoadPositionUs = loadingPeriodHolder.mediaPeriod.getNextLoadPositionUs(); long nextLoadPositionUs = !loadingPeriodHolder.prepared ? 0
: loadingPeriodHolder.mediaPeriod.getNextLoadPositionUs();
if (nextLoadPositionUs == C.TIME_END_OF_SOURCE) { if (nextLoadPositionUs == C.TIME_END_OF_SOURCE) {
setIsLoading(false); setIsLoading(false);
} else { } else {
......
...@@ -23,7 +23,7 @@ public interface ExoPlayerLibraryInfo { ...@@ -23,7 +23,7 @@ public interface ExoPlayerLibraryInfo {
/** /**
* The version of the library, expressed as a string. * The version of the library, expressed as a string.
*/ */
String VERSION = "2.1.0"; String VERSION = "2.1.1";
/** /**
* The version of the library, expressed as an integer. * The version of the library, expressed as an integer.
...@@ -32,7 +32,7 @@ public interface ExoPlayerLibraryInfo { ...@@ -32,7 +32,7 @@ public interface ExoPlayerLibraryInfo {
* corresponding integer version 1002003 (001-002-003), and "123.45.6" has the corresponding * corresponding integer version 1002003 (001-002-003), and "123.45.6" has the corresponding
* integer version 123045006 (123-045-006). * integer version 123045006 (123-045-006).
*/ */
int VERSION_INT = 2001000; int VERSION_INT = 2001001;
/** /**
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions} * Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}
......
...@@ -29,7 +29,6 @@ import android.view.SurfaceView; ...@@ -29,7 +29,6 @@ import android.view.SurfaceView;
import android.view.TextureView; import android.view.TextureView;
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.AudioRendererEventListener;
import com.google.android.exoplayer2.audio.AudioTrack;
import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer; import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer;
import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManager;
...@@ -178,7 +177,7 @@ public class SimpleExoPlayer implements ExoPlayer { ...@@ -178,7 +177,7 @@ public class SimpleExoPlayer implements ExoPlayer {
// Set initial values. // Set initial values.
audioVolume = 1; audioVolume = 1;
audioSessionId = AudioTrack.SESSION_ID_NOT_SET; audioSessionId = C.AUDIO_SESSION_ID_UNSET;
audioStreamType = C.STREAM_TYPE_DEFAULT; audioStreamType = C.STREAM_TYPE_DEFAULT;
videoScalingMode = C.VIDEO_SCALING_MODE_DEFAULT; videoScalingMode = C.VIDEO_SCALING_MODE_DEFAULT;
...@@ -393,7 +392,7 @@ public class SimpleExoPlayer implements ExoPlayer { ...@@ -393,7 +392,7 @@ public class SimpleExoPlayer implements ExoPlayer {
} }
/** /**
* Returns the audio session identifier, or {@code AudioTrack.SESSION_ID_NOT_SET} if not set. * Returns the audio session identifier, or {@link C#AUDIO_SESSION_ID_UNSET} if not set.
*/ */
public int getAudioSessionId() { public int getAudioSessionId() {
return audioSessionId; return audioSessionId;
...@@ -949,7 +948,7 @@ public class SimpleExoPlayer implements ExoPlayer { ...@@ -949,7 +948,7 @@ public class SimpleExoPlayer implements ExoPlayer {
} }
audioFormat = null; audioFormat = null;
audioDecoderCounters = null; audioDecoderCounters = null;
audioSessionId = AudioTrack.SESSION_ID_NOT_SET; audioSessionId = C.AUDIO_SESSION_ID_UNSET;
} }
// TextRenderer.Output implementation // TextRenderer.Output implementation
......
...@@ -129,7 +129,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -129,7 +129,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
boolean playClearSamplesWithoutKeys, Handler eventHandler, boolean playClearSamplesWithoutKeys, Handler eventHandler,
AudioRendererEventListener eventListener, AudioCapabilities audioCapabilities) { AudioRendererEventListener eventListener, AudioCapabilities audioCapabilities) {
super(C.TRACK_TYPE_AUDIO, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys); super(C.TRACK_TYPE_AUDIO, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys);
audioSessionId = AudioTrack.SESSION_ID_NOT_SET; audioSessionId = C.AUDIO_SESSION_ID_UNSET;
audioTrack = new AudioTrack(audioCapabilities, this); audioTrack = new AudioTrack(audioCapabilities, this);
eventDispatcher = new EventDispatcher(eventHandler, eventListener); eventDispatcher = new EventDispatcher(eventHandler, eventListener);
} }
...@@ -274,7 +274,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -274,7 +274,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
@Override @Override
protected void onDisabled() { protected void onDisabled() {
audioSessionId = AudioTrack.SESSION_ID_NOT_SET; audioSessionId = C.AUDIO_SESSION_ID_UNSET;
try { try {
audioTrack.release(); audioTrack.release();
} finally { } finally {
...@@ -328,8 +328,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -328,8 +328,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
if (!audioTrack.isInitialized()) { if (!audioTrack.isInitialized()) {
// Initialize the AudioTrack now. // Initialize the AudioTrack now.
try { try {
if (audioSessionId == AudioTrack.SESSION_ID_NOT_SET) { if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) {
audioSessionId = audioTrack.initialize(AudioTrack.SESSION_ID_NOT_SET); audioSessionId = audioTrack.initialize(C.AUDIO_SESSION_ID_UNSET);
eventDispatcher.audioSessionId(audioSessionId); eventDispatcher.audioSessionId(audioSessionId);
onAudioSessionId(audioSessionId); onAudioSessionId(audioSessionId);
} else { } else {
...@@ -387,7 +387,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -387,7 +387,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
case C.MSG_SET_STREAM_TYPE: case C.MSG_SET_STREAM_TYPE:
@C.StreamType int streamType = (Integer) message; @C.StreamType int streamType = (Integer) message;
if (audioTrack.setStreamType(streamType)) { if (audioTrack.setStreamType(streamType)) {
audioSessionId = AudioTrack.SESSION_ID_NOT_SET; audioSessionId = C.AUDIO_SESSION_ID_UNSET;
} }
break; break;
default: default:
......
...@@ -145,7 +145,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements ...@@ -145,7 +145,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
this.drmSessionManager = drmSessionManager; this.drmSessionManager = drmSessionManager;
formatHolder = new FormatHolder(); formatHolder = new FormatHolder();
this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys; this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys;
audioSessionId = AudioTrack.SESSION_ID_NOT_SET; audioSessionId = C.AUDIO_SESSION_ID_UNSET;
decoderReinitializationState = REINITIALIZATION_STATE_NONE; decoderReinitializationState = REINITIALIZATION_STATE_NONE;
audioTrackNeedsConfigure = true; audioTrackNeedsConfigure = true;
} }
...@@ -245,8 +245,8 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements ...@@ -245,8 +245,8 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
} }
if (!audioTrack.isInitialized()) { if (!audioTrack.isInitialized()) {
if (audioSessionId == AudioTrack.SESSION_ID_NOT_SET) { if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) {
audioSessionId = audioTrack.initialize(AudioTrack.SESSION_ID_NOT_SET); audioSessionId = audioTrack.initialize(C.AUDIO_SESSION_ID_UNSET);
eventDispatcher.audioSessionId(audioSessionId); eventDispatcher.audioSessionId(audioSessionId);
onAudioSessionId(audioSessionId); onAudioSessionId(audioSessionId);
} else { } else {
...@@ -425,7 +425,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements ...@@ -425,7 +425,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
@Override @Override
protected void onDisabled() { protected void onDisabled() {
inputFormat = null; inputFormat = null;
audioSessionId = AudioTrack.SESSION_ID_NOT_SET; audioSessionId = C.AUDIO_SESSION_ID_UNSET;
audioTrackNeedsConfigure = true; audioTrackNeedsConfigure = true;
waitingForKeys = false; waitingForKeys = false;
try { try {
...@@ -554,7 +554,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements ...@@ -554,7 +554,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
case C.MSG_SET_STREAM_TYPE: case C.MSG_SET_STREAM_TYPE:
@C.StreamType int streamType = (Integer) message; @C.StreamType int streamType = (Integer) message;
if (audioTrack.setStreamType(streamType)) { if (audioTrack.setStreamType(streamType)) {
audioSessionId = AudioTrack.SESSION_ID_NOT_SET; audioSessionId = C.AUDIO_SESSION_ID_UNSET;
} }
break; break;
default: default:
......
...@@ -244,7 +244,7 @@ import java.io.IOException; ...@@ -244,7 +244,7 @@ import java.io.IOException;
@Override @Override
public long getNextLoadPositionUs() { public long getNextLoadPositionUs() {
return getBufferedPositionUs(); return enabledTrackCount == 0 ? C.TIME_END_OF_SOURCE : getBufferedPositionUs();
} }
@Override @Override
......
...@@ -133,4 +133,32 @@ public interface MediaPeriod extends SequenceableLoader { ...@@ -133,4 +133,32 @@ public interface MediaPeriod extends SequenceableLoader {
*/ */
long seekToUs(long positionUs); long seekToUs(long positionUs);
// SequenceableLoader interface. Overridden to provide more specific documentation.
/**
* Returns the next load time, or {@link C#TIME_END_OF_SOURCE} if loading has finished.
* <p>
* This method should only be called after the period has been prepared. It may be called when no
* tracks are selected.
*/
@Override
long getNextLoadPositionUs();
/**
* Attempts to continue loading.
* <p>
* This method may be called both during and after the period has been prepared.
* <p>
* A period may call {@link Callback#onContinueLoadingRequested(SequenceableLoader)} on the
* {@link Callback} passed to {@link #prepare(Callback)} to request that this method be called
* when the period is permitted to continue loading data. A period may do this both during and
* after preparation.
*
* @param positionUs The current playback position.
* @return True if progress was made, meaning that {@link #getNextLoadPositionUs()} will return
* a different value than prior to the call. False otherwise.
*/
@Override
boolean continueLoading(long positionUs);
} }
...@@ -104,15 +104,14 @@ public final class HlsMediaSource implements MediaSource, ...@@ -104,15 +104,14 @@ public final class HlsMediaSource implements MediaSource,
SinglePeriodTimeline timeline; SinglePeriodTimeline timeline;
if (playlistTracker.isLive()) { if (playlistTracker.isLive()) {
// TODO: fix windowPositionInPeriodUs when playlist is empty. // TODO: fix windowPositionInPeriodUs when playlist is empty.
long windowPositionInPeriodUs = playlist.startTimeUs;
List<HlsMediaPlaylist.Segment> segments = playlist.segments; List<HlsMediaPlaylist.Segment> segments = playlist.segments;
long windowDefaultStartPositionUs = segments.isEmpty() ? 0 long windowDefaultStartPositionUs = segments.isEmpty() ? 0
: segments.get(Math.max(0, segments.size() - 3)).relativeStartTimeUs; : segments.get(Math.max(0, segments.size() - 3)).relativeStartTimeUs;
timeline = new SinglePeriodTimeline(C.TIME_UNSET, playlist.durationUs, timeline = new SinglePeriodTimeline(C.TIME_UNSET, playlist.durationUs,
windowPositionInPeriodUs, windowDefaultStartPositionUs, true, !playlist.hasEndTag); playlist.startTimeUs, windowDefaultStartPositionUs, true, !playlist.hasEndTag);
} else /* not live */ { } else /* not live */ {
timeline = new SinglePeriodTimeline(playlist.durationUs, playlist.durationUs, 0, 0, true, timeline = new SinglePeriodTimeline(playlist.startTimeUs + playlist.durationUs,
false); playlist.durationUs, playlist.startTimeUs, 0, true, false);
} }
sourceListener.onSourceInfoRefreshed(timeline, playlist); sourceListener.onSourceInfoRefreshed(timeline, playlist);
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.text; package com.google.android.exoplayer2.text;
import android.graphics.Color;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.text.Layout.Alignment; import android.text.Layout.Alignment;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
...@@ -36,19 +37,23 @@ public class Cue { ...@@ -36,19 +37,23 @@ public class Cue {
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({TYPE_UNSET, ANCHOR_TYPE_START, ANCHOR_TYPE_MIDDLE, ANCHOR_TYPE_END}) @IntDef({TYPE_UNSET, ANCHOR_TYPE_START, ANCHOR_TYPE_MIDDLE, ANCHOR_TYPE_END})
public @interface AnchorType {} public @interface AnchorType {}
/** /**
* An unset anchor or line type value. * An unset anchor or line type value.
*/ */
public static final int TYPE_UNSET = Integer.MIN_VALUE; public static final int TYPE_UNSET = Integer.MIN_VALUE;
/** /**
* Anchors the left (for horizontal positions) or top (for vertical positions) edge of the cue * Anchors the left (for horizontal positions) or top (for vertical positions) edge of the cue
* box. * box.
*/ */
public static final int ANCHOR_TYPE_START = 0; public static final int ANCHOR_TYPE_START = 0;
/** /**
* Anchors the middle of the cue box. * Anchors the middle of the cue box.
*/ */
public static final int ANCHOR_TYPE_MIDDLE = 1; public static final int ANCHOR_TYPE_MIDDLE = 1;
/** /**
* Anchors the right (for horizontal positions) or bottom (for vertical positions) edge of the cue * Anchors the right (for horizontal positions) or bottom (for vertical positions) edge of the cue
* box. * box.
...@@ -61,10 +66,12 @@ public class Cue { ...@@ -61,10 +66,12 @@ public class Cue {
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({TYPE_UNSET, LINE_TYPE_FRACTION, LINE_TYPE_NUMBER}) @IntDef({TYPE_UNSET, LINE_TYPE_FRACTION, LINE_TYPE_NUMBER})
public @interface LineType {} public @interface LineType {}
/** /**
* Value for {@link #lineType} when {@link #line} is a fractional position. * Value for {@link #lineType} when {@link #line} is a fractional position.
*/ */
public static final int LINE_TYPE_FRACTION = 0; public static final int LINE_TYPE_FRACTION = 0;
/** /**
* Value for {@link #lineType} when {@link #line} is a line number. * Value for {@link #lineType} when {@link #line} is a line number.
*/ */
...@@ -74,10 +81,12 @@ public class Cue { ...@@ -74,10 +81,12 @@ public class Cue {
* The cue text. Note the {@link CharSequence} may be decorated with styling spans. * The cue text. Note the {@link CharSequence} may be decorated with styling spans.
*/ */
public final CharSequence text; public final CharSequence text;
/** /**
* The alignment of the cue text within the cue box, or null if the alignment is undefined. * The alignment of the cue text within the cue box, or null if the alignment is undefined.
*/ */
public final Alignment textAlignment; public final Alignment textAlignment;
/** /**
* The position of the {@link #lineAnchor} of the cue box within the viewport in the direction * The position of the {@link #lineAnchor} of the cue box within the viewport in the direction
* orthogonal to the writing direction, or {@link #DIMEN_UNSET}. When set, the interpretation of * orthogonal to the writing direction, or {@link #DIMEN_UNSET}. When set, the interpretation of
...@@ -86,6 +95,7 @@ public class Cue { ...@@ -86,6 +95,7 @@ public class Cue {
* For horizontal text and {@link #lineType} equal to {@link #LINE_TYPE_FRACTION}, this is the * For horizontal text and {@link #lineType} equal to {@link #LINE_TYPE_FRACTION}, this is the
* fractional vertical position relative to the top of the viewport. * fractional vertical position relative to the top of the viewport.
*/ */
public final float line; public final float line;
/** /**
* The type of the {@link #line} value. * The type of the {@link #line} value.
...@@ -112,6 +122,7 @@ public class Cue { ...@@ -112,6 +122,7 @@ public class Cue {
* {@code (line == -2 && lineAnchor == ANCHOR_TYPE_START)} position a cue so that only its first * {@code (line == -2 && lineAnchor == ANCHOR_TYPE_START)} position a cue so that only its first
* line is visible at the bottom of the viewport. * line is visible at the bottom of the viewport.
*/ */
@LineType @LineType
public final int lineType; public final int lineType;
/** /**
...@@ -122,6 +133,7 @@ public class Cue { ...@@ -122,6 +133,7 @@ public class Cue {
* and {@link #ANCHOR_TYPE_END} correspond to the top, middle and bottom of the cue box * and {@link #ANCHOR_TYPE_END} correspond to the top, middle and bottom of the cue box
* respectively. * respectively.
*/ */
@AnchorType @AnchorType
public final int lineAnchor; public final int lineAnchor;
/** /**
...@@ -133,6 +145,7 @@ public class Cue { ...@@ -133,6 +145,7 @@ public class Cue {
* text. * text.
*/ */
public final float position; public final float position;
/** /**
* The cue box anchor positioned by {@link #position}. One of {@link #ANCHOR_TYPE_START}, * The cue box anchor positioned by {@link #position}. One of {@link #ANCHOR_TYPE_START},
* {@link #ANCHOR_TYPE_MIDDLE}, {@link #ANCHOR_TYPE_END} and {@link #TYPE_UNSET}. * {@link #ANCHOR_TYPE_MIDDLE}, {@link #ANCHOR_TYPE_END} and {@link #TYPE_UNSET}.
...@@ -143,6 +156,7 @@ public class Cue { ...@@ -143,6 +156,7 @@ public class Cue {
*/ */
@AnchorType @AnchorType
public final int positionAnchor; public final int positionAnchor;
/** /**
* The size of the cue box in the writing direction specified as a fraction of the viewport size * The size of the cue box in the writing direction specified as a fraction of the viewport size
* in that direction, or {@link #DIMEN_UNSET}. * in that direction, or {@link #DIMEN_UNSET}.
...@@ -150,6 +164,16 @@ public class Cue { ...@@ -150,6 +164,16 @@ public class Cue {
public final float size; public final float size;
/** /**
* Specifies whether or not the {@link #windowColor} property is set.
*/
public final boolean windowColorSet;
/**
* The fill color of the window.
*/
public final int windowColor;
/**
* Constructs a cue whose {@link #textAlignment} is null, whose type parameters are set to * Constructs a cue whose {@link #textAlignment} is null, whose type parameters are set to
* {@link #TYPE_UNSET} and whose dimension parameters are set to {@link #DIMEN_UNSET}. * {@link #TYPE_UNSET} and whose dimension parameters are set to {@link #DIMEN_UNSET}.
* *
...@@ -171,6 +195,25 @@ public class Cue { ...@@ -171,6 +195,25 @@ public class Cue {
*/ */
public Cue(CharSequence text, Alignment textAlignment, float line, @LineType int lineType, public Cue(CharSequence text, Alignment textAlignment, float line, @LineType int lineType,
@AnchorType int lineAnchor, float position, @AnchorType int positionAnchor, float size) { @AnchorType int lineAnchor, float position, @AnchorType int positionAnchor, float size) {
this(text, textAlignment, line, lineType, lineAnchor, position, positionAnchor, size, false,
Color.BLACK);
}
/**
* @param text See {@link #text}.
* @param textAlignment See {@link #textAlignment}.
* @param line See {@link #line}.
* @param lineType See {@link #lineType}.
* @param lineAnchor See {@link #lineAnchor}.
* @param position See {@link #position}.
* @param positionAnchor See {@link #positionAnchor}.
* @param size See {@link #size}.
* @param windowColorSet See {@link #windowColorSet}.
* @param windowColor See {@link #windowColor}.
*/
public Cue(CharSequence text, Alignment textAlignment, float line, @LineType int lineType,
@AnchorType int lineAnchor, float position, @AnchorType int positionAnchor, float size,
boolean windowColorSet, int windowColor) {
this.text = text; this.text = text;
this.textAlignment = textAlignment; this.textAlignment = textAlignment;
this.line = line; this.line = line;
...@@ -179,6 +222,8 @@ public class Cue { ...@@ -179,6 +222,8 @@ public class Cue {
this.position = position; this.position = position;
this.positionAnchor = positionAnchor; this.positionAnchor = positionAnchor;
this.size = size; this.size = size;
this.windowColorSet = windowColorSet;
this.windowColor = windowColor;
} }
} }
...@@ -65,6 +65,13 @@ import java.util.List; ...@@ -65,6 +65,13 @@ import java.util.List;
* <li>Default: {@code true}</li> * <li>Default: {@code true}</li>
* </ul> * </ul>
* </li> * </li>
* <li><b>{@code default_artwork}</b> - Default artwork to use if no artwork available in audio
* streams.
* <ul>
* <li>Corresponding method: {@link #setDefaultArtwork(Bitmap)}</li>
* <li>Default: {@code null}</li>
* </ul>
* </li>
* <li><b>{@code use_controller}</b> - Whether playback controls are displayed. * <li><b>{@code use_controller}</b> - Whether playback controls are displayed.
* <ul> * <ul>
* <li>Corresponding method: {@link #setUseController(boolean)}</li> * <li>Corresponding method: {@link #setUseController(boolean)}</li>
...@@ -179,6 +186,7 @@ public final class SimpleExoPlayerView extends FrameLayout { ...@@ -179,6 +186,7 @@ public final class SimpleExoPlayerView extends FrameLayout {
private SimpleExoPlayer player; private SimpleExoPlayer player;
private boolean useController; private boolean useController;
private boolean useArtwork; private boolean useArtwork;
private Bitmap defaultArtwork;
private int controllerShowTimeoutMs; private int controllerShowTimeoutMs;
public SimpleExoPlayerView(Context context) { public SimpleExoPlayerView(Context context) {
...@@ -194,6 +202,7 @@ public final class SimpleExoPlayerView extends FrameLayout { ...@@ -194,6 +202,7 @@ public final class SimpleExoPlayerView extends FrameLayout {
int playerLayoutId = R.layout.exo_simple_player_view; int playerLayoutId = R.layout.exo_simple_player_view;
boolean useArtwork = true; boolean useArtwork = true;
int defaultArtwork = 0;
boolean useController = true; boolean useController = true;
int surfaceType = SURFACE_TYPE_SURFACE_VIEW; int surfaceType = SURFACE_TYPE_SURFACE_VIEW;
int resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT; int resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT;
...@@ -205,6 +214,8 @@ public final class SimpleExoPlayerView extends FrameLayout { ...@@ -205,6 +214,8 @@ public final class SimpleExoPlayerView extends FrameLayout {
playerLayoutId = a.getResourceId(R.styleable.SimpleExoPlayerView_player_layout_id, playerLayoutId = a.getResourceId(R.styleable.SimpleExoPlayerView_player_layout_id,
playerLayoutId); playerLayoutId);
useArtwork = a.getBoolean(R.styleable.SimpleExoPlayerView_use_artwork, useArtwork); useArtwork = a.getBoolean(R.styleable.SimpleExoPlayerView_use_artwork, useArtwork);
defaultArtwork = a.getResourceId(R.styleable.SimpleExoPlayerView_default_artwork,
defaultArtwork);
useController = a.getBoolean(R.styleable.SimpleExoPlayerView_use_controller, useController); useController = a.getBoolean(R.styleable.SimpleExoPlayerView_use_controller, useController);
surfaceType = a.getInt(R.styleable.SimpleExoPlayerView_surface_type, surfaceType); surfaceType = a.getInt(R.styleable.SimpleExoPlayerView_surface_type, surfaceType);
resizeMode = a.getInt(R.styleable.SimpleExoPlayerView_resize_mode, resizeMode); resizeMode = a.getInt(R.styleable.SimpleExoPlayerView_resize_mode, resizeMode);
...@@ -246,6 +257,9 @@ public final class SimpleExoPlayerView extends FrameLayout { ...@@ -246,6 +257,9 @@ public final class SimpleExoPlayerView extends FrameLayout {
// Artwork view. // Artwork view.
artworkView = (ImageView) findViewById(R.id.exo_artwork); artworkView = (ImageView) findViewById(R.id.exo_artwork);
this.useArtwork = useArtwork && artworkView != null; this.useArtwork = useArtwork && artworkView != null;
if (defaultArtwork != 0) {
this.defaultArtwork = BitmapFactory.decodeResource(context.getResources(), defaultArtwork);
}
// Subtitle view. // Subtitle view.
subtitleView = (SubtitleView) findViewById(R.id.exo_subtitles); subtitleView = (SubtitleView) findViewById(R.id.exo_subtitles);
...@@ -352,6 +366,26 @@ public final class SimpleExoPlayerView extends FrameLayout { ...@@ -352,6 +366,26 @@ public final class SimpleExoPlayerView extends FrameLayout {
} }
/** /**
* Returns the default artwork to display.
*/
public Bitmap getDefaultArtwork() {
return defaultArtwork;
}
/**
* Sets the default artwork to display if {@code useArtwork} is {@code true} and no artwork is
* present in the media.
*
* @param defaultArtwork the default artwork to display.
*/
public void setDefaultArtwork(Bitmap defaultArtwork) {
if (this.defaultArtwork != defaultArtwork) {
this.defaultArtwork = defaultArtwork;
updateForCurrentTrackSelections();
}
}
/**
* Returns whether the playback controls are enabled. * Returns whether the playback controls are enabled.
*/ */
public boolean getUseController() { public boolean getUseController() {
...@@ -569,6 +603,9 @@ public final class SimpleExoPlayerView extends FrameLayout { ...@@ -569,6 +603,9 @@ public final class SimpleExoPlayerView extends FrameLayout {
} }
} }
} }
if (setArtworkFromBitmap(defaultArtwork)) {
return;
}
} }
// Artwork disabled or unavailable. // Artwork disabled or unavailable.
hideArtwork(); hideArtwork();
...@@ -580,18 +617,23 @@ public final class SimpleExoPlayerView extends FrameLayout { ...@@ -580,18 +617,23 @@ public final class SimpleExoPlayerView extends FrameLayout {
if (metadataEntry instanceof ApicFrame) { if (metadataEntry instanceof ApicFrame) {
byte[] bitmapData = ((ApicFrame) metadataEntry).pictureData; byte[] bitmapData = ((ApicFrame) metadataEntry).pictureData;
Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length); Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length);
if (bitmap != null) { return setArtworkFromBitmap(bitmap);
int bitmapWidth = bitmap.getWidth(); }
int bitmapHeight = bitmap.getHeight(); }
if (bitmapWidth > 0 && bitmapHeight > 0) { return false;
if (contentFrame != null) { }
contentFrame.setAspectRatio((float) bitmapWidth / bitmapHeight);
} private boolean setArtworkFromBitmap(Bitmap bitmap) {
artworkView.setImageBitmap(bitmap); if (bitmap != null) {
artworkView.setVisibility(VISIBLE); int bitmapWidth = bitmap.getWidth();
return true; int bitmapHeight = bitmap.getHeight();
} if (bitmapWidth > 0 && bitmapHeight > 0) {
if (contentFrame != null) {
contentFrame.setAspectRatio((float) bitmapWidth / bitmapHeight);
} }
artworkView.setImageBitmap(bitmap);
artworkView.setVisibility(VISIBLE);
return true;
} }
} }
return false; return false;
......
...@@ -146,9 +146,13 @@ import com.google.android.exoplayer2.util.Util; ...@@ -146,9 +146,13 @@ import com.google.android.exoplayer2.util.Util;
// Nothing to draw. // Nothing to draw.
return; return;
} }
int windowColor = cue.windowColorSet ? cue.windowColor : style.windowColor;
if (!applyEmbeddedStyles) { if (!applyEmbeddedStyles) {
// Strip out any embedded styling. // Strip out any embedded styling.
cueText = cueText.toString(); cueText = cueText.toString();
windowColor = style.windowColor;
} }
if (areCharSequencesEqual(this.cueText, cueText) if (areCharSequencesEqual(this.cueText, cueText)
&& Util.areEqual(this.cueTextAlignment, cue.textAlignment) && Util.areEqual(this.cueTextAlignment, cue.textAlignment)
...@@ -161,7 +165,7 @@ import com.google.android.exoplayer2.util.Util; ...@@ -161,7 +165,7 @@ import com.google.android.exoplayer2.util.Util;
&& this.applyEmbeddedStyles == applyEmbeddedStyles && this.applyEmbeddedStyles == applyEmbeddedStyles
&& this.foregroundColor == style.foregroundColor && this.foregroundColor == style.foregroundColor
&& this.backgroundColor == style.backgroundColor && this.backgroundColor == style.backgroundColor
&& this.windowColor == style.windowColor && this.windowColor == windowColor
&& this.edgeType == style.edgeType && this.edgeType == style.edgeType
&& this.edgeColor == style.edgeColor && this.edgeColor == style.edgeColor
&& Util.areEqual(this.textPaint.getTypeface(), style.typeface) && Util.areEqual(this.textPaint.getTypeface(), style.typeface)
...@@ -187,7 +191,7 @@ import com.google.android.exoplayer2.util.Util; ...@@ -187,7 +191,7 @@ import com.google.android.exoplayer2.util.Util;
this.applyEmbeddedStyles = applyEmbeddedStyles; this.applyEmbeddedStyles = applyEmbeddedStyles;
this.foregroundColor = style.foregroundColor; this.foregroundColor = style.foregroundColor;
this.backgroundColor = style.backgroundColor; this.backgroundColor = style.backgroundColor;
this.windowColor = style.windowColor; this.windowColor = windowColor;
this.edgeType = style.edgeType; this.edgeType = style.edgeType;
this.edgeColor = style.edgeColor; this.edgeColor = style.edgeColor;
this.textPaint.setTypeface(style.typeface); this.textPaint.setTypeface(style.typeface);
......
...@@ -403,7 +403,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -403,7 +403,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
bufferPresentationTimeUs, unadjustedFrameReleaseTimeNs); bufferPresentationTimeUs, unadjustedFrameReleaseTimeNs);
earlyUs = (adjustedReleaseTimeNs - systemTimeNs) / 1000; earlyUs = (adjustedReleaseTimeNs - systemTimeNs) / 1000;
if (earlyUs < -30000) { if (shouldDropOutputBuffer(earlyUs, elapsedRealtimeUs)) {
// We're more than 30ms late rendering the frame. // We're more than 30ms late rendering the frame.
dropOutputBuffer(codec, bufferIndex); dropOutputBuffer(codec, bufferIndex);
return true; return true;
...@@ -437,6 +437,17 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -437,6 +437,17 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
return false; return false;
} }
/**
* Returns true if the current frame should be dropped.
*
* @param earlyUs Time indicating how early the frame is. Negative values indicate late frame.
* @param elapsedRealtimeUs Wall clock time.
*/
protected boolean shouldDropOutputBuffer(long earlyUs, long elapsedRealtimeUs) {
// Drop the frame if we're more than 30ms late rendering the frame.
return earlyUs < -30000;
}
private void skipOutputBuffer(MediaCodec codec, int bufferIndex) { private void skipOutputBuffer(MediaCodec codec, int bufferIndex) {
TraceUtil.beginSection("skipVideoBuffer"); TraceUtil.beginSection("skipVideoBuffer");
codec.releaseOutputBuffer(bufferIndex, false); codec.releaseOutputBuffer(bufferIndex, false);
......

1.09 KB | W: | H:

354 Bytes | W: | H:

library/src/main/res/drawable-hdpi/exo_controls_fastforward.png
library/src/main/res/drawable-hdpi/exo_controls_fastforward.png
library/src/main/res/drawable-hdpi/exo_controls_fastforward.png
library/src/main/res/drawable-hdpi/exo_controls_fastforward.png
  • 2-up
  • Swipe
  • Onion skin

1.05 KB | W: | H:

323 Bytes | W: | H:

library/src/main/res/drawable-hdpi/exo_controls_next.png
library/src/main/res/drawable-hdpi/exo_controls_next.png
library/src/main/res/drawable-hdpi/exo_controls_next.png
library/src/main/res/drawable-hdpi/exo_controls_next.png
  • 2-up
  • Swipe
  • Onion skin

599 Bytes | W: | H:

108 Bytes | W: | H:

library/src/main/res/drawable-hdpi/exo_controls_pause.png
library/src/main/res/drawable-hdpi/exo_controls_pause.png
library/src/main/res/drawable-hdpi/exo_controls_pause.png
library/src/main/res/drawable-hdpi/exo_controls_pause.png
  • 2-up
  • Swipe
  • Onion skin

1.14 KB | W: | H:

286 Bytes | W: | H:

library/src/main/res/drawable-hdpi/exo_controls_play.png
library/src/main/res/drawable-hdpi/exo_controls_play.png
library/src/main/res/drawable-hdpi/exo_controls_play.png
library/src/main/res/drawable-hdpi/exo_controls_play.png
  • 2-up
  • Swipe
  • Onion skin

1.04 KB | W: | H:

292 Bytes | W: | H:

library/src/main/res/drawable-hdpi/exo_controls_previous.png
library/src/main/res/drawable-hdpi/exo_controls_previous.png
library/src/main/res/drawable-hdpi/exo_controls_previous.png
library/src/main/res/drawable-hdpi/exo_controls_previous.png
  • 2-up
  • Swipe
  • Onion skin

1.22 KB | W: | H:

347 Bytes | W: | H:

library/src/main/res/drawable-hdpi/exo_controls_rewind.png
library/src/main/res/drawable-hdpi/exo_controls_rewind.png
library/src/main/res/drawable-hdpi/exo_controls_rewind.png
library/src/main/res/drawable-hdpi/exo_controls_rewind.png
  • 2-up
  • Swipe
  • Onion skin

886 Bytes | W: | H:

192 Bytes | W: | H:

library/src/main/res/drawable-ldpi/exo_controls_fastforward.png
library/src/main/res/drawable-ldpi/exo_controls_fastforward.png
library/src/main/res/drawable-ldpi/exo_controls_fastforward.png
library/src/main/res/drawable-ldpi/exo_controls_fastforward.png
  • 2-up
  • Swipe
  • Onion skin

735 Bytes | W: | H:

167 Bytes | W: | H:

library/src/main/res/drawable-ldpi/exo_controls_next.png
library/src/main/res/drawable-ldpi/exo_controls_next.png
library/src/main/res/drawable-ldpi/exo_controls_next.png
library/src/main/res/drawable-ldpi/exo_controls_next.png
  • 2-up
  • Swipe
  • Onion skin

3.17 KB | W: | H:

91 Bytes | W: | H:

library/src/main/res/drawable-ldpi/exo_controls_pause.png
library/src/main/res/drawable-ldpi/exo_controls_pause.png
library/src/main/res/drawable-ldpi/exo_controls_pause.png
library/src/main/res/drawable-ldpi/exo_controls_pause.png
  • 2-up
  • Swipe
  • Onion skin

673 Bytes | W: | H:

182 Bytes | W: | H:

library/src/main/res/drawable-ldpi/exo_controls_play.png
library/src/main/res/drawable-ldpi/exo_controls_play.png
library/src/main/res/drawable-ldpi/exo_controls_play.png
library/src/main/res/drawable-ldpi/exo_controls_play.png
  • 2-up
  • Swipe
  • Onion skin

770 Bytes | W: | H:

187 Bytes | W: | H:

library/src/main/res/drawable-ldpi/exo_controls_previous.png
library/src/main/res/drawable-ldpi/exo_controls_previous.png
library/src/main/res/drawable-ldpi/exo_controls_previous.png
library/src/main/res/drawable-ldpi/exo_controls_previous.png
  • 2-up
  • Swipe
  • Onion skin

906 Bytes | W: | H:

214 Bytes | W: | H:

library/src/main/res/drawable-ldpi/exo_controls_rewind.png
library/src/main/res/drawable-ldpi/exo_controls_rewind.png
library/src/main/res/drawable-ldpi/exo_controls_rewind.png
library/src/main/res/drawable-ldpi/exo_controls_rewind.png
  • 2-up
  • Swipe
  • Onion skin

929 Bytes | W: | H:

255 Bytes | W: | H:

library/src/main/res/drawable-mdpi/exo_controls_fastforward.png
library/src/main/res/drawable-mdpi/exo_controls_fastforward.png
library/src/main/res/drawable-mdpi/exo_controls_fastforward.png
library/src/main/res/drawable-mdpi/exo_controls_fastforward.png
  • 2-up
  • Swipe
  • Onion skin

843 Bytes | W: | H:

276 Bytes | W: | H:

library/src/main/res/drawable-mdpi/exo_controls_next.png
library/src/main/res/drawable-mdpi/exo_controls_next.png
library/src/main/res/drawable-mdpi/exo_controls_next.png
library/src/main/res/drawable-mdpi/exo_controls_next.png
  • 2-up
  • Swipe
  • Onion skin

540 Bytes | W: | H:

153 Bytes | W: | H:

library/src/main/res/drawable-mdpi/exo_controls_pause.png
library/src/main/res/drawable-mdpi/exo_controls_pause.png
library/src/main/res/drawable-mdpi/exo_controls_pause.png
library/src/main/res/drawable-mdpi/exo_controls_pause.png
  • 2-up
  • Swipe
  • Onion skin

897 Bytes | W: | H:

228 Bytes | W: | H:

library/src/main/res/drawable-mdpi/exo_controls_play.png
library/src/main/res/drawable-mdpi/exo_controls_play.png
library/src/main/res/drawable-mdpi/exo_controls_play.png
library/src/main/res/drawable-mdpi/exo_controls_play.png
  • 2-up
  • Swipe
  • Onion skin

837 Bytes | W: | H:

227 Bytes | W: | H:

library/src/main/res/drawable-mdpi/exo_controls_previous.png
library/src/main/res/drawable-mdpi/exo_controls_previous.png
library/src/main/res/drawable-mdpi/exo_controls_previous.png
library/src/main/res/drawable-mdpi/exo_controls_previous.png
  • 2-up
  • Swipe
  • Onion skin

997 Bytes | W: | H:

273 Bytes | W: | H:

library/src/main/res/drawable-mdpi/exo_controls_rewind.png
library/src/main/res/drawable-mdpi/exo_controls_rewind.png
library/src/main/res/drawable-mdpi/exo_controls_rewind.png
library/src/main/res/drawable-mdpi/exo_controls_rewind.png
  • 2-up
  • Swipe
  • Onion skin
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path android:fillColor="#FFF" android:pathData="M4,18l8.5,-6L4,6v12zM13,6v12l8.5,-6L13,6z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path android:fillColor="#FFF" android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path android:fillColor="#FFF" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path android:fillColor="#FFF" android:pathData="M8,5v14l11,-7z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path android:fillColor="#FFF" android:pathData="M6,6h2v12L6,18zM9.5,12l8.5,6L18,6z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path android:fillColor="#FFF" android:pathData="M11,18L11,6l-8.5,6 8.5,6zM11.5,12l8.5,6L20,6l-8.5,6z"/>
</vector>

1.44 KB | W: | H:

392 Bytes | W: | H:

library/src/main/res/drawable-xhdpi/exo_controls_fastforward.png
library/src/main/res/drawable-xhdpi/exo_controls_fastforward.png
library/src/main/res/drawable-xhdpi/exo_controls_fastforward.png
library/src/main/res/drawable-xhdpi/exo_controls_fastforward.png
  • 2-up
  • Swipe
  • Onion skin

1.33 KB | W: | H:

334 Bytes | W: | H:

library/src/main/res/drawable-xhdpi/exo_controls_next.png
library/src/main/res/drawable-xhdpi/exo_controls_next.png
library/src/main/res/drawable-xhdpi/exo_controls_next.png
library/src/main/res/drawable-xhdpi/exo_controls_next.png
  • 2-up
  • Swipe
  • Onion skin

685 Bytes | W: | H:

164 Bytes | W: | H:

library/src/main/res/drawable-xhdpi/exo_controls_pause.png
library/src/main/res/drawable-xhdpi/exo_controls_pause.png
library/src/main/res/drawable-xhdpi/exo_controls_pause.png
library/src/main/res/drawable-xhdpi/exo_controls_pause.png
  • 2-up
  • Swipe
  • Onion skin

1.58 KB | W: | H:

343 Bytes | W: | H:

library/src/main/res/drawable-xhdpi/exo_controls_play.png
library/src/main/res/drawable-xhdpi/exo_controls_play.png
library/src/main/res/drawable-xhdpi/exo_controls_play.png
library/src/main/res/drawable-xhdpi/exo_controls_play.png
  • 2-up
  • Swipe
  • Onion skin

1.34 KB | W: | H:

339 Bytes | W: | H:

library/src/main/res/drawable-xhdpi/exo_controls_previous.png
library/src/main/res/drawable-xhdpi/exo_controls_previous.png
library/src/main/res/drawable-xhdpi/exo_controls_previous.png
library/src/main/res/drawable-xhdpi/exo_controls_previous.png
  • 2-up
  • Swipe
  • Onion skin

1.64 KB | W: | H:

400 Bytes | W: | H:

library/src/main/res/drawable-xhdpi/exo_controls_rewind.png
library/src/main/res/drawable-xhdpi/exo_controls_rewind.png
library/src/main/res/drawable-xhdpi/exo_controls_rewind.png
library/src/main/res/drawable-xhdpi/exo_controls_rewind.png
  • 2-up
  • Swipe
  • Onion skin

1.29 KB | W: | H:

391 Bytes | W: | H:

library/src/main/res/drawable-xxhdpi/exo_controls_next.png
library/src/main/res/drawable-xxhdpi/exo_controls_next.png
library/src/main/res/drawable-xxhdpi/exo_controls_next.png
library/src/main/res/drawable-xxhdpi/exo_controls_next.png
  • 2-up
  • Swipe
  • Onion skin

611 Bytes | W: | H:

113 Bytes | W: | H:

library/src/main/res/drawable-xxhdpi/exo_controls_pause.png
library/src/main/res/drawable-xxhdpi/exo_controls_pause.png
library/src/main/res/drawable-xxhdpi/exo_controls_pause.png
library/src/main/res/drawable-xxhdpi/exo_controls_pause.png
  • 2-up
  • Swipe
  • Onion skin

1.16 KB | W: | H:

384 Bytes | W: | H:

library/src/main/res/drawable-xxhdpi/exo_controls_play.png
library/src/main/res/drawable-xxhdpi/exo_controls_play.png
library/src/main/res/drawable-xxhdpi/exo_controls_play.png
library/src/main/res/drawable-xxhdpi/exo_controls_play.png
  • 2-up
  • Swipe
  • Onion skin

1.26 KB | W: | H:

464 Bytes | W: | H:

library/src/main/res/drawable-xxhdpi/exo_controls_previous.png
library/src/main/res/drawable-xxhdpi/exo_controls_previous.png
library/src/main/res/drawable-xxhdpi/exo_controls_previous.png
library/src/main/res/drawable-xxhdpi/exo_controls_previous.png
  • 2-up
  • Swipe
  • Onion skin

1.17 KB | W: | H:

571 Bytes | W: | H:

library/src/main/res/drawable-xxhdpi/exo_controls_rewind.png
library/src/main/res/drawable-xxhdpi/exo_controls_rewind.png
library/src/main/res/drawable-xxhdpi/exo_controls_rewind.png
library/src/main/res/drawable-xxhdpi/exo_controls_rewind.png
  • 2-up
  • Swipe
  • Onion skin
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
<declare-styleable name="SimpleExoPlayerView"> <declare-styleable name="SimpleExoPlayerView">
<attr name="use_artwork" format="boolean"/> <attr name="use_artwork" format="boolean"/>
<attr name="default_artwork" format="reference"/>
<attr name="use_controller" format="boolean"/> <attr name="use_controller" format="boolean"/>
<attr name="surface_type"/> <attr name="surface_type"/>
<attr name="show_timeout"/> <attr name="show_timeout"/>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment