mirror of
https://github.com/androidx/media.git
synced 2025-05-04 14:10:40 +08:00
commit
2bebd526e1
@ -1,11 +1,29 @@
|
|||||||
# Release notes #
|
# Release notes #
|
||||||
|
|
||||||
|
### r2.4.3 ###
|
||||||
|
|
||||||
|
* Audio: Workaround custom audio decoders misreporting their maximum supported
|
||||||
|
channel counts ([#2940](https://github.com/google/ExoPlayer/issues/2940)).
|
||||||
|
* Audio: Workaround for broken MediaTek raw decoder on some devices
|
||||||
|
([#2873](https://github.com/google/ExoPlayer/issues/2873)).
|
||||||
|
* Captions: Fix TTML captions appearing at the top of the screen
|
||||||
|
([#2953](https://github.com/google/ExoPlayer/issues/2953)).
|
||||||
|
* Captions: Fix handling of some DVB subtitles
|
||||||
|
([#2957](https://github.com/google/ExoPlayer/issues/2957)).
|
||||||
|
* Track selection: Fix setSelectionOverride(index, tracks, null)
|
||||||
|
([#2988](https://github.com/google/ExoPlayer/issues/2988)).
|
||||||
|
* GVR extension: Add support for mono input
|
||||||
|
([#2710](https://github.com/google/ExoPlayer/issues/2710)).
|
||||||
|
* FLAC extension: Fix failing build
|
||||||
|
([#2977](https://github.com/google/ExoPlayer/pull/2977)).
|
||||||
|
* Misc bugfixes.
|
||||||
|
|
||||||
### r2.4.2 ###
|
### r2.4.2 ###
|
||||||
|
|
||||||
* Stability: Work around Nexus 10 reboot when playing certain content
|
* Stability: Work around Nexus 10 reboot when playing certain content
|
||||||
([2806](https://github.com/google/ExoPlayer/issues/2806)).
|
([#2806](https://github.com/google/ExoPlayer/issues/2806)).
|
||||||
* MP3: Correctly treat MP3s with INFO headers as constant bitrate
|
* MP3: Correctly treat MP3s with INFO headers as constant bitrate
|
||||||
([2895](https://github.com/google/ExoPlayer/issues/2895)).
|
([#2895](https://github.com/google/ExoPlayer/issues/2895)).
|
||||||
* HLS: Use average rather than peak bandwidth when available
|
* HLS: Use average rather than peak bandwidth when available
|
||||||
([#2863](https://github.com/google/ExoPlayer/issues/2863)).
|
([#2863](https://github.com/google/ExoPlayer/issues/2863)).
|
||||||
* SmoothStreaming: Fix timeline for live streams
|
* SmoothStreaming: Fix timeline for live streams
|
||||||
|
@ -48,7 +48,7 @@ allprojects {
|
|||||||
releaseRepoName = getBintrayRepo()
|
releaseRepoName = getBintrayRepo()
|
||||||
releaseUserOrg = 'google'
|
releaseUserOrg = 'google'
|
||||||
releaseGroupId = 'com.google.android.exoplayer'
|
releaseGroupId = 'com.google.android.exoplayer'
|
||||||
releaseVersion = 'r2.4.2'
|
releaseVersion = 'r2.4.3'
|
||||||
releaseWebsite = 'https://github.com/google/ExoPlayer'
|
releaseWebsite = 'https://github.com/google/ExoPlayer'
|
||||||
}
|
}
|
||||||
if (it.hasProperty('externalBuildDir')) {
|
if (it.hasProperty('externalBuildDir')) {
|
||||||
|
@ -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="2402"
|
android:versionCode="2403"
|
||||||
android:versionName="2.4.2">
|
android:versionName="2.4.3">
|
||||||
|
|
||||||
<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"/>
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#define LOG_TAG "FLACParser"
|
#define LOG_TAG "FLACParser"
|
||||||
#define ALOGE(...) \
|
#define ALOGE(...) \
|
||||||
|
@ -25,7 +25,7 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':library-core')
|
compile project(':library-core')
|
||||||
compile 'com.google.vr:sdk-audio:1.30.0'
|
compile 'com.google.vr:sdk-audio:1.60.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
|
@ -82,6 +82,9 @@ public final class GvrAudioProcessor implements AudioProcessor {
|
|||||||
maybeReleaseGvrAudioSurround();
|
maybeReleaseGvrAudioSurround();
|
||||||
int surroundFormat;
|
int surroundFormat;
|
||||||
switch (channelCount) {
|
switch (channelCount) {
|
||||||
|
case 1:
|
||||||
|
surroundFormat = GvrAudioSurround.SurroundFormat.SURROUND_MONO;
|
||||||
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
surroundFormat = GvrAudioSurround.SurroundFormat.SURROUND_STEREO;
|
surroundFormat = GvrAudioSurround.SurroundFormat.SURROUND_STEREO;
|
||||||
break;
|
break;
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
package com.google.android.exoplayer2.ext.okhttp;
|
package com.google.android.exoplayer2.ext.okhttp;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.upstream.DataSourceException;
|
import com.google.android.exoplayer2.upstream.DataSourceException;
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
@ -45,13 +47,14 @@ public class OkHttpDataSource implements HttpDataSource {
|
|||||||
|
|
||||||
private static final AtomicReference<byte[]> skipBufferReference = new AtomicReference<>();
|
private static final AtomicReference<byte[]> skipBufferReference = new AtomicReference<>();
|
||||||
|
|
||||||
private final Call.Factory callFactory;
|
@NonNull private final Call.Factory callFactory;
|
||||||
private final String userAgent;
|
@NonNull private final RequestProperties requestProperties;
|
||||||
private final Predicate<String> contentTypePredicate;
|
|
||||||
private final TransferListener<? super OkHttpDataSource> listener;
|
@Nullable private final String userAgent;
|
||||||
private final CacheControl cacheControl;
|
@Nullable private final Predicate<String> contentTypePredicate;
|
||||||
private final RequestProperties defaultRequestProperties;
|
@Nullable private final TransferListener<? super OkHttpDataSource> listener;
|
||||||
private final RequestProperties requestProperties;
|
@Nullable private final CacheControl cacheControl;
|
||||||
|
@Nullable private final RequestProperties defaultRequestProperties;
|
||||||
|
|
||||||
private DataSpec dataSpec;
|
private DataSpec dataSpec;
|
||||||
private Response response;
|
private Response response;
|
||||||
@ -67,33 +70,34 @@ public class OkHttpDataSource implements HttpDataSource {
|
|||||||
/**
|
/**
|
||||||
* @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use
|
* @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use
|
||||||
* by the source.
|
* by the source.
|
||||||
* @param userAgent The User-Agent string that should be used.
|
* @param userAgent An optional User-Agent string.
|
||||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||||
* predicate then a InvalidContentTypeException} is thrown from {@link #open(DataSpec)}.
|
* predicate then a InvalidContentTypeException} is thrown from {@link #open(DataSpec)}.
|
||||||
*/
|
*/
|
||||||
public OkHttpDataSource(Call.Factory callFactory, String userAgent,
|
public OkHttpDataSource(@NonNull Call.Factory callFactory, @Nullable String userAgent,
|
||||||
Predicate<String> contentTypePredicate) {
|
@Nullable Predicate<String> contentTypePredicate) {
|
||||||
this(callFactory, userAgent, contentTypePredicate, null);
|
this(callFactory, userAgent, contentTypePredicate, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use
|
* @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use
|
||||||
* by the source.
|
* by the source.
|
||||||
* @param userAgent The User-Agent string that should be used.
|
* @param userAgent An optional User-Agent string.
|
||||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||||
* predicate then a {@link InvalidContentTypeException} is thrown from
|
* predicate then a {@link InvalidContentTypeException} is thrown from
|
||||||
* {@link #open(DataSpec)}.
|
* {@link #open(DataSpec)}.
|
||||||
* @param listener An optional listener.
|
* @param listener An optional listener.
|
||||||
*/
|
*/
|
||||||
public OkHttpDataSource(Call.Factory callFactory, String userAgent,
|
public OkHttpDataSource(@NonNull Call.Factory callFactory, @Nullable String userAgent,
|
||||||
Predicate<String> contentTypePredicate, TransferListener<? super OkHttpDataSource> listener) {
|
@Nullable Predicate<String> contentTypePredicate,
|
||||||
|
@Nullable TransferListener<? super OkHttpDataSource> listener) {
|
||||||
this(callFactory, userAgent, contentTypePredicate, listener, null, null);
|
this(callFactory, userAgent, contentTypePredicate, listener, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use
|
* @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use
|
||||||
* by the source.
|
* by the source.
|
||||||
* @param userAgent The User-Agent string that should be used.
|
* @param userAgent An optional User-Agent string.
|
||||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||||
* predicate then a {@link InvalidContentTypeException} is thrown from
|
* predicate then a {@link InvalidContentTypeException} is thrown from
|
||||||
* {@link #open(DataSpec)}.
|
* {@link #open(DataSpec)}.
|
||||||
@ -102,11 +106,12 @@ public class OkHttpDataSource implements HttpDataSource {
|
|||||||
* @param defaultRequestProperties The optional default {@link RequestProperties} to be sent to
|
* @param defaultRequestProperties The optional default {@link RequestProperties} to be sent to
|
||||||
* the server as HTTP headers on every request.
|
* the server as HTTP headers on every request.
|
||||||
*/
|
*/
|
||||||
public OkHttpDataSource(Call.Factory callFactory, String userAgent,
|
public OkHttpDataSource(@NonNull Call.Factory callFactory, @Nullable String userAgent,
|
||||||
Predicate<String> contentTypePredicate, TransferListener<? super OkHttpDataSource> listener,
|
@Nullable Predicate<String> contentTypePredicate,
|
||||||
CacheControl cacheControl, RequestProperties defaultRequestProperties) {
|
@Nullable TransferListener<? super OkHttpDataSource> listener,
|
||||||
|
@Nullable CacheControl cacheControl, @Nullable RequestProperties defaultRequestProperties) {
|
||||||
this.callFactory = Assertions.checkNotNull(callFactory);
|
this.callFactory = Assertions.checkNotNull(callFactory);
|
||||||
this.userAgent = Assertions.checkNotEmpty(userAgent);
|
this.userAgent = userAgent;
|
||||||
this.contentTypePredicate = contentTypePredicate;
|
this.contentTypePredicate = contentTypePredicate;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
this.cacheControl = cacheControl;
|
this.cacheControl = cacheControl;
|
||||||
@ -280,7 +285,10 @@ public class OkHttpDataSource implements HttpDataSource {
|
|||||||
}
|
}
|
||||||
builder.addHeader("Range", rangeRequest);
|
builder.addHeader("Range", rangeRequest);
|
||||||
}
|
}
|
||||||
builder.addHeader("User-Agent", userAgent);
|
if (userAgent != null) {
|
||||||
|
builder.addHeader("User-Agent", userAgent);
|
||||||
|
}
|
||||||
|
|
||||||
if (!allowGzip) {
|
if (!allowGzip) {
|
||||||
builder.addHeader("Accept-Encoding", "identity");
|
builder.addHeader("Accept-Encoding", "identity");
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.ext.okhttp;
|
package com.google.android.exoplayer2.ext.okhttp;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||||
import com.google.android.exoplayer2.upstream.HttpDataSource.BaseFactory;
|
import com.google.android.exoplayer2.upstream.HttpDataSource.BaseFactory;
|
||||||
@ -28,31 +30,32 @@ import okhttp3.Call;
|
|||||||
*/
|
*/
|
||||||
public final class OkHttpDataSourceFactory extends BaseFactory {
|
public final class OkHttpDataSourceFactory extends BaseFactory {
|
||||||
|
|
||||||
private final Call.Factory callFactory;
|
@NonNull private final Call.Factory callFactory;
|
||||||
private final String userAgent;
|
@Nullable private final String userAgent;
|
||||||
private final TransferListener<? super DataSource> listener;
|
@Nullable private final TransferListener<? super DataSource> listener;
|
||||||
private final CacheControl cacheControl;
|
@Nullable private final CacheControl cacheControl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use
|
* @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use
|
||||||
* by the sources created by the factory.
|
* by the sources created by the factory.
|
||||||
* @param userAgent The User-Agent string that should be used.
|
* @param userAgent An optional User-Agent string.
|
||||||
* @param listener An optional listener.
|
* @param listener An optional listener.
|
||||||
*/
|
*/
|
||||||
public OkHttpDataSourceFactory(Call.Factory callFactory, String userAgent,
|
public OkHttpDataSourceFactory(@NonNull Call.Factory callFactory, @Nullable String userAgent,
|
||||||
TransferListener<? super DataSource> listener) {
|
@Nullable TransferListener<? super DataSource> listener) {
|
||||||
this(callFactory, userAgent, listener, null);
|
this(callFactory, userAgent, listener, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use
|
* @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use
|
||||||
* by the sources created by the factory.
|
* by the sources created by the factory.
|
||||||
* @param userAgent The User-Agent string that should be used.
|
* @param userAgent An optional User-Agent string.
|
||||||
* @param listener An optional listener.
|
* @param listener An optional listener.
|
||||||
* @param cacheControl An optional {@link CacheControl} for setting the Cache-Control header.
|
* @param cacheControl An optional {@link CacheControl} for setting the Cache-Control header.
|
||||||
*/
|
*/
|
||||||
public OkHttpDataSourceFactory(Call.Factory callFactory, String userAgent,
|
public OkHttpDataSourceFactory(@NonNull Call.Factory callFactory, @Nullable String userAgent,
|
||||||
TransferListener<? super DataSource> listener, CacheControl cacheControl) {
|
@Nullable TransferListener<? super DataSource> listener,
|
||||||
|
@Nullable CacheControl cacheControl) {
|
||||||
this.callFactory = callFactory;
|
this.callFactory = callFactory;
|
||||||
this.userAgent = userAgent;
|
this.userAgent = userAgent;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
|
@ -480,11 +480,11 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||||||
protected void onStarted() {
|
protected void onStarted() {
|
||||||
droppedFrames = 0;
|
droppedFrames = 0;
|
||||||
droppedFrameAccumulationStartTimeMs = SystemClock.elapsedRealtime();
|
droppedFrameAccumulationStartTimeMs = SystemClock.elapsedRealtime();
|
||||||
joiningDeadlineMs = C.TIME_UNSET;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStopped() {
|
protected void onStopped() {
|
||||||
|
joiningDeadlineMs = C.TIME_UNSET;
|
||||||
maybeNotifyDroppedFrames();
|
maybeNotifyDroppedFrames();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,9 @@
|
|||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
tools:ignore="MissingApplicationIcon,HardcodedDebugMode">
|
tools:ignore="MissingApplicationIcon,HardcodedDebugMode">
|
||||||
<uses-library android:name="android.test.runner"/>
|
<uses-library android:name="android.test.runner"/>
|
||||||
|
<provider
|
||||||
|
android:authorities="com.google.android.exoplayer2.core.test"
|
||||||
|
android:name="com.google.android.exoplayer2.upstream.ContentDataSourceTest$TestContentProvider"/>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
<instrumentation
|
<instrumentation
|
||||||
|
Binary file not shown.
@ -8,7 +8,7 @@ track 0:
|
|||||||
bitrate = -1
|
bitrate = -1
|
||||||
id = null
|
id = null
|
||||||
containerMimeType = null
|
containerMimeType = null
|
||||||
sampleMimeType = audio/x-flac
|
sampleMimeType = audio/flac
|
||||||
maxInputSize = 768000
|
maxInputSize = 768000
|
||||||
width = -1
|
width = -1
|
||||||
height = -1
|
height = -1
|
||||||
|
@ -8,7 +8,7 @@ track 0:
|
|||||||
bitrate = -1
|
bitrate = -1
|
||||||
id = null
|
id = null
|
||||||
containerMimeType = null
|
containerMimeType = null
|
||||||
sampleMimeType = audio/x-flac
|
sampleMimeType = audio/flac
|
||||||
maxInputSize = 768000
|
maxInputSize = 768000
|
||||||
width = -1
|
width = -1
|
||||||
height = -1
|
height = -1
|
||||||
|
@ -8,7 +8,7 @@ track 0:
|
|||||||
bitrate = -1
|
bitrate = -1
|
||||||
id = null
|
id = null
|
||||||
containerMimeType = null
|
containerMimeType = null
|
||||||
sampleMimeType = audio/x-flac
|
sampleMimeType = audio/flac
|
||||||
maxInputSize = 768000
|
maxInputSize = 768000
|
||||||
width = -1
|
width = -1
|
||||||
height = -1
|
height = -1
|
||||||
|
@ -8,7 +8,7 @@ track 0:
|
|||||||
bitrate = -1
|
bitrate = -1
|
||||||
id = null
|
id = null
|
||||||
containerMimeType = null
|
containerMimeType = null
|
||||||
sampleMimeType = audio/x-flac
|
sampleMimeType = audio/flac
|
||||||
maxInputSize = 768000
|
maxInputSize = 768000
|
||||||
width = -1
|
width = -1
|
||||||
height = -1
|
height = -1
|
||||||
|
@ -8,7 +8,7 @@ track 0:
|
|||||||
bitrate = -1
|
bitrate = -1
|
||||||
id = null
|
id = null
|
||||||
containerMimeType = null
|
containerMimeType = null
|
||||||
sampleMimeType = audio/x-flac
|
sampleMimeType = audio/flac
|
||||||
maxInputSize = 768000
|
maxInputSize = 768000
|
||||||
width = -1
|
width = -1
|
||||||
height = -1
|
height = -1
|
||||||
|
@ -8,7 +8,7 @@ track 0:
|
|||||||
bitrate = -1
|
bitrate = -1
|
||||||
id = null
|
id = null
|
||||||
containerMimeType = null
|
containerMimeType = null
|
||||||
sampleMimeType = audio/x-flac
|
sampleMimeType = audio/flac
|
||||||
maxInputSize = 768000
|
maxInputSize = 768000
|
||||||
width = -1
|
width = -1
|
||||||
height = -1
|
height = -1
|
||||||
|
@ -8,7 +8,7 @@ track 0:
|
|||||||
bitrate = -1
|
bitrate = -1
|
||||||
id = null
|
id = null
|
||||||
containerMimeType = null
|
containerMimeType = null
|
||||||
sampleMimeType = audio/x-flac
|
sampleMimeType = audio/flac
|
||||||
maxInputSize = 768000
|
maxInputSize = 768000
|
||||||
width = -1
|
width = -1
|
||||||
height = -1
|
height = -1
|
||||||
|
@ -8,7 +8,7 @@ track 0:
|
|||||||
bitrate = -1
|
bitrate = -1
|
||||||
id = null
|
id = null
|
||||||
containerMimeType = null
|
containerMimeType = null
|
||||||
sampleMimeType = audio/x-flac
|
sampleMimeType = audio/flac
|
||||||
maxInputSize = 768000
|
maxInputSize = 768000
|
||||||
width = -1
|
width = -1
|
||||||
height = -1
|
height = -1
|
||||||
|
@ -8,7 +8,7 @@ track 0:
|
|||||||
bitrate = -1
|
bitrate = -1
|
||||||
id = null
|
id = null
|
||||||
containerMimeType = null
|
containerMimeType = null
|
||||||
sampleMimeType = audio/x-flac
|
sampleMimeType = audio/flac
|
||||||
maxInputSize = 768000
|
maxInputSize = 768000
|
||||||
width = -1
|
width = -1
|
||||||
height = -1
|
height = -1
|
||||||
|
@ -8,7 +8,7 @@ track 0:
|
|||||||
bitrate = -1
|
bitrate = -1
|
||||||
id = null
|
id = null
|
||||||
containerMimeType = null
|
containerMimeType = null
|
||||||
sampleMimeType = audio/x-flac
|
sampleMimeType = audio/flac
|
||||||
maxInputSize = 768000
|
maxInputSize = 768000
|
||||||
width = -1
|
width = -1
|
||||||
height = -1
|
height = -1
|
||||||
|
@ -179,9 +179,13 @@ public final class TtmlDecoderTest extends InstrumentationTestCase {
|
|||||||
assertEquals(1, output.size());
|
assertEquals(1, output.size());
|
||||||
ttmlCue = output.get(0);
|
ttmlCue = output.get(0);
|
||||||
assertEquals("dolor", ttmlCue.text.toString());
|
assertEquals("dolor", ttmlCue.text.toString());
|
||||||
assertEquals(10f / 100f, ttmlCue.position);
|
assertEquals(Cue.DIMEN_UNSET, ttmlCue.position);
|
||||||
assertEquals(80f / 100f, ttmlCue.line);
|
assertEquals(Cue.DIMEN_UNSET, ttmlCue.line);
|
||||||
assertEquals(1f, ttmlCue.size);
|
assertEquals(Cue.DIMEN_UNSET, ttmlCue.size);
|
||||||
|
// TODO: Should be as below, once https://github.com/google/ExoPlayer/issues/2953 is fixed.
|
||||||
|
// assertEquals(10f / 100f, ttmlCue.position);
|
||||||
|
// assertEquals(80f / 100f, ttmlCue.line);
|
||||||
|
// assertEquals(1f, ttmlCue.size);
|
||||||
|
|
||||||
output = subtitle.getCues(21000000);
|
output = subtitle.getCues(21000000);
|
||||||
assertEquals(1, output.size());
|
assertEquals(1, output.size());
|
||||||
|
@ -0,0 +1,196 @@
|
|||||||
|
/*
|
||||||
|
* 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.trackselection;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||||
|
import com.google.android.exoplayer2.Format;
|
||||||
|
import com.google.android.exoplayer2.RendererCapabilities;
|
||||||
|
import com.google.android.exoplayer2.source.TrackGroup;
|
||||||
|
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link MappingTrackSelector}.
|
||||||
|
*/
|
||||||
|
public final class MappingTrackSelectorTest extends TestCase {
|
||||||
|
|
||||||
|
private static final RendererCapabilities VIDEO_CAPABILITIES =
|
||||||
|
new FakeRendererCapabilities(C.TRACK_TYPE_VIDEO);
|
||||||
|
private static final RendererCapabilities AUDIO_CAPABILITIES =
|
||||||
|
new FakeRendererCapabilities(C.TRACK_TYPE_AUDIO);
|
||||||
|
private static final RendererCapabilities[] RENDERER_CAPABILITIES = new RendererCapabilities[] {
|
||||||
|
VIDEO_CAPABILITIES, AUDIO_CAPABILITIES
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final TrackGroup VIDEO_TRACK_GROUP = new TrackGroup(
|
||||||
|
Format.createVideoSampleFormat("video", MimeTypes.VIDEO_H264, null, Format.NO_VALUE,
|
||||||
|
Format.NO_VALUE, 1024, 768, Format.NO_VALUE, null, null));
|
||||||
|
private static final TrackGroup AUDIO_TRACK_GROUP = new TrackGroup(
|
||||||
|
Format.createAudioSampleFormat("audio", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
|
||||||
|
Format.NO_VALUE, 2, 44100, null, null, 0, null));
|
||||||
|
private static final TrackGroupArray TRACK_GROUPS = new TrackGroupArray(
|
||||||
|
VIDEO_TRACK_GROUP, AUDIO_TRACK_GROUP);
|
||||||
|
|
||||||
|
private static final TrackSelection[] TRACK_SELECTIONS = new TrackSelection[] {
|
||||||
|
new FixedTrackSelection(VIDEO_TRACK_GROUP, 0),
|
||||||
|
new FixedTrackSelection(AUDIO_TRACK_GROUP, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that the video and audio track groups are mapped onto the correct renderers.
|
||||||
|
*/
|
||||||
|
public void testMapping() throws ExoPlaybackException {
|
||||||
|
FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector();
|
||||||
|
trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS);
|
||||||
|
trackSelector.assertMappedTrackGroups(0, VIDEO_TRACK_GROUP);
|
||||||
|
trackSelector.assertMappedTrackGroups(1, AUDIO_TRACK_GROUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that the video and audio track groups are mapped onto the correct renderers when the
|
||||||
|
* renderer ordering is reversed.
|
||||||
|
*/
|
||||||
|
public void testMappingReverseOrder() throws ExoPlaybackException {
|
||||||
|
FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector();
|
||||||
|
RendererCapabilities[] reverseOrderRendererCapabilities = new RendererCapabilities[] {
|
||||||
|
AUDIO_CAPABILITIES, VIDEO_CAPABILITIES};
|
||||||
|
trackSelector.selectTracks(reverseOrderRendererCapabilities, TRACK_GROUPS);
|
||||||
|
trackSelector.assertMappedTrackGroups(0, AUDIO_TRACK_GROUP);
|
||||||
|
trackSelector.assertMappedTrackGroups(1, VIDEO_TRACK_GROUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests video and audio track groups are mapped onto the correct renderers when there are
|
||||||
|
* multiple track groups of the same type.
|
||||||
|
*/
|
||||||
|
public void testMappingMulti() throws ExoPlaybackException {
|
||||||
|
FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector();
|
||||||
|
TrackGroupArray multiTrackGroups = new TrackGroupArray(VIDEO_TRACK_GROUP, AUDIO_TRACK_GROUP,
|
||||||
|
VIDEO_TRACK_GROUP);
|
||||||
|
trackSelector.selectTracks(RENDERER_CAPABILITIES, multiTrackGroups);
|
||||||
|
trackSelector.assertMappedTrackGroups(0, VIDEO_TRACK_GROUP, VIDEO_TRACK_GROUP);
|
||||||
|
trackSelector.assertMappedTrackGroups(1, AUDIO_TRACK_GROUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the result of {@link MappingTrackSelector#selectTracks(RendererCapabilities[],
|
||||||
|
* TrackGroupArray[], int[][][])} is propagated correctly to the result of
|
||||||
|
* {@link MappingTrackSelector#selectTracks(RendererCapabilities[], TrackGroupArray)}.
|
||||||
|
*/
|
||||||
|
public void testSelectTracks() throws ExoPlaybackException {
|
||||||
|
FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector(TRACK_SELECTIONS);
|
||||||
|
TrackSelectorResult result = trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS);
|
||||||
|
assertEquals(TRACK_SELECTIONS[0], result.selections.get(0));
|
||||||
|
assertEquals(TRACK_SELECTIONS[1], result.selections.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that a null override clears a track selection.
|
||||||
|
*/
|
||||||
|
public void testSelectTracksWithNullOverride() throws ExoPlaybackException {
|
||||||
|
FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector(TRACK_SELECTIONS);
|
||||||
|
trackSelector.setSelectionOverride(0, new TrackGroupArray(VIDEO_TRACK_GROUP), null);
|
||||||
|
TrackSelectorResult result = trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS);
|
||||||
|
assertNull(result.selections.get(0));
|
||||||
|
assertEquals(TRACK_SELECTIONS[1], result.selections.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that a null override can be cleared.
|
||||||
|
*/
|
||||||
|
public void testSelectTracksWithClearedNullOverride() throws ExoPlaybackException {
|
||||||
|
FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector(TRACK_SELECTIONS);
|
||||||
|
trackSelector.setSelectionOverride(0, new TrackGroupArray(VIDEO_TRACK_GROUP), null);
|
||||||
|
trackSelector.clearSelectionOverride(0, new TrackGroupArray(VIDEO_TRACK_GROUP));
|
||||||
|
TrackSelectorResult result = trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS);
|
||||||
|
assertEquals(TRACK_SELECTIONS[0], result.selections.get(0));
|
||||||
|
assertEquals(TRACK_SELECTIONS[1], result.selections.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that an override is not applied for a different set of available track groups.
|
||||||
|
*/
|
||||||
|
public void testSelectTracksWithNullOverrideForDifferentTracks() throws ExoPlaybackException {
|
||||||
|
FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector(TRACK_SELECTIONS);
|
||||||
|
trackSelector.setSelectionOverride(0, new TrackGroupArray(VIDEO_TRACK_GROUP), null);
|
||||||
|
TrackSelectorResult result = trackSelector.selectTracks(RENDERER_CAPABILITIES,
|
||||||
|
new TrackGroupArray(VIDEO_TRACK_GROUP, AUDIO_TRACK_GROUP, VIDEO_TRACK_GROUP));
|
||||||
|
assertEquals(TRACK_SELECTIONS[0], result.selections.get(0));
|
||||||
|
assertEquals(TRACK_SELECTIONS[1], result.selections.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link MappingTrackSelector} that returns a fixed result from
|
||||||
|
* {@link #selectTracks(RendererCapabilities[], TrackGroupArray[], int[][][])}.
|
||||||
|
*/
|
||||||
|
private static final class FakeMappingTrackSelector extends MappingTrackSelector {
|
||||||
|
|
||||||
|
private final TrackSelection[] result;
|
||||||
|
private TrackGroupArray[] lastRendererTrackGroupArrays;
|
||||||
|
|
||||||
|
public FakeMappingTrackSelector(TrackSelection... result) {
|
||||||
|
this.result = result.length == 0 ? null : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TrackSelection[] selectTracks(RendererCapabilities[] rendererCapabilities,
|
||||||
|
TrackGroupArray[] rendererTrackGroupArrays, int[][][] rendererFormatSupports)
|
||||||
|
throws ExoPlaybackException {
|
||||||
|
lastRendererTrackGroupArrays = rendererTrackGroupArrays;
|
||||||
|
return result == null ? new TrackSelection[rendererCapabilities.length] : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertMappedTrackGroups(int rendererIndex, TrackGroup... expected) {
|
||||||
|
assertEquals(expected.length, lastRendererTrackGroupArrays[rendererIndex].length);
|
||||||
|
for (int i = 0; i < expected.length; i++) {
|
||||||
|
assertEquals(expected[i], lastRendererTrackGroupArrays[rendererIndex].get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link RendererCapabilities} that advertises adaptive support for all tracks of a given type.
|
||||||
|
*/
|
||||||
|
private static final class FakeRendererCapabilities implements RendererCapabilities {
|
||||||
|
|
||||||
|
private final int trackType;
|
||||||
|
|
||||||
|
public FakeRendererCapabilities(int trackType) {
|
||||||
|
this.trackType = trackType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTrackType() {
|
||||||
|
return trackType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int supportsFormat(Format format) throws ExoPlaybackException {
|
||||||
|
return MimeTypes.getTrackType(format.sampleMimeType) == trackType
|
||||||
|
? (FORMAT_HANDLED | ADAPTIVE_SEAMLESS) : FORMAT_UNSUPPORTED_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException {
|
||||||
|
return ADAPTIVE_SEAMLESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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.upstream;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.test.InstrumentationTestCase;
|
||||||
|
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link AssetDataSource}.
|
||||||
|
*/
|
||||||
|
public final class AssetDataSourceTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
|
private static final String DATA_PATH = "binary/1024_incrementing_bytes.mp3";
|
||||||
|
|
||||||
|
public void testReadFileUri() throws Exception {
|
||||||
|
AssetDataSource dataSource = new AssetDataSource(getInstrumentation().getContext());
|
||||||
|
DataSpec dataSpec = new DataSpec(Uri.parse("file:///android_asset/" + DATA_PATH));
|
||||||
|
TestUtil.assertDataSourceContent(dataSource, dataSpec,
|
||||||
|
TestUtil.getByteArray(getInstrumentation(), DATA_PATH));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testReadAssetUri() throws Exception {
|
||||||
|
AssetDataSource dataSource = new AssetDataSource(getInstrumentation().getContext());
|
||||||
|
DataSpec dataSpec = new DataSpec(Uri.parse("asset:///" + DATA_PATH));
|
||||||
|
TestUtil.assertDataSourceContent(dataSource, dataSpec,
|
||||||
|
TestUtil.getByteArray(getInstrumentation(), DATA_PATH));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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.upstream;
|
||||||
|
|
||||||
|
import android.content.ContentProvider;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.res.AssetFileDescriptor;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.test.InstrumentationTestCase;
|
||||||
|
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link ContentDataSource}.
|
||||||
|
*/
|
||||||
|
public final class ContentDataSourceTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
|
private static final String AUTHORITY = "com.google.android.exoplayer2.core.test";
|
||||||
|
private static final String DATA_PATH = "binary/1024_incrementing_bytes.mp3";
|
||||||
|
|
||||||
|
public void testReadValidUri() throws Exception {
|
||||||
|
ContentDataSource dataSource = new ContentDataSource(getInstrumentation().getContext());
|
||||||
|
Uri contentUri = new Uri.Builder()
|
||||||
|
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||||
|
.authority(AUTHORITY)
|
||||||
|
.path(DATA_PATH).build();
|
||||||
|
DataSpec dataSpec = new DataSpec(contentUri);
|
||||||
|
TestUtil.assertDataSourceContent(dataSource, dataSpec,
|
||||||
|
TestUtil.getByteArray(getInstrumentation(), DATA_PATH));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testReadInvalidUri() throws Exception {
|
||||||
|
ContentDataSource dataSource = new ContentDataSource(getInstrumentation().getContext());
|
||||||
|
Uri contentUri = new Uri.Builder()
|
||||||
|
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||||
|
.authority(AUTHORITY)
|
||||||
|
.build();
|
||||||
|
DataSpec dataSpec = new DataSpec(contentUri);
|
||||||
|
try {
|
||||||
|
dataSource.open(dataSpec);
|
||||||
|
fail();
|
||||||
|
} catch (ContentDataSource.ContentDataSourceException e) {
|
||||||
|
// Expected.
|
||||||
|
assertTrue(e.getCause() instanceof FileNotFoundException);
|
||||||
|
} finally {
|
||||||
|
dataSource.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link ContentProvider} for the test.
|
||||||
|
*/
|
||||||
|
public static final class TestContentProvider extends ContentProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreate() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor query(@NonNull Uri uri, String[] projection, String selection,
|
||||||
|
String[] selectionArgs, String sortOrder) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode)
|
||||||
|
throws FileNotFoundException {
|
||||||
|
if (uri.getPath() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return getContext().getAssets().openFd(uri.getPath().replaceFirst("/", ""));
|
||||||
|
} catch (IOException e) {
|
||||||
|
FileNotFoundException exception = new FileNotFoundException(e.getMessage());
|
||||||
|
exception.initCause(e);
|
||||||
|
throw exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType(@NonNull Uri uri) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uri insert(@NonNull Uri uri, ContentValues values) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int delete(@NonNull Uri uri, String selection,
|
||||||
|
String[] selectionArgs) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int update(@NonNull Uri uri, ContentValues values,
|
||||||
|
String selection, String[] selectionArgs) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -24,13 +24,13 @@ public interface ExoPlayerLibraryInfo {
|
|||||||
* The version of the library expressed as a string, for example "1.2.3".
|
* The version of the library expressed as a string, for example "1.2.3".
|
||||||
*/
|
*/
|
||||||
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
|
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
|
||||||
String VERSION = "2.4.2";
|
String VERSION = "2.4.3";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}.
|
* The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}.
|
||||||
*/
|
*/
|
||||||
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
|
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
|
||||||
String VERSION_SLASHY = "ExoPlayerLib/2.4.2";
|
String VERSION_SLASHY = "ExoPlayerLib/2.4.3";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The version of the library expressed as an integer, for example 1002003.
|
* The version of the library expressed as an integer, for example 1002003.
|
||||||
@ -40,7 +40,7 @@ public interface ExoPlayerLibraryInfo {
|
|||||||
* integer version 123045006 (123-045-006).
|
* integer version 123045006 (123-045-006).
|
||||||
*/
|
*/
|
||||||
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
|
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
|
||||||
int VERSION_INT = 2004002;
|
int VERSION_INT = 2004003;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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}
|
||||||
|
@ -1292,7 +1292,7 @@ public final class AudioTrack {
|
|||||||
// The timestamp time base is probably wrong.
|
// The timestamp time base is probably wrong.
|
||||||
String message = "Spurious audio timestamp (system clock mismatch): "
|
String message = "Spurious audio timestamp (system clock mismatch): "
|
||||||
+ audioTimestampFramePosition + ", " + audioTimestampUs + ", " + systemClockUs + ", "
|
+ audioTimestampFramePosition + ", " + audioTimestampUs + ", " + systemClockUs + ", "
|
||||||
+ playbackPositionUs;
|
+ playbackPositionUs + ", " + getSubmittedFrames() + ", " + getWrittenFrames();
|
||||||
if (failOnSpuriousAudioTimestamp) {
|
if (failOnSpuriousAudioTimestamp) {
|
||||||
throw new InvalidAudioTrackTimestampException(message);
|
throw new InvalidAudioTrackTimestampException(message);
|
||||||
}
|
}
|
||||||
@ -1303,7 +1303,7 @@ public final class AudioTrack {
|
|||||||
// The timestamp frame position is probably wrong.
|
// The timestamp frame position is probably wrong.
|
||||||
String message = "Spurious audio timestamp (frame position mismatch): "
|
String message = "Spurious audio timestamp (frame position mismatch): "
|
||||||
+ audioTimestampFramePosition + ", " + audioTimestampUs + ", " + systemClockUs + ", "
|
+ audioTimestampFramePosition + ", " + audioTimestampUs + ", " + systemClockUs + ", "
|
||||||
+ playbackPositionUs;
|
+ playbackPositionUs + ", " + getSubmittedFrames() + ", " + getWrittenFrames();
|
||||||
if (failOnSpuriousAudioTimestamp) {
|
if (failOnSpuriousAudioTimestamp) {
|
||||||
throw new InvalidAudioTrackTimestampException(message);
|
throw new InvalidAudioTrackTimestampException(message);
|
||||||
}
|
}
|
||||||
|
@ -28,11 +28,11 @@ import java.util.Map;
|
|||||||
@TargetApi(16)
|
@TargetApi(16)
|
||||||
public interface DrmSession<T extends ExoMediaCrypto> {
|
public interface DrmSession<T extends ExoMediaCrypto> {
|
||||||
|
|
||||||
/** Wraps the exception which is the cause of the error state. */
|
/** Wraps the throwable which is the cause of the error state. */
|
||||||
class DrmSessionException extends Exception {
|
class DrmSessionException extends Exception {
|
||||||
|
|
||||||
public DrmSessionException(Exception e) {
|
public DrmSessionException(Throwable cause) {
|
||||||
super(e);
|
super(cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ import java.util.Collections;
|
|||||||
hasOutputFormat = true;
|
hasOutputFormat = true;
|
||||||
} else if (audioFormat == AUDIO_FORMAT_ALAW || audioFormat == AUDIO_FORMAT_ULAW) {
|
} else if (audioFormat == AUDIO_FORMAT_ALAW || audioFormat == AUDIO_FORMAT_ULAW) {
|
||||||
String type = audioFormat == AUDIO_FORMAT_ALAW ? MimeTypes.AUDIO_ALAW
|
String type = audioFormat == AUDIO_FORMAT_ALAW ? MimeTypes.AUDIO_ALAW
|
||||||
: MimeTypes.AUDIO_ULAW;
|
: MimeTypes.AUDIO_MLAW;
|
||||||
int pcmEncoding = (header & 0x01) == 1 ? C.ENCODING_PCM_16BIT : C.ENCODING_PCM_8BIT;
|
int pcmEncoding = (header & 0x01) == 1 ? C.ENCODING_PCM_16BIT : C.ENCODING_PCM_8BIT;
|
||||||
Format format = Format.createAudioSampleFormat(null, type, null, Format.NO_VALUE,
|
Format format = Format.createAudioSampleFormat(null, type, null, Format.NO_VALUE,
|
||||||
Format.NO_VALUE, 1, 8000, pcmEncoding, null, null, 0, null);
|
Format.NO_VALUE, 1, 8000, pcmEncoding, null, null, 0, null);
|
||||||
|
@ -264,7 +264,9 @@ public final class MediaCodecInfo {
|
|||||||
logNoSupport("channelCount.aCaps");
|
logNoSupport("channelCount.aCaps");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (audioCapabilities.getMaxInputChannelCount() < channelCount) {
|
int maxInputChannelCount = adjustMaxInputChannelCount(name, mimeType,
|
||||||
|
audioCapabilities.getMaxInputChannelCount());
|
||||||
|
if (maxInputChannelCount < channelCount) {
|
||||||
logNoSupport("channelCount.support, " + channelCount);
|
logNoSupport("channelCount.support, " + channelCount);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -281,6 +283,40 @@ public final class MediaCodecInfo {
|
|||||||
+ Util.DEVICE_DEBUG_INFO + "]");
|
+ Util.DEVICE_DEBUG_INFO + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int adjustMaxInputChannelCount(String name, String mimeType, int maxChannelCount) {
|
||||||
|
if (maxChannelCount > 1 || (Util.SDK_INT >= 26 && maxChannelCount > 0)) {
|
||||||
|
// The maximum channel count looks like it's been set correctly.
|
||||||
|
return maxChannelCount;
|
||||||
|
}
|
||||||
|
if (MimeTypes.AUDIO_MPEG.equals(mimeType)
|
||||||
|
|| MimeTypes.AUDIO_AMR_NB.equals(mimeType)
|
||||||
|
|| MimeTypes.AUDIO_AMR_WB.equals(mimeType)
|
||||||
|
|| MimeTypes.AUDIO_AAC.equals(mimeType)
|
||||||
|
|| MimeTypes.AUDIO_VORBIS.equals(mimeType)
|
||||||
|
|| MimeTypes.AUDIO_OPUS.equals(mimeType)
|
||||||
|
|| MimeTypes.AUDIO_RAW.equals(mimeType)
|
||||||
|
|| MimeTypes.AUDIO_FLAC.equals(mimeType)
|
||||||
|
|| MimeTypes.AUDIO_ALAW.equals(mimeType)
|
||||||
|
|| MimeTypes.AUDIO_MLAW.equals(mimeType)
|
||||||
|
|| MimeTypes.AUDIO_MSGSM.equals(mimeType)) {
|
||||||
|
// Platform code should have set a default.
|
||||||
|
return maxChannelCount;
|
||||||
|
}
|
||||||
|
// The maximum channel count looks incorrect. Adjust it to an assumed default.
|
||||||
|
int assumedMaxChannelCount;
|
||||||
|
if (MimeTypes.AUDIO_AC3.equals(mimeType)) {
|
||||||
|
assumedMaxChannelCount = 6;
|
||||||
|
} else if (MimeTypes.AUDIO_E_AC3.equals(mimeType)) {
|
||||||
|
assumedMaxChannelCount = 16;
|
||||||
|
} else {
|
||||||
|
// Default to the platform limit, which is 30.
|
||||||
|
assumedMaxChannelCount = 30;
|
||||||
|
}
|
||||||
|
Log.w(TAG, "AssumedMaxChannelAdjustment: " + name + ", [" + maxChannelCount + " to "
|
||||||
|
+ assumedMaxChannelCount + "]");
|
||||||
|
return assumedMaxChannelCount;
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isAdaptive(CodecCapabilities capabilities) {
|
private static boolean isAdaptive(CodecCapabilities capabilities) {
|
||||||
return Util.SDK_INT >= 19 && isAdaptiveV19(capabilities);
|
return Util.SDK_INT >= 19 && isAdaptiveV19(capabilities);
|
||||||
}
|
}
|
||||||
|
@ -56,8 +56,10 @@ public final class MediaCodecUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final String TAG = "MediaCodecUtil";
|
private static final String TAG = "MediaCodecUtil";
|
||||||
|
private static final String GOOGLE_RAW_DECODER_NAME = "OMX.google.raw.decoder";
|
||||||
|
private static final String MTK_RAW_DECODER_NAME = "OMX.MTK.AUDIO.DECODER.RAW";
|
||||||
private static final MediaCodecInfo PASSTHROUGH_DECODER_INFO =
|
private static final MediaCodecInfo PASSTHROUGH_DECODER_INFO =
|
||||||
MediaCodecInfo.newPassthroughInstance("OMX.google.raw.decoder");
|
MediaCodecInfo.newPassthroughInstance(GOOGLE_RAW_DECODER_NAME);
|
||||||
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<MediaCodecInfo>> decoderInfosCache = new HashMap<>();
|
private static final HashMap<CodecKey, List<MediaCodecInfo>> decoderInfosCache = new HashMap<>();
|
||||||
@ -155,6 +157,7 @@ public final class MediaCodecUtil {
|
|||||||
+ ". Assuming: " + decoderInfos.get(0).name);
|
+ ". Assuming: " + decoderInfos.get(0).name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
applyWorkarounds(decoderInfos);
|
||||||
decoderInfos = Collections.unmodifiableList(decoderInfos);
|
decoderInfos = Collections.unmodifiableList(decoderInfos);
|
||||||
decoderInfosCache.put(key, decoderInfos);
|
decoderInfosCache.put(key, decoderInfos);
|
||||||
return decoderInfos;
|
return decoderInfos;
|
||||||
@ -339,6 +342,27 @@ public final class MediaCodecUtil {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies a list of {@link MediaCodecInfo}s to apply workarounds where we know better than the
|
||||||
|
* platform.
|
||||||
|
*
|
||||||
|
* @param decoderInfos The list to modify.
|
||||||
|
*/
|
||||||
|
private static void applyWorkarounds(List<MediaCodecInfo> decoderInfos) {
|
||||||
|
if (Util.SDK_INT < 26 && decoderInfos.size() > 1
|
||||||
|
&& MTK_RAW_DECODER_NAME.equals(decoderInfos.get(0).name)) {
|
||||||
|
// Prefer the Google raw decoder over the MediaTek one [Internal: b/62337687].
|
||||||
|
for (int i = 1; i < decoderInfos.size(); i++) {
|
||||||
|
MediaCodecInfo decoderInfo = decoderInfos.get(i);
|
||||||
|
if (GOOGLE_RAW_DECODER_NAME.equals(decoderInfo.name)) {
|
||||||
|
decoderInfos.remove(i);
|
||||||
|
decoderInfos.add(0, decoderInfo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the decoder is known to fail when adapting, despite advertising itself as an
|
* Returns whether the decoder is known to fail when adapting, despite advertising itself as an
|
||||||
* adaptive decoder.
|
* adaptive decoder.
|
||||||
|
@ -106,6 +106,8 @@ public interface MediaPeriod extends SequenceableLoader {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Discards buffered media up to the specified position.
|
* Discards buffered media up to the specified position.
|
||||||
|
* <p>
|
||||||
|
* This method should only be called after the period has been prepared.
|
||||||
*
|
*
|
||||||
* @param positionUs The position in microseconds.
|
* @param positionUs The position in microseconds.
|
||||||
*/
|
*/
|
||||||
@ -116,6 +118,8 @@ public interface MediaPeriod extends SequenceableLoader {
|
|||||||
* <p>
|
* <p>
|
||||||
* After this method has returned a value other than {@link C#TIME_UNSET}, all
|
* After this method has returned a value other than {@link C#TIME_UNSET}, all
|
||||||
* {@link SampleStream}s provided by the period are guaranteed to start from a key frame.
|
* {@link SampleStream}s provided by the period are guaranteed to start from a key frame.
|
||||||
|
* <p>
|
||||||
|
* This method should only be called after the period has been prepared.
|
||||||
*
|
*
|
||||||
* @return If a discontinuity was read then the playback position in microseconds after the
|
* @return If a discontinuity was read then the playback position in microseconds after the
|
||||||
* discontinuity. Else {@link C#TIME_UNSET}.
|
* discontinuity. Else {@link C#TIME_UNSET}.
|
||||||
|
@ -42,7 +42,7 @@ public final class TrackGroup {
|
|||||||
private int hashCode;
|
private int hashCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param formats The track formats. Must not be null or contain null elements.
|
* @param formats The track formats. Must not be null, contain null elements or be of length 0.
|
||||||
*/
|
*/
|
||||||
public TrackGroup(Format... formats) {
|
public TrackGroup(Format... formats) {
|
||||||
Assertions.checkState(formats.length > 0);
|
Assertions.checkState(formats.length > 0);
|
||||||
|
@ -667,13 +667,15 @@ import java.util.List;
|
|||||||
int runLength = 0;
|
int runLength = 0;
|
||||||
int clutIndex = 0;
|
int clutIndex = 0;
|
||||||
int peek = data.readBits(2);
|
int peek = data.readBits(2);
|
||||||
if (!data.readBit()) {
|
if (peek != 0x00) {
|
||||||
runLength = 1;
|
runLength = 1;
|
||||||
clutIndex = peek;
|
clutIndex = peek;
|
||||||
} else if (data.readBit()) {
|
} else if (data.readBit()) {
|
||||||
runLength = 3 + data.readBits(3);
|
runLength = 3 + data.readBits(3);
|
||||||
clutIndex = data.readBits(2);
|
clutIndex = data.readBits(2);
|
||||||
} else if (!data.readBit()) {
|
} else if (data.readBit()) {
|
||||||
|
runLength = 1;
|
||||||
|
} else {
|
||||||
switch (data.readBits(2)) {
|
switch (data.readBits(2)) {
|
||||||
case 0x00:
|
case 0x00:
|
||||||
endOfPixelCodeString = true;
|
endOfPixelCodeString = true;
|
||||||
|
@ -222,9 +222,9 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
|
|||||||
/**
|
/**
|
||||||
* Parses a region declaration.
|
* Parses a region declaration.
|
||||||
* <p>
|
* <p>
|
||||||
* If the region defines an origin and/or extent, it is required that they're defined as
|
* If the region defines an origin and extent, it is required that they're defined as percentages
|
||||||
* percentages of the viewport. Region declarations that define origin and/or extent in other
|
* of the viewport. Region declarations that define origin and extent in other formats are
|
||||||
* formats are unsupported, and null is returned.
|
* unsupported, and null is returned.
|
||||||
*/
|
*/
|
||||||
private TtmlRegion parseRegionAttributes(XmlPullParser xmlParser) {
|
private TtmlRegion parseRegionAttributes(XmlPullParser xmlParser) {
|
||||||
String regionId = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_ID);
|
String regionId = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_ID);
|
||||||
@ -250,9 +250,13 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Log.w(TAG, "Ignoring region without an origin");
|
||||||
|
return null;
|
||||||
|
// TODO: Should default to top left as below in this case, but need to fix
|
||||||
|
// https://github.com/google/ExoPlayer/issues/2953 first.
|
||||||
// Origin is omitted. Default to top left.
|
// Origin is omitted. Default to top left.
|
||||||
position = 0;
|
// position = 0;
|
||||||
line = 0;
|
// line = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
float width;
|
float width;
|
||||||
@ -273,9 +277,13 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Log.w(TAG, "Ignoring region without an extent");
|
||||||
|
return null;
|
||||||
|
// TODO: Should default to extent of parent as below in this case, but need to fix
|
||||||
|
// https://github.com/google/ExoPlayer/issues/2953 first.
|
||||||
// Extent is omitted. Default to extent of parent.
|
// Extent is omitted. Default to extent of parent.
|
||||||
width = 1;
|
// width = 1;
|
||||||
height = 1;
|
// height = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Cue.AnchorType int lineAnchor = Cue.ANCHOR_TYPE_START;
|
@Cue.AnchorType int lineAnchor = Cue.ANCHOR_TYPE_START;
|
||||||
|
@ -304,10 +304,10 @@ public abstract class MappingTrackSelector extends TrackSelector {
|
|||||||
trackSelections[i] = null;
|
trackSelections[i] = null;
|
||||||
} else {
|
} else {
|
||||||
TrackGroupArray rendererTrackGroup = rendererTrackGroupArrays[i];
|
TrackGroupArray rendererTrackGroup = rendererTrackGroupArrays[i];
|
||||||
Map<TrackGroupArray, SelectionOverride> overrides = selectionOverrides.get(i);
|
if (hasSelectionOverride(i, rendererTrackGroup)) {
|
||||||
SelectionOverride override = overrides == null ? null : overrides.get(rendererTrackGroup);
|
SelectionOverride override = selectionOverrides.get(i).get(rendererTrackGroup);
|
||||||
if (override != null) {
|
trackSelections[i] = override == null ? null
|
||||||
trackSelections[i] = override.createTrackSelection(rendererTrackGroup);
|
: override.createTrackSelection(rendererTrackGroup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import android.net.Uri;
|
|||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
@ -71,9 +72,13 @@ public final class ContentDataSource implements DataSource {
|
|||||||
try {
|
try {
|
||||||
uri = dataSpec.uri;
|
uri = dataSpec.uri;
|
||||||
assetFileDescriptor = resolver.openAssetFileDescriptor(uri, "r");
|
assetFileDescriptor = resolver.openAssetFileDescriptor(uri, "r");
|
||||||
|
if (assetFileDescriptor == null) {
|
||||||
|
throw new FileNotFoundException("Could not open file descriptor for: " + uri);
|
||||||
|
}
|
||||||
inputStream = new FileInputStream(assetFileDescriptor.getFileDescriptor());
|
inputStream = new FileInputStream(assetFileDescriptor.getFileDescriptor());
|
||||||
long skipped = inputStream.skip(dataSpec.position);
|
long assertStartOffset = assetFileDescriptor.getStartOffset();
|
||||||
if (skipped < dataSpec.position) {
|
long skipped = inputStream.skip(assertStartOffset + dataSpec.position) - assertStartOffset;
|
||||||
|
if (skipped != dataSpec.position) {
|
||||||
// We expect the skip to be satisfied in full. If it isn't then we're probably trying to
|
// We expect the skip to be satisfied in full. If it isn't then we're probably trying to
|
||||||
// skip beyond the end of the data.
|
// skip beyond the end of the data.
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
@ -81,12 +86,16 @@ public final class ContentDataSource implements DataSource {
|
|||||||
if (dataSpec.length != C.LENGTH_UNSET) {
|
if (dataSpec.length != C.LENGTH_UNSET) {
|
||||||
bytesRemaining = dataSpec.length;
|
bytesRemaining = dataSpec.length;
|
||||||
} else {
|
} else {
|
||||||
bytesRemaining = inputStream.available();
|
bytesRemaining = assetFileDescriptor.getLength();
|
||||||
if (bytesRemaining == 0) {
|
if (bytesRemaining == AssetFileDescriptor.UNKNOWN_LENGTH) {
|
||||||
// FileInputStream.available() returns 0 if the remaining length cannot be determined, or
|
// The asset must extend to the end of the file.
|
||||||
// if it's greater than Integer.MAX_VALUE. We don't know the true length in either case,
|
bytesRemaining = inputStream.available();
|
||||||
// so treat as unbounded.
|
if (bytesRemaining == 0) {
|
||||||
bytesRemaining = C.LENGTH_UNSET;
|
// FileInputStream.available() returns 0 if the remaining length cannot be determined,
|
||||||
|
// or if it's greater than Integer.MAX_VALUE. We don't know the true length in either
|
||||||
|
// case, so treat as unbounded.
|
||||||
|
bytesRemaining = C.LENGTH_UNSET;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -48,7 +48,7 @@ public final class MimeTypes {
|
|||||||
public static final String AUDIO_MPEG_L2 = BASE_TYPE_AUDIO + "/mpeg-L2";
|
public static final String AUDIO_MPEG_L2 = BASE_TYPE_AUDIO + "/mpeg-L2";
|
||||||
public static final String AUDIO_RAW = BASE_TYPE_AUDIO + "/raw";
|
public static final String AUDIO_RAW = BASE_TYPE_AUDIO + "/raw";
|
||||||
public static final String AUDIO_ALAW = BASE_TYPE_AUDIO + "/g711-alaw";
|
public static final String AUDIO_ALAW = BASE_TYPE_AUDIO + "/g711-alaw";
|
||||||
public static final String AUDIO_ULAW = BASE_TYPE_AUDIO + "/g711-mlaw";
|
public static final String AUDIO_MLAW = BASE_TYPE_AUDIO + "/g711-mlaw";
|
||||||
public static final String AUDIO_AC3 = BASE_TYPE_AUDIO + "/ac3";
|
public static final String AUDIO_AC3 = BASE_TYPE_AUDIO + "/ac3";
|
||||||
public static final String AUDIO_E_AC3 = BASE_TYPE_AUDIO + "/eac3";
|
public static final String AUDIO_E_AC3 = BASE_TYPE_AUDIO + "/eac3";
|
||||||
public static final String AUDIO_TRUEHD = BASE_TYPE_AUDIO + "/true-hd";
|
public static final String AUDIO_TRUEHD = BASE_TYPE_AUDIO + "/true-hd";
|
||||||
@ -59,8 +59,9 @@ public final class MimeTypes {
|
|||||||
public static final String AUDIO_OPUS = BASE_TYPE_AUDIO + "/opus";
|
public static final String AUDIO_OPUS = BASE_TYPE_AUDIO + "/opus";
|
||||||
public static final String AUDIO_AMR_NB = BASE_TYPE_AUDIO + "/3gpp";
|
public static final String AUDIO_AMR_NB = BASE_TYPE_AUDIO + "/3gpp";
|
||||||
public static final String AUDIO_AMR_WB = BASE_TYPE_AUDIO + "/amr-wb";
|
public static final String AUDIO_AMR_WB = BASE_TYPE_AUDIO + "/amr-wb";
|
||||||
public static final String AUDIO_FLAC = BASE_TYPE_AUDIO + "/x-flac";
|
public static final String AUDIO_FLAC = BASE_TYPE_AUDIO + "/flac";
|
||||||
public static final String AUDIO_ALAC = BASE_TYPE_AUDIO + "/alac";
|
public static final String AUDIO_ALAC = BASE_TYPE_AUDIO + "/alac";
|
||||||
|
public static final String AUDIO_MSGSM = BASE_TYPE_AUDIO + "/gsm";
|
||||||
public static final String AUDIO_UNKNOWN = BASE_TYPE_AUDIO + "/x-unknown";
|
public static final String AUDIO_UNKNOWN = BASE_TYPE_AUDIO + "/x-unknown";
|
||||||
|
|
||||||
public static final String TEXT_VTT = BASE_TYPE_TEXT + "/vtt";
|
public static final String TEXT_VTT = BASE_TYPE_TEXT + "/vtt";
|
||||||
|
@ -260,11 +260,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||||||
super.onStarted();
|
super.onStarted();
|
||||||
droppedFrames = 0;
|
droppedFrames = 0;
|
||||||
droppedFrameAccumulationStartTimeMs = SystemClock.elapsedRealtime();
|
droppedFrameAccumulationStartTimeMs = SystemClock.elapsedRealtime();
|
||||||
joiningDeadlineMs = C.TIME_UNSET;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStopped() {
|
protected void onStopped() {
|
||||||
|
joiningDeadlineMs = C.TIME_UNSET;
|
||||||
maybeNotifyDroppedFrames();
|
maybeNotifyDroppedFrames();
|
||||||
super.onStopped();
|
super.onStopped();
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ import java.io.InputStream;
|
|||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
@ -174,6 +175,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||||||
|
|
||||||
private static HlsMasterPlaylist parseMasterPlaylist(LineIterator iterator, String baseUri)
|
private static HlsMasterPlaylist parseMasterPlaylist(LineIterator iterator, String baseUri)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
HashSet<String> variantUrls = new HashSet<>();
|
||||||
ArrayList<HlsMasterPlaylist.HlsUrl> variants = new ArrayList<>();
|
ArrayList<HlsMasterPlaylist.HlsUrl> variants = new ArrayList<>();
|
||||||
ArrayList<HlsMasterPlaylist.HlsUrl> audios = new ArrayList<>();
|
ArrayList<HlsMasterPlaylist.HlsUrl> audios = new ArrayList<>();
|
||||||
ArrayList<HlsMasterPlaylist.HlsUrl> subtitles = new ArrayList<>();
|
ArrayList<HlsMasterPlaylist.HlsUrl> subtitles = new ArrayList<>();
|
||||||
@ -251,11 +253,13 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||||||
width = Format.NO_VALUE;
|
width = Format.NO_VALUE;
|
||||||
height = Format.NO_VALUE;
|
height = Format.NO_VALUE;
|
||||||
}
|
}
|
||||||
line = iterator.next();
|
line = iterator.next(); // #EXT-X-STREAM-INF's URI.
|
||||||
Format format = Format.createVideoContainerFormat(Integer.toString(variants.size()),
|
if (variantUrls.add(line)) {
|
||||||
MimeTypes.APPLICATION_M3U8, null, codecs, bitrate, width, height, Format.NO_VALUE, null,
|
Format format = Format.createVideoContainerFormat(Integer.toString(variants.size()),
|
||||||
0);
|
MimeTypes.APPLICATION_M3U8, null, codecs, bitrate, width, height, Format.NO_VALUE,
|
||||||
variants.add(new HlsMasterPlaylist.HlsUrl(line, format));
|
null, 0);
|
||||||
|
variants.add(new HlsMasterPlaylist.HlsUrl(line, format));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (noClosedCaptions) {
|
if (noClosedCaptions) {
|
||||||
|
@ -89,7 +89,6 @@ public class DefaultTimeBar extends View implements TimeBar {
|
|||||||
private final Formatter formatter;
|
private final Formatter formatter;
|
||||||
private final Runnable stopScrubbingRunnable;
|
private final Runnable stopScrubbingRunnable;
|
||||||
|
|
||||||
private int scrubberSize;
|
|
||||||
private OnScrubListener listener;
|
private OnScrubListener listener;
|
||||||
private int keyCountIncrement;
|
private int keyCountIncrement;
|
||||||
private long keyTimeIncrement;
|
private long keyTimeIncrement;
|
||||||
@ -184,7 +183,6 @@ public class DefaultTimeBar extends View implements TimeBar {
|
|||||||
stopScrubbing(false);
|
stopScrubbing(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
scrubberSize = scrubberEnabledSize;
|
|
||||||
scrubberPadding =
|
scrubberPadding =
|
||||||
(Math.max(scrubberDisabledSize, Math.max(scrubberEnabledSize, scrubberDraggedSize)) + 1)
|
(Math.max(scrubberDisabledSize, Math.max(scrubberEnabledSize, scrubberDraggedSize)) + 1)
|
||||||
/ 2;
|
/ 2;
|
||||||
@ -234,8 +232,6 @@ public class DefaultTimeBar extends View implements TimeBar {
|
|||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
if (scrubbing && duration == C.TIME_UNSET) {
|
if (scrubbing && duration == C.TIME_UNSET) {
|
||||||
stopScrubbing(true);
|
stopScrubbing(true);
|
||||||
} else {
|
|
||||||
updateScrubberState();
|
|
||||||
}
|
}
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
@ -251,7 +247,6 @@ public class DefaultTimeBar extends View implements TimeBar {
|
|||||||
@Override
|
@Override
|
||||||
public void setEnabled(boolean enabled) {
|
public void setEnabled(boolean enabled) {
|
||||||
super.setEnabled(enabled);
|
super.setEnabled(enabled);
|
||||||
updateScrubberState();
|
|
||||||
if (scrubbing && !enabled) {
|
if (scrubbing && !enabled) {
|
||||||
stopScrubbing(true);
|
stopScrubbing(true);
|
||||||
}
|
}
|
||||||
@ -436,7 +431,6 @@ public class DefaultTimeBar extends View implements TimeBar {
|
|||||||
|
|
||||||
private void startScrubbing() {
|
private void startScrubbing() {
|
||||||
scrubbing = true;
|
scrubbing = true;
|
||||||
updateScrubberState();
|
|
||||||
ViewParent parent = getParent();
|
ViewParent parent = getParent();
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
parent.requestDisallowInterceptTouchEvent(true);
|
parent.requestDisallowInterceptTouchEvent(true);
|
||||||
@ -452,18 +446,12 @@ public class DefaultTimeBar extends View implements TimeBar {
|
|||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
parent.requestDisallowInterceptTouchEvent(false);
|
parent.requestDisallowInterceptTouchEvent(false);
|
||||||
}
|
}
|
||||||
updateScrubberState();
|
|
||||||
invalidate();
|
invalidate();
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
listener.onScrubStop(this, getScrubberPosition(), canceled);
|
listener.onScrubStop(this, getScrubberPosition(), canceled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateScrubberState() {
|
|
||||||
scrubberSize = scrubbing ? scrubberDraggedSize
|
|
||||||
: (isEnabled() && duration >= 0 ? scrubberEnabledSize : scrubberDisabledSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void update() {
|
private void update() {
|
||||||
bufferedBar.set(progressBar);
|
bufferedBar.set(progressBar);
|
||||||
scrubberBar.set(progressBar);
|
scrubberBar.set(progressBar);
|
||||||
@ -543,6 +531,8 @@ public class DefaultTimeBar extends View implements TimeBar {
|
|||||||
if (duration <= 0) {
|
if (duration <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
int scrubberSize = (scrubbing || isFocused()) ? scrubberDraggedSize
|
||||||
|
: (isEnabled() ? scrubberEnabledSize : scrubberDisabledSize);
|
||||||
int playheadRadius = scrubberSize / 2;
|
int playheadRadius = scrubberSize / 2;
|
||||||
int playheadCenter = Util.constrainValue(scrubberBar.right, scrubberBar.left,
|
int playheadCenter = Util.constrainValue(scrubberBar.right, scrubberBar.left,
|
||||||
progressBar.right);
|
progressBar.right);
|
||||||
|
@ -17,11 +17,14 @@ package com.google.android.exoplayer2.testutil;
|
|||||||
|
|
||||||
import android.app.Instrumentation;
|
import android.app.Instrumentation;
|
||||||
import android.test.InstrumentationTestCase;
|
import android.test.InstrumentationTestCase;
|
||||||
|
import android.test.MoreAsserts;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.extractor.Extractor;
|
import com.google.android.exoplayer2.extractor.Extractor;
|
||||||
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.testutil.FakeExtractorInput.SimulatedIOException;
|
import com.google.android.exoplayer2.testutil.FakeExtractorInput.SimulatedIOException;
|
||||||
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -64,6 +67,22 @@ public class TestUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] readToEnd(DataSource dataSource) throws IOException {
|
||||||
|
byte[] data = new byte[1024];
|
||||||
|
int position = 0;
|
||||||
|
int bytesRead = 0;
|
||||||
|
while (bytesRead != C.RESULT_END_OF_INPUT) {
|
||||||
|
if (position == data.length) {
|
||||||
|
data = Arrays.copyOf(data, data.length * 2);
|
||||||
|
}
|
||||||
|
bytesRead = dataSource.read(data, position, data.length - position);
|
||||||
|
if (bytesRead != C.RESULT_END_OF_INPUT) {
|
||||||
|
position += bytesRead;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Arrays.copyOf(data, position);
|
||||||
|
}
|
||||||
|
|
||||||
public static FakeExtractorOutput consumeTestData(Extractor extractor, FakeExtractorInput input,
|
public static FakeExtractorOutput consumeTestData(Extractor extractor, FakeExtractorInput input,
|
||||||
long timeUs) throws IOException, InterruptedException {
|
long timeUs) throws IOException, InterruptedException {
|
||||||
return consumeTestData(extractor, input, timeUs, false);
|
return consumeTestData(extractor, input, timeUs, false);
|
||||||
@ -373,4 +392,24 @@ public class TestUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that data read from a {@link DataSource} matches {@code expected}.
|
||||||
|
*
|
||||||
|
* @param dataSource The {@link DataSource} through which to read.
|
||||||
|
* @param dataSpec The {@link DataSpec} to use when opening the {@link DataSource}.
|
||||||
|
* @param expectedData The expected data.
|
||||||
|
* @throws IOException If an error occurs reading fom the {@link DataSource}.
|
||||||
|
*/
|
||||||
|
public static void assertDataSourceContent(DataSource dataSource, DataSpec dataSpec,
|
||||||
|
byte[] expectedData) throws IOException {
|
||||||
|
try {
|
||||||
|
long length = dataSource.open(dataSpec);
|
||||||
|
Assert.assertEquals(length, expectedData.length);
|
||||||
|
byte[] readData = TestUtil.readToEnd(dataSource);
|
||||||
|
MoreAsserts.assertEquals(expectedData, readData);
|
||||||
|
} finally {
|
||||||
|
dataSource.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user