Update to 4.9.1

This commit is contained in:
DrKLO 2018-08-27 11:33:11 +03:00
parent d073b80063
commit fe599cd519
663 changed files with 40241 additions and 34380 deletions

View file

@ -11,26 +11,29 @@ configurations {
}
dependencies {
compile 'com.google.firebase:firebase-messaging:17.1.0'
compile 'com.google.firebase:firebase-config:16.0.0'
compile 'com.google.android.gms:play-services-maps:15.0.1'
compile 'com.google.android.gms:play-services-vision:15.0.2'
compile 'com.google.android.gms:play-services-wallet:15.0.1'
compile 'com.google.android.gms:play-services-wearable:15.0.1'
implementation 'com.android.support:support-core-ui:27.1.1'
implementation 'com.android.support:support-compat:27.1.1'
implementation 'com.android.support:support-core-utils:27.1.1'
implementation 'com.android.support:support-v13:27.1.1'
implementation 'com.android.support:palette-v7:27.1.1'
implementation 'com.android.support:exifinterface:27.1.1'
compile 'net.hockeyapp.android:HockeySDK:5.1.0'
compile 'com.googlecode.mp4parser:isoparser:1.0.6'
compile 'com.stripe:stripe-android:2.0.2'
compileOnly 'org.checkerframework:checker-qual:2.5.0'
compileOnly 'org.checkerframework:checker-compat-qual:2.5.0'
implementation 'com.google.firebase:firebase-core:16.0.3'
implementation 'com.google.firebase:firebase-messaging:17.3.0'
implementation 'com.google.firebase:firebase-config:16.0.0'
implementation 'com.google.android.gms:play-services-maps:15.0.1'
implementation 'com.google.android.gms:play-services-vision:15.0.2'
implementation 'com.google.android.gms:play-services-wallet:16.0.0'
implementation 'com.google.android.gms:play-services-wearable:15.0.1'
implementation 'com.android.support:support-core-ui:28.0.0-rc01'
implementation 'com.android.support:support-compat:28.0.0-rc01'
implementation 'com.android.support:support-core-utils:28.0.0-rc01'
implementation 'com.android.support:support-v13:28.0.0-rc01'
implementation 'com.android.support:palette-v7:28.0.0-rc01'
implementation 'com.android.support:exifinterface:28.0.0-rc01'
implementation 'net.hockeyapp.android:HockeySDK:5.1.0'
implementation 'com.googlecode.mp4parser:isoparser:1.0.6'
implementation 'com.stripe:stripe-android:2.0.2'
}
android {
compileSdkVersion 27
buildToolsVersion '27.0.3'
compileSdkVersion 28
buildToolsVersion '28.0.2'
defaultConfig.applicationId = "org.telegram.messenger"
@ -52,8 +55,8 @@ android {
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
signingConfigs {
@ -98,7 +101,7 @@ android {
}
}
defaultConfig.versionCode = 1340
defaultConfig.versionCode = 1358
sourceSets.debug {
manifest.srcFile 'config/debug/AndroidManifest.xml'
@ -235,7 +238,7 @@ android {
defaultConfig {
minSdkVersion 16
targetSdkVersion 27
versionName "4.9.0"
versionName "4.9.1"
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']

View file

@ -387,7 +387,7 @@ include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE := tmessages.28
LOCAL_MODULE := tmessages.29
LOCAL_CFLAGS := -w -std=c11 -Os -DNULL=0 -DSOCKLEN_T=socklen_t -DLOCALE_NOT_USED -D_LARGEFILE_SOURCE=1
LOCAL_CFLAGS += -Drestrict='' -D__EMX__ -DOPUS_BUILD -DFIXED_POINT -DUSE_ALLOCA -DHAVE_LRINT -DHAVE_LRINTF -fno-math-errno
LOCAL_CFLAGS += -DANDROID_NDK -DDISABLE_IMPORTGL -fno-strict-aliasing -fprefetch-loop-arrays -DAVOID_TABLES -DANDROID_TILE_BASED_DECODE -DANDROID_ARMV6_IDCT -ffast-math -D__STDC_CONSTANT_MACROS

View file

@ -142,10 +142,12 @@ jlong Java_org_telegram_SQLite_SQLiteDatabase_opendb(JNIEnv *env, jobject object
char const *fileNameStr = env->GetStringUTFChars(fileName, 0);
char const *tempDirStr = env->GetStringUTFChars(tempDir, 0);
if (sqlite3_temp_directory != 0) {
if (sqlite3_temp_directory != 0 && strcmp(sqlite3_temp_directory, tempDirStr)) {
sqlite3_free(sqlite3_temp_directory);
}
if (sqlite3_temp_directory == 0) {
sqlite3_temp_directory = sqlite3_mprintf("%s", tempDirStr);
}
sqlite3 *handle = 0;
int err = sqlite3_open(fileNameStr, &handle);

View file

@ -39,6 +39,8 @@ jmethodID jclass_ConnectionsManager_onProxyError;
jmethodID jclass_ConnectionsManager_getHostByName;
jmethodID jclass_ConnectionsManager_getInitFlags;
bool check_utf8(const char *data, size_t len);
/*jint createLoadOpetation(JNIEnv *env, jclass c, jint dc_id, jlong id, jlong volume_id, jlong access_hash, jint local_id, jbyteArray encKey, jbyteArray encIv, jstring extension, jint version, jint size, jstring dest, jstring temp, jobject delegate) {
if (encKey != nullptr && encIv == nullptr || encKey == nullptr && encIv != nullptr || extension == nullptr || dest == nullptr || temp == nullptr) {
return 0;
@ -196,7 +198,13 @@ void sendRequest(JNIEnv *env, jclass c, jint instanceNum, jlong object, jobject
ptr = (jlong) resp->response.get();
} else if (error != nullptr) {
errorCode = error->code;
errorText = jniEnv[instanceNum]->NewStringUTF(error->text.c_str());
const char *text = error->text.c_str();
size_t size = error->text.size();
if (check_utf8(text, size)) {
errorText = jniEnv[instanceNum]->NewStringUTF(text);
} else {
errorText = jniEnv[instanceNum]->NewStringUTF("UTF-8 ERROR");
}
}
if (onComplete != nullptr) {
jniEnv[instanceNum]->CallVoidMethod(onComplete, jclass_RequestDelegateInternal_run, ptr, errorCode, errorText, networkType);
@ -632,3 +640,56 @@ extern "C" int registerNativeTgNetFunctions(JavaVM *vm, JNIEnv *env) {
return JNI_TRUE;
}
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
bool check_utf8(const char *data, size_t len) {
const char *data_end = data + len;
do {
unsigned int a = (unsigned char) (*data++);
if ((a & 0x80) == 0) {
if (data == data_end + 1) {
return true;
}
continue;
}
#define ENSURE(condition) \
if (!(condition)) { \
return false; \
}
ENSURE((a & 0x40) != 0);
unsigned int b = (unsigned char) (*data++);
ENSURE((b & 0xc0) == 0x80);
if ((a & 0x20) == 0) {
ENSURE((a & 0x1e) > 0);
continue;
}
unsigned int c = (unsigned char) (*data++);
ENSURE((c & 0xc0) == 0x80);
if ((a & 0x10) == 0) {
int x = (((a & 0x0f) << 6) | (b & 0x20));
ENSURE(x != 0 && x != 0x360);
continue;
}
unsigned int d = (unsigned char) (*data++);
ENSURE((d & 0xc0) == 0x80);
if ((a & 0x08) == 0) {
int t = (((a & 0x07) << 6) | (b & 0x30));
ENSURE(0 < t && t < 0x110);
continue;
}
return false;
#undef ENSURE
} while (1);
}

View file

@ -36,10 +36,10 @@ extern "C" {
__VA_ARGS__))
#define DECODER_FUNC(RETURN_TYPE, NAME, ...) \
JNIEXPORT RETURN_TYPE Java_org_telegram_messenger_exoplayer2_ext_ffmpeg_FfmpegDecoder_##NAME(JNIEnv* env, jobject thiz, ##__VA_ARGS__)
JNIEXPORT RETURN_TYPE Java_com_google_android_exoplayer2_ext_ffmpeg_FfmpegDecoder_##NAME(JNIEnv* env, jobject thiz, ##__VA_ARGS__)
#define LIBRARY_FUNC(RETURN_TYPE, NAME, ...) \
JNIEXPORT RETURN_TYPE Java_org_telegram_messenger_exoplayer2_ext_ffmpeg_FfmpegLibrary_##NAME(JNIEnv* env, jobject thiz, ##__VA_ARGS__)
JNIEXPORT RETURN_TYPE Java_com_google_android_exoplayer2_ext_ffmpeg_FfmpegLibrary_##NAME(JNIEnv* env, jobject thiz, ##__VA_ARGS__)
#define ERROR_STRING_BUFFER_LENGTH 256
@ -60,8 +60,9 @@ AVCodec *getCodecByName(JNIEnv* env, jstring codecName);
* provided extraData as initialization data for the decoder if it is non-NULL.
* Returns the created context.
*/
AVCodecContext *createContext(JNIEnv *env, AVCodec *codec,
jbyteArray extraData, jboolean outputFloat);
AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData,
jboolean outputFloat, jint rawSampleRate,
jint rawChannelCount);
/**
* Decodes the packet into the output buffer, returning the number of bytes
@ -89,13 +90,14 @@ LIBRARY_FUNC(jboolean, ffmpegHasDecoder, jstring codecName) {
}
DECODER_FUNC(jlong, ffmpegInitialize, jstring codecName, jbyteArray extraData,
jboolean outputFloat) {
jboolean outputFloat, jint rawSampleRate, jint rawChannelCount) {
AVCodec *codec = getCodecByName(env, codecName);
if (!codec) {
LOGE("Codec not found.");
return 0L;
}
return (jlong) createContext(env, codec, extraData, outputFloat);
return (jlong)createContext(env, codec, extraData, outputFloat, rawSampleRate,
rawChannelCount);
}
DECODER_FUNC(jint, ffmpegDecode, jlong context, jobject inputData,
@ -159,8 +161,11 @@ DECODER_FUNC(jlong, ffmpegReset, jlong jContext, jbyteArray extraData) {
LOGE("Unexpected error finding codec %d.", codecId);
return 0L;
}
return (jlong) createContext(env, codec, extraData,
context->request_sample_fmt == OUTPUT_FORMAT_PCM_FLOAT);
jboolean outputFloat =
(jboolean)(context->request_sample_fmt == OUTPUT_FORMAT_PCM_FLOAT);
return (jlong)createContext(env, codec, extraData, outputFloat,
/* rawSampleRate= */ -1,
/* rawChannelCount= */ -1);
}
avcodec_flush_buffers(context);
@ -183,8 +188,9 @@ AVCodec *getCodecByName(JNIEnv *env, jstring codecName) {
return codec;
}
AVCodecContext *createContext(JNIEnv *env, AVCodec *codec,
jbyteArray extraData, jboolean outputFloat) {
AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData,
jboolean outputFloat, jint rawSampleRate,
jint rawChannelCount) {
AVCodecContext *context = avcodec_alloc_context3(codec);
if (!context) {
LOGE("Failed to allocate context.");
@ -204,6 +210,12 @@ AVCodecContext *createContext(JNIEnv *env, AVCodec *codec,
}
env->GetByteArrayRegion(extraData, 0, size, (jbyte *) context->extradata);
}
if (context->codec_id == AV_CODEC_ID_PCM_MULAW ||
context->codec_id == AV_CODEC_ID_PCM_ALAW) {
context->sample_rate = rawSampleRate;
context->channels = rawChannelCount;
context->channel_layout = av_get_default_channel_layout(rawChannelCount);
}
int result = avcodec_open2(context, codec, NULL);
if (result < 0) {
logError("avcodec_open2", result);
@ -244,7 +256,7 @@ int decodePacket(AVCodecContext *context, AVPacket *packet,
// Resample output.
AVSampleFormat sampleFormat = context->sample_fmt;
int channelCount = context->channels;
uint64_t channelLayout = context->channel_layout;
int channelLayout = context->channel_layout;
int sampleRate = context->sample_rate;
int sampleCount = frame->nb_samples;
int dataSize = av_samples_get_buffer_size(NULL, channelCount, sampleCount,

View file

@ -19,7 +19,7 @@
#include "include/flac_parser.h"
#define DECODER_FUNC(RETURN_TYPE, NAME, ...) \
RETURN_TYPE Java_org_telegram_messenger_exoplayer2_ext_flac_FlacDecoderJni_##NAME(JNIEnv *env, jobject thiz, ##__VA_ARGS__)
RETURN_TYPE Java_com_google_android_exoplayer2_ext_flac_FlacDecoderJni_##NAME(JNIEnv *env, jobject thiz, ##__VA_ARGS__)
class JavaDataSource : public DataSource {
public:
@ -27,7 +27,7 @@ class JavaDataSource : public DataSource {
this->env = env;
this->flacDecoderJni = flacDecoderJni;
if (mid == NULL) {
jclass cls = env->GetObjectClass(flacDecoderJni);
jclass cls = env->GetObjectClass(this->flacDecoderJni);
mid = env->GetMethodID(cls, "read", "(Ljava/nio/ByteBuffer;)I");
env->DeleteLocalRef(cls);
}
@ -86,7 +86,9 @@ DECODER_FUNC(jobject, flacDecodeMetadata, jlong jContext) {
const FLAC__StreamMetadata_StreamInfo &streamInfo =
context->parser->getStreamInfo();
jclass cls = env->FindClass("org/telegram/messenger/exoplayer2/util/FlacStreamInfo");
jclass cls = env->FindClass(
"com/google/android/exoplayer2/util/"
"FlacStreamInfo");
jmethodID constructor = env->GetMethodID(cls, "<init>", "(IIIIIIIJ)V");
return env->NewObject(cls, constructor, streamInfo.min_blocksize,

View file

@ -28,10 +28,10 @@
__VA_ARGS__))
#define DECODER_FUNC(RETURN_TYPE, NAME, ...) \
JNIEXPORT RETURN_TYPE Java_org_telegram_messenger_exoplayer2_ext_opus_OpusDecoder_##NAME(JNIEnv* env, jobject thiz, ##__VA_ARGS__)
JNIEXPORT RETURN_TYPE Java_com_google_android_exoplayer2_ext_opus_OpusDecoder_##NAME(JNIEnv* env, jobject thiz, ##__VA_ARGS__)
#define LIBRARY_FUNC(RETURN_TYPE, NAME, ...) \
JNIEXPORT RETURN_TYPE Java_org_telegram_messenger_exoplayer2_ext_opus_OpusLibrary_##NAME(JNIEnv* env, jobject thiz, ##__VA_ARGS__)
JNIEXPORT RETURN_TYPE Java_com_google_android_exoplayer2_ext_opus_OpusLibrary_##NAME(JNIEnv* env, jobject thiz, ##__VA_ARGS__)
// JNI references for SimpleOutputBuffer class.
static jmethodID outputBufferInit;
@ -65,7 +65,7 @@ DECODER_FUNC(jlong, opusInit, jint sampleRate, jint channelCount,
// Populate JNI References.
const jclass outputBufferClass = env->FindClass(
"org/telegram/messenger/exoplayer2/decoder/SimpleOutputBuffer");
"com/google/android/exoplayer2/decoder/SimpleOutputBuffer");
outputBufferInit = env->GetMethodID(outputBufferClass, "init",
"(JI)Ljava/nio/ByteBuffer;");

View file

@ -5,6 +5,7 @@
#include <inttypes.h>
#include <stdlib.h>
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
@ -52,6 +53,23 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_aesIgeEncryption(JNIEnv *en
(*env)->ReleaseByteArrayElements(env, iv, ivBuff, 0);
}
JNIEXPORT jint Java_org_telegram_messenger_Utilities_pbkdf2(JNIEnv *env, jclass class, jbyteArray password, jbyteArray salt, jbyteArray dst, jint iterations) {
jbyte *passwordBuff = (*env)->GetByteArrayElements(env, password, NULL);
size_t passwordLength = (size_t) (*env)->GetArrayLength(env, password);
jbyte *saltBuff = (*env)->GetByteArrayElements(env, salt, NULL);
size_t saltLength = (size_t) (*env)->GetArrayLength(env, salt);
jbyte *dstBuff = (*env)->GetByteArrayElements(env, dst, NULL);
size_t dstLength = (size_t) (*env)->GetArrayLength(env, dst);
int result = PKCS5_PBKDF2_HMAC((char *) passwordBuff, passwordLength, (uint8_t *) saltBuff, saltLength, (unsigned int) iterations, EVP_sha512(), dstLength, (uint8_t *) dstBuff);
(*env)->ReleaseByteArrayElements(env, password, passwordBuff, JNI_ABORT);
(*env)->ReleaseByteArrayElements(env, salt, saltBuff, JNI_ABORT);
(*env)->ReleaseByteArrayElements(env, dst, dstBuff, 0);
return result;
}
JNIEXPORT void Java_org_telegram_messenger_Utilities_aesCtrDecryption(JNIEnv *env, jclass class, jobject buffer, jbyteArray key, jbyteArray iv, jint offset, jint length) {
jbyte *what = (*env)->GetDirectBufferAddress(env, buffer) + offset;
unsigned char *keyBuff = (unsigned char *)(*env)->GetByteArrayElements(env, key, NULL);

@ -1 +1 @@
Subproject commit 8faf6f670037a8049ede4b85a004c40bdd359c6a
Subproject commit 23ae67306d7fa1c4d5a8a9c8653f63ab93100f88

View file

@ -4,9 +4,12 @@
@com.google.android.gms.common.annotation.KeepName *;
}
-keep class org.telegram.** { *; }
-keep class com.google.android.exoplayer2.** { *; }
-keep class com.coremedia.** { *; }
-keep class com.googlecode.mp4parser.** { *; }
-dontwarn com.coremedia.**
-dontwarn org.telegram.**
-dontwarn com.google.android.exoplayer2.**
-dontwarn com.google.android.gms.**
-dontwarn com.google.common.cache.**
-dontwarn com.google.common.primitives.**

View file

@ -20,6 +20,7 @@
<uses-feature android:name="android.hardware.camera2" android:required="false" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

View file

@ -13,15 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.support.annotation.Nullable;
import org.telegram.messenger.exoplayer2.decoder.DecoderInputBuffer;
import org.telegram.messenger.exoplayer2.drm.DrmInitData;
import org.telegram.messenger.exoplayer2.drm.DrmSessionManager;
import org.telegram.messenger.exoplayer2.source.SampleStream;
import org.telegram.messenger.exoplayer2.util.Assertions;
import org.telegram.messenger.exoplayer2.util.MediaClock;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MediaClock;
import java.io.IOException;
/**

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.annotation.TargetApi;
import android.content.Context;
@ -23,8 +23,8 @@ import android.media.MediaCodec;
import android.media.MediaFormat;
import android.support.annotation.IntDef;
import android.view.Surface;
import org.telegram.messenger.exoplayer2.PlayerMessage.Target;
import org.telegram.messenger.exoplayer2.util.Util;
import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.util.Util;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.UUID;
@ -77,6 +77,12 @@ public final class C {
*/
public static final long NANOS_PER_SECOND = 1000000000L;
/** The number of bits per byte. */
public static final int BITS_PER_BYTE = 8;
/** The number of bytes per float. */
public static final int BYTES_PER_FLOAT = 4;
/**
* The name of the ASCII charset.
*/
@ -136,6 +142,8 @@ public final class C {
ENCODING_PCM_24BIT,
ENCODING_PCM_32BIT,
ENCODING_PCM_FLOAT,
ENCODING_PCM_MU_LAW,
ENCODING_PCM_A_LAW,
ENCODING_AC3,
ENCODING_E_AC3,
ENCODING_DTS,
@ -144,12 +152,19 @@ public final class C {
})
public @interface Encoding {}
/**
* Represents a PCM audio encoding, or an invalid or unset value.
*/
/** Represents a PCM audio encoding, or an invalid or unset value. */
@Retention(RetentionPolicy.SOURCE)
@IntDef({Format.NO_VALUE, ENCODING_INVALID, ENCODING_PCM_8BIT, ENCODING_PCM_16BIT,
ENCODING_PCM_24BIT, ENCODING_PCM_32BIT, ENCODING_PCM_FLOAT})
@IntDef({
Format.NO_VALUE,
ENCODING_INVALID,
ENCODING_PCM_8BIT,
ENCODING_PCM_16BIT,
ENCODING_PCM_24BIT,
ENCODING_PCM_32BIT,
ENCODING_PCM_FLOAT,
ENCODING_PCM_MU_LAW,
ENCODING_PCM_A_LAW
})
public @interface PcmEncoding {}
/** @see AudioFormat#ENCODING_INVALID */
public static final int ENCODING_INVALID = AudioFormat.ENCODING_INVALID;
@ -163,6 +178,10 @@ public final class C {
public static final int ENCODING_PCM_32BIT = 0x40000000;
/** @see AudioFormat#ENCODING_PCM_FLOAT */
public static final int ENCODING_PCM_FLOAT = AudioFormat.ENCODING_PCM_FLOAT;
/** Audio encoding for mu-law. */
public static final int ENCODING_PCM_MU_LAW = 0x10000000;
/** Audio encoding for A-law. */
public static final int ENCODING_PCM_A_LAW = 0x20000000;
/** @see AudioFormat#ENCODING_AC3 */
public static final int ENCODING_AC3 = AudioFormat.ENCODING_AC3;
/** @see AudioFormat#ENCODING_E_AC3 */
@ -226,7 +245,7 @@ public final class C {
public static final int STREAM_TYPE_DEFAULT = STREAM_TYPE_MUSIC;
/**
* Content types for {@link org.telegram.messenger.exoplayer2.audio.AudioAttributes}.
* Content types for {@link com.google.android.exoplayer2.audio.AudioAttributes}.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({CONTENT_TYPE_MOVIE, CONTENT_TYPE_MUSIC, CONTENT_TYPE_SONIFICATION, CONTENT_TYPE_SPEECH,
@ -257,7 +276,7 @@ public final class C {
android.media.AudioAttributes.CONTENT_TYPE_UNKNOWN;
/**
* Flags for {@link org.telegram.messenger.exoplayer2.audio.AudioAttributes}.
* Flags for {@link com.google.android.exoplayer2.audio.AudioAttributes}.
* <p>
* Note that {@code FLAG_HW_AV_SYNC} is not available because the player takes care of setting the
* flag when tunneling is enabled via a track selector.
@ -272,7 +291,7 @@ public final class C {
android.media.AudioAttributes.FLAG_AUDIBILITY_ENFORCED;
/**
* Usage types for {@link org.telegram.messenger.exoplayer2.audio.AudioAttributes}.
* Usage types for {@link com.google.android.exoplayer2.audio.AudioAttributes}.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({USAGE_ALARM, USAGE_ASSISTANCE_ACCESSIBILITY, USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
@ -664,7 +683,7 @@ public final class C {
/**
* A type of a message that can be passed to an audio {@link Renderer} via {@link
* ExoPlayer#createMessage(Target)}. The message payload should be an {@link
* org.telegram.messenger.exoplayer2.audio.AudioAttributes} instance that will configure the
* com.google.android.exoplayer2.audio.AudioAttributes} instance that will configure the
* underlying audio track. If not set, the default audio attributes will be used. They are
* suitable for general media playback.
*
@ -797,6 +816,45 @@ public final class C {
*/
public static final int PRIORITY_DOWNLOAD = PRIORITY_PLAYBACK - 1000;
/** Network connection type. */
@Retention(RetentionPolicy.SOURCE)
@IntDef({
NETWORK_TYPE_UNKNOWN,
NETWORK_TYPE_OFFLINE,
NETWORK_TYPE_WIFI,
NETWORK_TYPE_2G,
NETWORK_TYPE_3G,
NETWORK_TYPE_4G,
NETWORK_TYPE_CELLULAR_UNKNOWN,
NETWORK_TYPE_ETHERNET,
NETWORK_TYPE_OTHER
})
public @interface NetworkType {}
/** Unknown network type. */
public static final int NETWORK_TYPE_UNKNOWN = 0;
/** No network connection. */
public static final int NETWORK_TYPE_OFFLINE = 1;
/** Network type for a Wifi connection. */
public static final int NETWORK_TYPE_WIFI = 2;
/** Network type for a 2G cellular connection. */
public static final int NETWORK_TYPE_2G = 3;
/** Network type for a 3G cellular connection. */
public static final int NETWORK_TYPE_3G = 4;
/** Network type for a 4G cellular connection. */
public static final int NETWORK_TYPE_4G = 5;
/**
* Network type for cellular connections which cannot be mapped to one of {@link
* #NETWORK_TYPE_2G}, {@link #NETWORK_TYPE_3G}, or {@link #NETWORK_TYPE_4G}.
*/
public static final int NETWORK_TYPE_CELLULAR_UNKNOWN = 6;
/** Network type for an Ethernet connection. */
public static final int NETWORK_TYPE_ETHERNET = 7;
/**
* Network type for other connections which are not Wifi or cellular (e.g. Ethernet, VPN,
* Bluetooth).
*/
public static final int NETWORK_TYPE_OTHER = 8;
/**
* Converts a time in microseconds to the corresponding time in milliseconds, preserving
* {@link #TIME_UNSET} and {@link #TIME_END_OF_SOURCE} values.

View file

@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import org.telegram.messenger.exoplayer2.Player.RepeatMode;
import com.google.android.exoplayer2.Player.RepeatMode;
/**
* Dispatches operations to the {@link Player}.

View file

@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import org.telegram.messenger.exoplayer2.Player.RepeatMode;
import com.google.android.exoplayer2.Player.RepeatMode;
/**
* Default {@link ControlDispatcher} that dispatches all operations to the player without

View file

@ -13,15 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import org.telegram.messenger.exoplayer2.source.TrackGroupArray;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelectionArray;
import org.telegram.messenger.exoplayer2.upstream.Allocator;
import org.telegram.messenger.exoplayer2.upstream.DefaultAllocator;
import org.telegram.messenger.exoplayer2.util.Assertions;
import org.telegram.messenger.exoplayer2.util.PriorityTaskManager;
import org.telegram.messenger.exoplayer2.util.Util;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.PriorityTaskManager;
import com.google.android.exoplayer2.util.Util;
/**
* The default {@link LoadControl} implementation.

View file

@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.support.annotation.Nullable;
import org.telegram.messenger.exoplayer2.util.Clock;
import org.telegram.messenger.exoplayer2.util.MediaClock;
import org.telegram.messenger.exoplayer2.util.StandaloneMediaClock;
import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.MediaClock;
import com.google.android.exoplayer2.util.StandaloneMediaClock;
/**
* Default {@link MediaClock} which uses a renderer media clock and falls back to a

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.content.Context;
import android.os.Handler;
@ -21,20 +21,20 @@ import android.os.Looper;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import android.util.Log;
import org.telegram.messenger.exoplayer2.audio.AudioCapabilities;
import org.telegram.messenger.exoplayer2.audio.AudioProcessor;
import org.telegram.messenger.exoplayer2.audio.AudioRendererEventListener;
import org.telegram.messenger.exoplayer2.audio.MediaCodecAudioRenderer;
import org.telegram.messenger.exoplayer2.drm.DrmSessionManager;
import org.telegram.messenger.exoplayer2.drm.FrameworkMediaCrypto;
import org.telegram.messenger.exoplayer2.mediacodec.MediaCodecSelector;
import org.telegram.messenger.exoplayer2.metadata.MetadataOutput;
import org.telegram.messenger.exoplayer2.metadata.MetadataRenderer;
import org.telegram.messenger.exoplayer2.text.TextOutput;
import org.telegram.messenger.exoplayer2.text.TextRenderer;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelector;
import org.telegram.messenger.exoplayer2.video.MediaCodecVideoRenderer;
import org.telegram.messenger.exoplayer2.video.VideoRendererEventListener;
import com.google.android.exoplayer2.audio.AudioCapabilities;
import com.google.android.exoplayer2.audio.AudioProcessor;
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
import com.google.android.exoplayer2.metadata.MetadataOutput;
import com.google.android.exoplayer2.metadata.MetadataRenderer;
import com.google.android.exoplayer2.text.TextOutput;
import com.google.android.exoplayer2.text.TextRenderer;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
import com.google.android.exoplayer2.video.VideoRendererEventListener;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Constructor;
@ -221,13 +221,13 @@ public class DefaultRenderersFactory implements RenderersFactory {
try {
// Full class names used for constructor args so the LINT rule triggers if any of them move.
// LINT.IfChange
Class<?> clazz = Class.forName("org.telegram.messenger.exoplayer2.ext.vp9.LibvpxVideoRenderer");
Class<?> clazz = Class.forName("com.google.android.exoplayer2.ext.vp9.LibvpxVideoRenderer");
Constructor<?> constructor =
clazz.getConstructor(
boolean.class,
long.class,
android.os.Handler.class,
org.telegram.messenger.exoplayer2.video.VideoRendererEventListener.class,
com.google.android.exoplayer2.video.VideoRendererEventListener.class,
int.class);
// LINT.ThenChange(../../../../../../../proguard-rules.txt)
Renderer renderer =
@ -288,12 +288,12 @@ public class DefaultRenderersFactory implements RenderersFactory {
try {
// Full class names used for constructor args so the LINT rule triggers if any of them move.
// LINT.IfChange
Class<?> clazz = Class.forName("org.telegram.messenger.exoplayer2.ext.opus.LibopusAudioRenderer");
Class<?> clazz = Class.forName("com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer");
Constructor<?> constructor =
clazz.getConstructor(
android.os.Handler.class,
org.telegram.messenger.exoplayer2.audio.AudioRendererEventListener.class,
org.telegram.messenger.exoplayer2.audio.AudioProcessor[].class);
com.google.android.exoplayer2.audio.AudioRendererEventListener.class,
com.google.android.exoplayer2.audio.AudioProcessor[].class);
// LINT.ThenChange(../../../../../../../proguard-rules.txt)
Renderer renderer =
(Renderer) constructor.newInstance(eventHandler, eventListener, audioProcessors);
@ -309,12 +309,12 @@ public class DefaultRenderersFactory implements RenderersFactory {
try {
// Full class names used for constructor args so the LINT rule triggers if any of them move.
// LINT.IfChange
Class<?> clazz = Class.forName("org.telegram.messenger.exoplayer2.ext.flac.LibflacAudioRenderer");
Class<?> clazz = Class.forName("com.google.android.exoplayer2.ext.flac.LibflacAudioRenderer");
Constructor<?> constructor =
clazz.getConstructor(
android.os.Handler.class,
org.telegram.messenger.exoplayer2.audio.AudioRendererEventListener.class,
org.telegram.messenger.exoplayer2.audio.AudioProcessor[].class);
com.google.android.exoplayer2.audio.AudioRendererEventListener.class,
com.google.android.exoplayer2.audio.AudioProcessor[].class);
// LINT.ThenChange(../../../../../../../proguard-rules.txt)
Renderer renderer =
(Renderer) constructor.newInstance(eventHandler, eventListener, audioProcessors);
@ -331,12 +331,12 @@ public class DefaultRenderersFactory implements RenderersFactory {
// Full class names used for constructor args so the LINT rule triggers if any of them move.
// LINT.IfChange
Class<?> clazz =
Class.forName("org.telegram.messenger.exoplayer2.ext.ffmpeg.FfmpegAudioRenderer");
Class.forName("com.google.android.exoplayer2.ext.ffmpeg.FfmpegAudioRenderer");
Constructor<?> constructor =
clazz.getConstructor(
android.os.Handler.class,
org.telegram.messenger.exoplayer2.audio.AudioRendererEventListener.class,
org.telegram.messenger.exoplayer2.audio.AudioProcessor[].class);
com.google.android.exoplayer2.audio.AudioRendererEventListener.class,
com.google.android.exoplayer2.audio.AudioProcessor[].class);
// LINT.ThenChange(../../../../../../../proguard-rules.txt)
Renderer renderer =
(Renderer) constructor.newInstance(eventHandler, eventListener, audioProcessors);

View file

@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.support.annotation.IntDef;
import org.telegram.messenger.exoplayer2.source.MediaSource;
import org.telegram.messenger.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.util.Assertions;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

View file

@ -13,24 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.os.Looper;
import android.support.annotation.Nullable;
import org.telegram.messenger.exoplayer2.audio.MediaCodecAudioRenderer;
import org.telegram.messenger.exoplayer2.metadata.MetadataRenderer;
import org.telegram.messenger.exoplayer2.source.ClippingMediaSource;
import org.telegram.messenger.exoplayer2.source.ConcatenatingMediaSource;
import org.telegram.messenger.exoplayer2.source.ExtractorMediaSource;
import org.telegram.messenger.exoplayer2.source.LoopingMediaSource;
import org.telegram.messenger.exoplayer2.source.MediaSource;
import org.telegram.messenger.exoplayer2.source.MergingMediaSource;
import org.telegram.messenger.exoplayer2.source.SingleSampleMediaSource;
import org.telegram.messenger.exoplayer2.text.TextRenderer;
import org.telegram.messenger.exoplayer2.trackselection.DefaultTrackSelector;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelector;
import org.telegram.messenger.exoplayer2.upstream.DataSource;
import org.telegram.messenger.exoplayer2.video.MediaCodecVideoRenderer;
import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer;
import com.google.android.exoplayer2.metadata.MetadataRenderer;
import com.google.android.exoplayer2.source.ClippingMediaSource;
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.LoopingMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MergingMediaSource;
import com.google.android.exoplayer2.source.SingleSampleMediaSource;
import com.google.android.exoplayer2.text.TextRenderer;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
/**
* An extensible media player that plays {@link MediaSource}s. Instances can be obtained from {@link
@ -89,12 +89,13 @@ import org.telegram.messenger.exoplayer2.video.MediaCodecVideoRenderer;
* model">
*
* <ul>
* <li>It is strongly recommended that ExoPlayer instances are created and accessed from a single
* application thread. The application's main thread is ideal. Accessing an instance from
* multiple threads is discouraged as it may cause synchronization problems.
* <li>Registered listeners are called on the thread that created the ExoPlayer instance, unless
* the thread that created the ExoPlayer instance does not have a {@link Looper}. In that
* case, registered listeners will be called on the application's main thread.
* <li>ExoPlayer instances must be accessed from the thread associated with {@link
* #getApplicationLooper()}. This Looper can be specified when creating the player, or this is
* the Looper of the thread the player is created on, or the Looper of the application's main
* thread if the player is created on a thread without Looper.
* <li>Registered listeners are called on the thread thread associated with {@link
* #getApplicationLooper()}. Note that this means registered listeners are called on the same
* thread which must be used to access the player.
* <li>An internal playback thread is responsible for playback. Injected player components such as
* Renderers, MediaSources, TrackSelectors and LoadControls are called by the player on this
* thread.
@ -178,13 +179,15 @@ public interface ExoPlayer extends Player {
@Deprecated
@RepeatMode int REPEAT_MODE_ALL = Player.REPEAT_MODE_ALL;
/**
* Gets the {@link Looper} associated with the playback thread.
*
* @return The {@link Looper} associated with the playback thread.
*/
/** Returns the {@link Looper} associated with the playback thread. */
Looper getPlaybackLooper();
/**
* Returns the {@link Looper} associated with the application thread that's used to access the
* player and on which player events are received.
*/
Looper getApplicationLooper();
/**
* Prepares the player to play the provided {@link MediaSource}. Equivalent to
* {@code prepare(mediaSource, true, true)}.
@ -239,4 +242,7 @@ public interface ExoPlayer extends Player {
* @param seekParameters The seek parameters, or {@code null} to use the defaults.
*/
void setSeekParameters(@Nullable SeekParameters seekParameters);
/** Returns the currently active {@link SeekParameters} of the player. */
SeekParameters getSeekParameters();
}

View file

@ -13,21 +13,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.content.Context;
import android.os.Looper;
import android.support.annotation.Nullable;
import org.telegram.messenger.exoplayer2.analytics.AnalyticsCollector;
import org.telegram.messenger.exoplayer2.drm.DrmSessionManager;
import org.telegram.messenger.exoplayer2.drm.FrameworkMediaCrypto;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelector;
import org.telegram.messenger.exoplayer2.util.Clock;
import com.google.android.exoplayer2.analytics.AnalyticsCollector;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.Util;
/**
* A factory for {@link ExoPlayer} instances.
*/
public final class ExoPlayerFactory {
private static @Nullable BandwidthMeter singletonBandwidthMeter;
private ExoPlayerFactory() {}
/**
@ -155,8 +161,12 @@ public final class ExoPlayerFactory {
*/
public static SimpleExoPlayer newSimpleInstance(RenderersFactory renderersFactory,
TrackSelector trackSelector, LoadControl loadControl) {
return new SimpleExoPlayer(
renderersFactory, trackSelector, loadControl, /* drmSessionManager= */ null);
return newSimpleInstance(
renderersFactory,
trackSelector,
loadControl,
/* drmSessionManager= */ null,
Util.getLooper());
}
/**
@ -173,7 +183,34 @@ public final class ExoPlayerFactory {
TrackSelector trackSelector,
LoadControl loadControl,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) {
return new SimpleExoPlayer(renderersFactory, trackSelector, loadControl, drmSessionManager);
return newSimpleInstance(
renderersFactory, trackSelector, loadControl, drmSessionManager, Util.getLooper());
}
/**
* Creates a {@link SimpleExoPlayer} instance.
*
* @param renderersFactory A factory for creating {@link Renderer}s to be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance
* will not be used for DRM protected playbacks.
* @param bandwidthMeter The {@link BandwidthMeter} that will be used by the instance.
*/
public static SimpleExoPlayer newSimpleInstance(
RenderersFactory renderersFactory,
TrackSelector trackSelector,
LoadControl loadControl,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
BandwidthMeter bandwidthMeter) {
return newSimpleInstance(
renderersFactory,
trackSelector,
loadControl,
drmSessionManager,
bandwidthMeter,
new AnalyticsCollector.Factory(),
Util.getLooper());
}
/**
@ -193,8 +230,100 @@ public final class ExoPlayerFactory {
LoadControl loadControl,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
AnalyticsCollector.Factory analyticsCollectorFactory) {
return newSimpleInstance(
renderersFactory,
trackSelector,
loadControl,
drmSessionManager,
analyticsCollectorFactory,
Util.getLooper());
}
/**
* Creates a {@link SimpleExoPlayer} instance.
*
* @param renderersFactory A factory for creating {@link Renderer}s to be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance
* will not be used for DRM protected playbacks.
* @param looper The {@link Looper} which must be used for all calls to the player and which is
* used to call listeners on.
*/
public static SimpleExoPlayer newSimpleInstance(
RenderersFactory renderersFactory,
TrackSelector trackSelector,
LoadControl loadControl,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
Looper looper) {
return newSimpleInstance(
renderersFactory,
trackSelector,
loadControl,
drmSessionManager,
new AnalyticsCollector.Factory(),
looper);
}
/**
* Creates a {@link SimpleExoPlayer} instance.
*
* @param renderersFactory A factory for creating {@link Renderer}s to be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance
* will not be used for DRM protected playbacks.
* @param analyticsCollectorFactory A factory for creating the {@link AnalyticsCollector} that
* will collect and forward all player events.
* @param looper The {@link Looper} which must be used for all calls to the player and which is
* used to call listeners on.
*/
public static SimpleExoPlayer newSimpleInstance(
RenderersFactory renderersFactory,
TrackSelector trackSelector,
LoadControl loadControl,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
AnalyticsCollector.Factory analyticsCollectorFactory,
Looper looper) {
return newSimpleInstance(
renderersFactory,
trackSelector,
loadControl,
drmSessionManager,
getDefaultBandwidthMeter(),
analyticsCollectorFactory,
looper);
}
/**
* Creates a {@link SimpleExoPlayer} instance.
*
* @param renderersFactory A factory for creating {@link Renderer}s to be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance
* will not be used for DRM protected playbacks.
* @param analyticsCollectorFactory A factory for creating the {@link AnalyticsCollector} that
* will collect and forward all player events.
* @param looper The {@link Looper} which must be used for all calls to the player and which is
* used to call listeners on.
*/
public static SimpleExoPlayer newSimpleInstance(
RenderersFactory renderersFactory,
TrackSelector trackSelector,
LoadControl loadControl,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
BandwidthMeter bandwidthMeter,
AnalyticsCollector.Factory analyticsCollectorFactory,
Looper looper) {
return new SimpleExoPlayer(
renderersFactory, trackSelector, loadControl, drmSessionManager, analyticsCollectorFactory);
renderersFactory,
trackSelector,
loadControl,
drmSessionManager,
bandwidthMeter,
analyticsCollectorFactory,
looper);
}
/**
@ -216,7 +345,47 @@ public final class ExoPlayerFactory {
*/
public static ExoPlayer newInstance(Renderer[] renderers, TrackSelector trackSelector,
LoadControl loadControl) {
return new ExoPlayerImpl(renderers, trackSelector, loadControl, Clock.DEFAULT);
return newInstance(renderers, trackSelector, loadControl, Util.getLooper());
}
/**
* Creates an {@link ExoPlayer} instance.
*
* @param renderers The {@link Renderer}s that will be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance.
* @param looper The {@link Looper} which must be used for all calls to the player and which is
* used to call listeners on.
*/
public static ExoPlayer newInstance(
Renderer[] renderers, TrackSelector trackSelector, LoadControl loadControl, Looper looper) {
return newInstance(renderers, trackSelector, loadControl, getDefaultBandwidthMeter(), looper);
}
/**
* Creates an {@link ExoPlayer} instance.
*
* @param renderers The {@link Renderer}s that will be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance.
* @param bandwidthMeter The {@link BandwidthMeter} that will be used by the instance.
* @param looper The {@link Looper} which must be used for all calls to the player and which is
* used to call listeners on.
*/
public static ExoPlayer newInstance(
Renderer[] renderers,
TrackSelector trackSelector,
LoadControl loadControl,
BandwidthMeter bandwidthMeter,
Looper looper) {
return new ExoPlayerImpl(
renderers, trackSelector, loadControl, bandwidthMeter, Clock.DEFAULT, looper);
}
private static synchronized BandwidthMeter getDefaultBandwidthMeter() {
if (singletonBandwidthMeter == null) {
singletonBandwidthMeter = new DefaultBandwidthMeter.Builder().build();
}
return singletonBandwidthMeter;
}
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.annotation.SuppressLint;
import android.os.Handler;
@ -22,19 +22,22 @@ import android.os.Message;
import android.support.annotation.Nullable;
import android.util.Log;
import android.util.Pair;
import org.telegram.messenger.exoplayer2.PlayerMessage.Target;
import org.telegram.messenger.exoplayer2.source.MediaSource;
import org.telegram.messenger.exoplayer2.source.MediaSource.MediaPeriodId;
import org.telegram.messenger.exoplayer2.source.TrackGroupArray;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelection;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelectionArray;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelector;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelectorResult;
import org.telegram.messenger.exoplayer2.util.Assertions;
import org.telegram.messenger.exoplayer2.util.Clock;
import org.telegram.messenger.exoplayer2.util.Util;
import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.Util;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
@ -53,6 +56,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
private final CopyOnWriteArraySet<Player.EventListener> listeners;
private final Timeline.Window window;
private final Timeline.Period period;
private final ArrayDeque<PlaybackInfoUpdate> pendingPlaybackInfoUpdates;
private boolean playWhenReady;
private @RepeatMode int repeatMode;
@ -61,6 +65,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
private boolean hasPendingPrepare;
private boolean hasPendingSeek;
private PlaybackParameters playbackParameters;
private SeekParameters seekParameters;
private @Nullable ExoPlaybackException playbackError;
// Playback information when there is no pending seek/set source operation.
@ -77,11 +82,19 @@ import java.util.concurrent.CopyOnWriteArraySet;
* @param renderers The {@link Renderer}s that will be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance.
* @param bandwidthMeter The {@link BandwidthMeter} that will be used by the instance.
* @param clock The {@link Clock} that will be used by the instance.
* @param looper The {@link Looper} which must be used for all calls to the player and which is
* used to call listeners on.
*/
@SuppressLint("HandlerLeak")
public ExoPlayerImpl(
Renderer[] renderers, TrackSelector trackSelector, LoadControl loadControl, Clock clock) {
Renderer[] renderers,
TrackSelector trackSelector,
LoadControl loadControl,
BandwidthMeter bandwidthMeter,
Clock clock,
Looper looper) {
Log.i(TAG, "Init " + Integer.toHexString(System.identityHashCode(this)) + " ["
+ ExoPlayerLibraryInfo.VERSION_SLASHY + "] [" + Util.DEVICE_DEBUG_INFO + "]");
Assertions.checkState(renderers.length > 0);
@ -99,25 +112,23 @@ import java.util.concurrent.CopyOnWriteArraySet;
window = new Timeline.Window();
period = new Timeline.Period();
playbackParameters = PlaybackParameters.DEFAULT;
Looper eventLooper = Looper.myLooper() != null ? Looper.myLooper() : Looper.getMainLooper();
eventHandler = new Handler(eventLooper) {
seekParameters = SeekParameters.DEFAULT;
eventHandler =
new Handler(looper) {
@Override
public void handleMessage(Message msg) {
ExoPlayerImpl.this.handleEvent(msg);
}
};
playbackInfo =
new PlaybackInfo(
Timeline.EMPTY,
/* startPositionUs= */ 0,
TrackGroupArray.EMPTY,
emptyTrackSelectorResult);
playbackInfo = PlaybackInfo.createDummy(/* startPositionUs= */ 0, emptyTrackSelectorResult);
pendingPlaybackInfoUpdates = new ArrayDeque<>();
internalPlayer =
new ExoPlayerImplInternal(
renderers,
trackSelector,
emptyTrackSelectorResult,
loadControl,
bandwidthMeter,
playWhenReady,
repeatMode,
shuffleModeEnabled,
@ -127,6 +138,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
internalPlayerHandler = new Handler(internalPlayer.getPlaybackLooper());
}
@Override
public AudioComponent getAudioComponent() {
return null;
}
@Override
public VideoComponent getVideoComponent() {
return null;
@ -142,6 +158,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
return internalPlayer.getPlaybackLooper();
}
@Override
public Looper getApplicationLooper() {
return eventHandler.getLooper();
}
@Override
public void addListener(Player.EventListener listener) {
listeners.add(listener);
@ -185,7 +206,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
/* positionDiscontinuity= */ false,
/* ignored */ DISCONTINUITY_REASON_INTERNAL,
TIMELINE_CHANGE_REASON_RESET,
/* seekProcessed= */ false);
/* seekProcessed= */ false,
/* playWhenReadyChanged= */ false);
}
@Override
@ -193,10 +215,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
if (this.playWhenReady != playWhenReady) {
this.playWhenReady = playWhenReady;
internalPlayer.setPlayWhenReady(playWhenReady);
PlaybackInfo playbackInfo = this.playbackInfo;
for (Player.EventListener listener : listeners) {
listener.onPlayerStateChanged(playWhenReady, playbackInfo.playbackState);
}
updatePlaybackInfo(
playbackInfo,
/* positionDiscontinuity= */ false,
/* ignored */ DISCONTINUITY_REASON_INTERNAL,
/* ignored */ TIMELINE_CHANGE_REASON_RESET,
/* seekProcessed= */ false,
/* playWhenReadyChanged= */ true);
}
}
@ -286,10 +311,10 @@ import java.util.concurrent.CopyOnWriteArraySet;
} else {
long windowPositionUs = positionMs == C.TIME_UNSET
? timeline.getWindow(windowIndex, window).getDefaultPositionUs() : C.msToUs(positionMs);
Pair<Integer, Long> periodIndexAndPositon =
Pair<Integer, Long> periodIndexAndPosition =
timeline.getPeriodPosition(window, period, windowIndex, windowPositionUs);
maskingWindowPositionMs = C.usToMs(windowPositionUs);
maskingPeriodIndex = periodIndexAndPositon.first;
maskingPeriodIndex = periodIndexAndPosition.first;
}
internalPlayer.seekTo(timeline, windowIndex, C.msToUs(positionMs));
for (Player.EventListener listener : listeners) {
@ -315,8 +340,16 @@ import java.util.concurrent.CopyOnWriteArraySet;
if (seekParameters == null) {
seekParameters = SeekParameters.DEFAULT;
}
if (!this.seekParameters.equals(seekParameters)) {
this.seekParameters = seekParameters;
internalPlayer.setSeekParameters(seekParameters);
}
}
@Override
public SeekParameters getSeekParameters() {
return seekParameters;
}
@Override
public @Nullable Object getCurrentTag() {
@ -352,7 +385,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
/* positionDiscontinuity= */ false,
/* ignored */ DISCONTINUITY_REASON_INTERNAL,
TIMELINE_CHANGE_REASON_RESET,
/* seekProcessed= */ false);
/* seekProcessed= */ false,
/* playWhenReadyChanged= */ false);
}
@Override
@ -461,29 +495,37 @@ import java.util.concurrent.CopyOnWriteArraySet;
public long getCurrentPosition() {
if (shouldMaskPosition()) {
return maskingWindowPositionMs;
} else if (playbackInfo.periodId.isAd()) {
return C.usToMs(playbackInfo.positionUs);
} else {
return playbackInfoPositionUsToWindowPositionMs(playbackInfo.positionUs);
return periodPositionUsToWindowPositionMs(playbackInfo.periodId, playbackInfo.positionUs);
}
}
@Override
public long getBufferedPosition() {
// TODO - Implement this properly.
if (shouldMaskPosition()) {
return maskingWindowPositionMs;
} else {
return playbackInfoPositionUsToWindowPositionMs(playbackInfo.bufferedPositionUs);
if (isPlayingAd()) {
return playbackInfo.loadingMediaPeriodId.equals(playbackInfo.periodId)
? C.usToMs(playbackInfo.bufferedPositionUs)
: getDuration();
}
return getContentBufferedPosition();
}
@Override
public int getBufferedPercentage() {
long position = getBufferedPosition();
long duration = getDuration();
return position == C.TIME_UNSET || duration == C.TIME_UNSET ? 0
return position == C.TIME_UNSET || duration == C.TIME_UNSET
? 0
: (duration == 0 ? 100 : Util.constrainValue((int) ((position * 100) / duration), 0, 100));
}
@Override
public long getTotalBufferedDuration() {
return Math.max(0, C.usToMs(playbackInfo.totalBufferedDurationUs));
}
@Override
public boolean isCurrentWindowDynamic() {
Timeline timeline = playbackInfo.timeline;
@ -521,6 +563,29 @@ import java.util.concurrent.CopyOnWriteArraySet;
}
}
@Override
public long getContentBufferedPosition() {
if (shouldMaskPosition()) {
return maskingWindowPositionMs;
}
if (playbackInfo.loadingMediaPeriodId.windowSequenceNumber
!= playbackInfo.periodId.windowSequenceNumber) {
return playbackInfo.timeline.getWindow(getCurrentWindowIndex(), window).getDurationMs();
}
long contentBufferedPositionUs = playbackInfo.bufferedPositionUs;
if (playbackInfo.loadingMediaPeriodId.isAd()) {
Timeline.Period loadingPeriod =
playbackInfo.timeline.getPeriod(playbackInfo.loadingMediaPeriodId.periodIndex, period);
contentBufferedPositionUs =
loadingPeriod.getAdGroupTimeUs(playbackInfo.loadingMediaPeriodId.adGroupIndex);
if (contentBufferedPositionUs == C.TIME_END_OF_SOURCE) {
contentBufferedPositionUs = loadingPeriod.durationUs;
}
}
return periodPositionUsToWindowPositionMs(
playbackInfo.loadingMediaPeriodId, contentBufferedPositionUs);
}
@Override
public int getRendererCount() {
return renderers.length;
@ -615,7 +680,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
positionDiscontinuity,
positionDiscontinuityReason,
timelineChangeReason,
seekProcessed);
seekProcessed,
/* playWhenReadyChanged= */ false);
}
}
@ -639,23 +705,100 @@ import java.util.concurrent.CopyOnWriteArraySet;
playbackState,
/* isLoading= */ false,
resetState ? TrackGroupArray.EMPTY : playbackInfo.trackGroups,
resetState ? emptyTrackSelectorResult : playbackInfo.trackSelectorResult);
resetState ? emptyTrackSelectorResult : playbackInfo.trackSelectorResult,
playbackInfo.periodId,
playbackInfo.startPositionUs,
/* totalBufferedDurationUs= */ 0,
playbackInfo.startPositionUs);
}
private void updatePlaybackInfo(
PlaybackInfo newPlaybackInfo,
PlaybackInfo playbackInfo,
boolean positionDiscontinuity,
@Player.DiscontinuityReason int positionDiscontinuityReason,
@Player.TimelineChangeReason int timelineChangeReason,
boolean seekProcessed) {
boolean timelineOrManifestChanged =
playbackInfo.timeline != newPlaybackInfo.timeline
|| playbackInfo.manifest != newPlaybackInfo.manifest;
boolean playbackStateChanged = playbackInfo.playbackState != newPlaybackInfo.playbackState;
boolean isLoadingChanged = playbackInfo.isLoading != newPlaybackInfo.isLoading;
boolean trackSelectorResultChanged =
playbackInfo.trackSelectorResult != newPlaybackInfo.trackSelectorResult;
playbackInfo = newPlaybackInfo;
boolean seekProcessed,
boolean playWhenReadyChanged) {
boolean isRunningRecursiveListenerNotification = !pendingPlaybackInfoUpdates.isEmpty();
pendingPlaybackInfoUpdates.addLast(
new PlaybackInfoUpdate(
playbackInfo,
/* previousPlaybackInfo= */ this.playbackInfo,
listeners,
trackSelector,
positionDiscontinuity,
positionDiscontinuityReason,
timelineChangeReason,
seekProcessed,
playWhenReady,
playWhenReadyChanged));
// Assign playback info immediately such that all getters return the right values.
this.playbackInfo = playbackInfo;
if (isRunningRecursiveListenerNotification) {
return;
}
while (!pendingPlaybackInfoUpdates.isEmpty()) {
pendingPlaybackInfoUpdates.peekFirst().notifyListeners();
pendingPlaybackInfoUpdates.removeFirst();
}
}
private long periodPositionUsToWindowPositionMs(MediaPeriodId periodId, long positionUs) {
long positionMs = C.usToMs(positionUs);
playbackInfo.timeline.getPeriod(periodId.periodIndex, period);
positionMs += period.getPositionInWindowMs();
return positionMs;
}
private boolean shouldMaskPosition() {
return playbackInfo.timeline.isEmpty() || pendingOperationAcks > 0;
}
private static final class PlaybackInfoUpdate {
private final PlaybackInfo playbackInfo;
private final Set<Player.EventListener> listeners;
private final TrackSelector trackSelector;
private final boolean positionDiscontinuity;
private final @Player.DiscontinuityReason int positionDiscontinuityReason;
private final @Player.TimelineChangeReason int timelineChangeReason;
private final boolean seekProcessed;
private final boolean playWhenReady;
private final boolean playbackStateOrPlayWhenReadyChanged;
private final boolean timelineOrManifestChanged;
private final boolean isLoadingChanged;
private final boolean trackSelectorResultChanged;
public PlaybackInfoUpdate(
PlaybackInfo playbackInfo,
PlaybackInfo previousPlaybackInfo,
Set<Player.EventListener> listeners,
TrackSelector trackSelector,
boolean positionDiscontinuity,
@Player.DiscontinuityReason int positionDiscontinuityReason,
@Player.TimelineChangeReason int timelineChangeReason,
boolean seekProcessed,
boolean playWhenReady,
boolean playWhenReadyChanged) {
this.playbackInfo = playbackInfo;
this.listeners = listeners;
this.trackSelector = trackSelector;
this.positionDiscontinuity = positionDiscontinuity;
this.positionDiscontinuityReason = positionDiscontinuityReason;
this.timelineChangeReason = timelineChangeReason;
this.seekProcessed = seekProcessed;
this.playWhenReady = playWhenReady;
playbackStateOrPlayWhenReadyChanged =
playWhenReadyChanged || previousPlaybackInfo.playbackState != playbackInfo.playbackState;
timelineOrManifestChanged =
previousPlaybackInfo.timeline != playbackInfo.timeline
|| previousPlaybackInfo.manifest != playbackInfo.manifest;
isLoadingChanged = previousPlaybackInfo.isLoading != playbackInfo.isLoading;
trackSelectorResultChanged =
previousPlaybackInfo.trackSelectorResult != playbackInfo.trackSelectorResult;
}
public void notifyListeners() {
if (timelineOrManifestChanged || timelineChangeReason == TIMELINE_CHANGE_REASON_PREPARED) {
for (Player.EventListener listener : listeners) {
listener.onTimelineChanged(
@ -679,7 +822,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
listener.onLoadingChanged(playbackInfo.isLoading);
}
}
if (playbackStateChanged) {
if (playbackStateOrPlayWhenReadyChanged) {
for (Player.EventListener listener : listeners) {
listener.onPlayerStateChanged(playWhenReady, playbackInfo.playbackState);
}
@ -690,17 +833,5 @@ import java.util.concurrent.CopyOnWriteArraySet;
}
}
}
private long playbackInfoPositionUsToWindowPositionMs(long positionUs) {
long positionMs = C.usToMs(positionUs);
if (!playbackInfo.periodId.isAd()) {
playbackInfo.timeline.getPeriod(playbackInfo.periodId.periodIndex, period);
positionMs += period.getPositionInWindowMs();
}
return positionMs;
}
private boolean shouldMaskPosition() {
return playbackInfo.timeline.isEmpty() || pendingOperationAcks > 0;
}
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.os.Handler;
import android.os.HandlerThread;
@ -25,21 +25,22 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.util.Pair;
import org.telegram.messenger.exoplayer2.DefaultMediaClock.PlaybackParameterListener;
import org.telegram.messenger.exoplayer2.Player.DiscontinuityReason;
import org.telegram.messenger.exoplayer2.source.MediaPeriod;
import org.telegram.messenger.exoplayer2.source.MediaSource;
import org.telegram.messenger.exoplayer2.source.MediaSource.MediaPeriodId;
import org.telegram.messenger.exoplayer2.source.SampleStream;
import org.telegram.messenger.exoplayer2.source.TrackGroupArray;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelection;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelector;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelectorResult;
import org.telegram.messenger.exoplayer2.util.Assertions;
import org.telegram.messenger.exoplayer2.util.Clock;
import org.telegram.messenger.exoplayer2.util.HandlerWrapper;
import org.telegram.messenger.exoplayer2.util.TraceUtil;
import org.telegram.messenger.exoplayer2.util.Util;
import com.google.android.exoplayer2.DefaultMediaClock.PlaybackParameterListener;
import com.google.android.exoplayer2.Player.DiscontinuityReason;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.HandlerWrapper;
import com.google.android.exoplayer2.util.TraceUtil;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@ -77,6 +78,7 @@ import java.util.Collections;
private static final int MSG_SET_SHUFFLE_ENABLED = 13;
private static final int MSG_SEND_MESSAGE = 14;
private static final int MSG_SEND_MESSAGE_TO_TARGET_THREAD = 15;
private static final int MSG_PLAYBACK_PARAMETERS_CHANGED_INTERNAL = 16;
private static final int PREPARING_SOURCE_INTERVAL_MS = 10;
private static final int RENDERING_INTERVAL_MS = 10;
@ -87,6 +89,7 @@ import java.util.Collections;
private final TrackSelector trackSelector;
private final TrackSelectorResult emptyTrackSelectorResult;
private final LoadControl loadControl;
private final BandwidthMeter bandwidthMeter;
private final HandlerWrapper handler;
private final HandlerThread internalPlaybackThread;
private final Handler eventHandler;
@ -123,6 +126,7 @@ import java.util.Collections;
TrackSelector trackSelector,
TrackSelectorResult emptyTrackSelectorResult,
LoadControl loadControl,
BandwidthMeter bandwidthMeter,
boolean playWhenReady,
@Player.RepeatMode int repeatMode,
boolean shuffleModeEnabled,
@ -133,6 +137,7 @@ import java.util.Collections;
this.trackSelector = trackSelector;
this.emptyTrackSelectorResult = emptyTrackSelectorResult;
this.loadControl = loadControl;
this.bandwidthMeter = bandwidthMeter;
this.playWhenReady = playWhenReady;
this.repeatMode = repeatMode;
this.shuffleModeEnabled = shuffleModeEnabled;
@ -146,11 +151,7 @@ import java.util.Collections;
seekParameters = SeekParameters.DEFAULT;
playbackInfo =
new PlaybackInfo(
Timeline.EMPTY,
/* startPositionUs= */ C.TIME_UNSET,
TrackGroupArray.EMPTY,
emptyTrackSelectorResult);
PlaybackInfo.createDummy(/* startPositionUs= */ C.TIME_UNSET, emptyTrackSelectorResult);
playbackInfoUpdate = new PlaybackInfoUpdate();
rendererCapabilities = new RendererCapabilities[renderers.length];
for (int i = 0; i < renderers.length; i++) {
@ -162,7 +163,7 @@ import java.util.Collections;
enabledRenderers = new Renderer[0];
window = new Timeline.Window();
period = new Timeline.Period();
trackSelector.init(this);
trackSelector.init(/* listener= */ this, bandwidthMeter);
// Note: The documentation for Process.THREAD_PRIORITY_AUDIO that states "Applications can
// not normally change to this priority" is incorrect.
@ -271,8 +272,9 @@ import java.util.Collections;
@Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
eventHandler.obtainMessage(MSG_PLAYBACK_PARAMETERS_CHANGED, playbackParameters).sendToTarget();
updateTrackSelectionPlaybackSpeed(playbackParameters.speed);
handler
.obtainMessage(MSG_PLAYBACK_PARAMETERS_CHANGED_INTERNAL, playbackParameters)
.sendToTarget();
}
// Handler.Callback implementation.
@ -324,6 +326,9 @@ import java.util.Collections;
case MSG_TRACK_SELECTION_INVALIDATED:
reselectTracksInternal();
break;
case MSG_PLAYBACK_PARAMETERS_CHANGED_INTERNAL:
handlePlaybackParameters((PlaybackParameters) msg.obj);
break;
case MSG_SEND_MESSAGE:
sendMessageInternal((PlayerMessage) msg.obj);
break;
@ -393,7 +398,11 @@ import java.util.Collections;
loadControl.onPrepared();
this.mediaSource = mediaSource;
setState(Player.STATE_BUFFERING);
mediaSource.prepareSource(player, /* isTopLevelSource= */ true, /* listener= */ this);
mediaSource.prepareSource(
player,
/* isTopLevelSource= */ true,
/* listener= */ this,
bandwidthMeter.getTransferListener());
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
}
@ -419,6 +428,7 @@ import java.util.Collections;
if (!queue.updateRepeatMode(repeatMode)) {
seekToCurrentPosition(/* sendDiscontinuity= */ true);
}
updateLoadingMediaPeriodId();
}
private void setShuffleModeEnabledInternal(boolean shuffleModeEnabled)
@ -427,6 +437,7 @@ import java.util.Collections;
if (!queue.updateShuffleModeEnabled(shuffleModeEnabled)) {
seekToCurrentPosition(/* sendDiscontinuity= */ true);
}
updateLoadingMediaPeriodId();
}
private void seekToCurrentPosition(boolean sendDiscontinuity) throws ExoPlaybackException {
@ -483,11 +494,12 @@ import java.util.Collections;
playbackInfo.positionUs = periodPositionUs;
}
// Update the buffered position.
// Update the buffered position and total buffered duration.
MediaPeriodHolder loadingPeriod = queue.getLoadingPeriod();
playbackInfo.bufferedPositionUs =
enabledRenderers.length == 0
? playingPeriodHolder.info.durationUs
: playingPeriodHolder.getBufferedPositionUs(/* convertEosToDuration= */ true);
loadingPeriod.getBufferedPositionUs(/* convertEosToDuration= */ true);
playbackInfo.totalBufferedDurationUs =
playbackInfo.bufferedPositionUs - loadingPeriod.toPeriodTime(rendererPositionUs);
}
private void doSomeWork() throws ExoPlaybackException, IOException {
@ -587,7 +599,7 @@ import java.util.Collections;
if (resolvedSeekPosition == null) {
// The seek position was valid for the timeline that it was performed into, but the
// timeline has changed or is not ready and a suitable seek position could not be resolved.
periodId = new MediaPeriodId(getFirstPeriodIndex());
periodId = getFirstMediaPeriodId();
periodPositionUs = C.TIME_UNSET;
contentPositionUs = C.TIME_UNSET;
seekPositionAdjusted = true;
@ -660,7 +672,7 @@ import java.util.Collections;
MediaPeriodHolder oldPlayingPeriodHolder = queue.getPlayingPeriod();
MediaPeriodHolder newPlayingPeriodHolder = oldPlayingPeriodHolder;
while (newPlayingPeriodHolder != null) {
if (shouldKeepPeriodHolder(periodId, periodPositionUs, newPlayingPeriodHolder)) {
if (periodId.equals(newPlayingPeriodHolder.info.id) && newPlayingPeriodHolder.prepared) {
queue.removeAfter(newPlayingPeriodHolder);
break;
}
@ -691,23 +703,11 @@ import java.util.Collections;
resetRendererPosition(periodPositionUs);
}
updateLoadingMediaPeriodId();
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
return periodPositionUs;
}
private boolean shouldKeepPeriodHolder(
MediaPeriodId seekPeriodId, long positionUs, MediaPeriodHolder holder) {
if (seekPeriodId.equals(holder.info.id) && holder.prepared) {
playbackInfo.timeline.getPeriod(holder.info.id.periodIndex, period);
int nextAdGroupIndex = period.getAdGroupIndexAfterPositionUs(positionUs);
if (nextAdGroupIndex == C.INDEX_UNSET
|| period.getAdGroupTimeUs(nextAdGroupIndex) == holder.info.endPositionUs) {
return true;
}
}
return false;
}
private void resetRendererPosition(long periodPositionUs) throws ExoPlaybackException {
rendererPositionUs =
!queue.hasPlayingPeriod()
@ -749,12 +749,15 @@ import java.util.Collections;
}
}
private int getFirstPeriodIndex() {
private MediaPeriodId getFirstMediaPeriodId() {
Timeline timeline = playbackInfo.timeline;
return timeline.isEmpty()
? 0
: timeline.getWindow(timeline.getFirstWindowIndex(shuffleModeEnabled), window)
if (timeline.isEmpty()) {
return PlaybackInfo.DUMMY_MEDIA_PERIOD_ID;
}
int firstPeriodIndex =
timeline.getWindow(timeline.getFirstWindowIndex(shuffleModeEnabled), window)
.firstPeriodIndex;
return new MediaPeriodId(firstPeriodIndex);
}
private void resetInternal(
@ -785,18 +788,25 @@ import java.util.Collections;
pendingMessages.clear();
nextPendingMessageIndex = 0;
}
// Set the start position to TIME_UNSET so that a subsequent seek to 0 isn't ignored.
MediaPeriodId mediaPeriodId = resetPosition ? getFirstMediaPeriodId() : playbackInfo.periodId;
long startPositionUs = resetPosition ? C.TIME_UNSET : playbackInfo.positionUs;
long contentPositionUs = resetPosition ? C.TIME_UNSET : playbackInfo.contentPositionUs;
playbackInfo =
new PlaybackInfo(
resetState ? Timeline.EMPTY : playbackInfo.timeline,
resetState ? null : playbackInfo.manifest,
resetPosition ? new MediaPeriodId(getFirstPeriodIndex()) : playbackInfo.periodId,
// Set the start position to TIME_UNSET so that a subsequent seek to 0 isn't ignored.
resetPosition ? C.TIME_UNSET : playbackInfo.positionUs,
resetPosition ? C.TIME_UNSET : playbackInfo.contentPositionUs,
mediaPeriodId,
startPositionUs,
contentPositionUs,
playbackInfo.playbackState,
/* isLoading= */ false,
resetState ? TrackGroupArray.EMPTY : playbackInfo.trackGroups,
resetState ? emptyTrackSelectorResult : playbackInfo.trackSelectorResult);
resetState ? emptyTrackSelectorResult : playbackInfo.trackSelectorResult,
mediaPeriodId,
startPositionUs,
/* totalBufferedDurationUs= */ 0,
startPositionUs);
if (releaseMediaSource) {
if (mediaSource != null) {
mediaSource.releaseSource(/* listener= */ this);
@ -892,7 +902,7 @@ import java.util.Collections;
pendingMessageInfo.setResolvedPosition(
periodPosition.first,
periodPosition.second,
playbackInfo.timeline.getPeriod(periodPosition.first, period, true).uid);
playbackInfo.timeline.getUidOfPeriod(periodPosition.first));
} else {
// Position has been resolved for a previous timeline. Try to find the updated period index.
int index = playbackInfo.timeline.getIndexOfPeriod(pendingMessageInfo.resolvedPeriodUid);
@ -1051,6 +1061,7 @@ import java.util.Collections;
updateLoadControlTrackSelection(periodHolder.trackGroups, periodHolder.trackSelectorResult);
}
}
updateLoadingMediaPeriodId();
if (playbackInfo.playbackState != Player.STATE_ENDED) {
maybeContinueLoading();
updatePlaybackPositions();
@ -1142,8 +1153,15 @@ import java.util.Collections;
playbackInfoUpdate.incrementPendingOperationAcks(pendingPrepareCount);
pendingPrepareCount = 0;
if (pendingInitialSeekPosition != null) {
Pair<Integer, Long> periodPosition =
Pair<Integer, Long> periodPosition;
try {
periodPosition =
resolveSeekPosition(pendingInitialSeekPosition, /* trySubsequentPeriods= */ true);
} catch (IllegalSeekPositionException e) {
playbackInfo =
playbackInfo.fromNewPosition(getFirstMediaPeriodId(), C.TIME_UNSET, C.TIME_UNSET);
throw e;
}
pendingInitialSeekPosition = null;
if (periodPosition == null) {
// The seek position was valid for the timeline that it was performed into, but the
@ -1176,22 +1194,28 @@ import java.util.Collections;
return;
}
int playingPeriodIndex = playbackInfo.periodId.periodIndex;
long contentPositionUs = playbackInfo.contentPositionUs;
if (oldTimeline.isEmpty()) {
// If the old timeline is empty, the period queue is also empty.
if (!timeline.isEmpty()) {
MediaPeriodId periodId =
queue.resolveMediaPeriodIdForAds(playingPeriodIndex, contentPositionUs);
Pair<Integer, Long> defaultPosition =
getPeriodPosition(
timeline, timeline.getFirstWindowIndex(shuffleModeEnabled), C.TIME_UNSET);
int periodIndex = defaultPosition.first;
long startPositionUs = defaultPosition.second;
MediaPeriodId periodId = queue.resolveMediaPeriodIdForAds(periodIndex, startPositionUs);
playbackInfo =
playbackInfo.fromNewPosition(
periodId, periodId.isAd() ? 0 : contentPositionUs, contentPositionUs);
periodId,
/* startPositionUs= */ periodId.isAd() ? 0 : startPositionUs,
/* contentPositionUs= */ startPositionUs);
}
return;
}
MediaPeriodHolder periodHolder = queue.getFrontPeriod();
Object playingPeriodUid = periodHolder == null
? oldTimeline.getPeriod(playingPeriodIndex, period, true).uid : periodHolder.uid;
int playingPeriodIndex = playbackInfo.periodId.periodIndex;
long contentPositionUs = playbackInfo.contentPositionUs;
Object playingPeriodUid =
periodHolder == null ? oldTimeline.getUidOfPeriod(playingPeriodIndex) : periodHolder.uid;
int periodIndex = timeline.getIndexOfPeriod(playingPeriodUid);
if (periodIndex == C.INDEX_UNSET) {
// We didn't find the current period in the new timeline. Attempt to resolve a subsequent
@ -1208,11 +1232,10 @@ import java.util.Collections;
newPeriodIndex = defaultPosition.first;
contentPositionUs = defaultPosition.second;
MediaPeriodId periodId = queue.resolveMediaPeriodIdForAds(newPeriodIndex, contentPositionUs);
timeline.getPeriod(newPeriodIndex, period, true);
if (periodHolder != null) {
// Clear the index of each holder that doesn't contain the default position. If a holder
// contains the default position then update its index so it can be re-used when seeking.
Object newPeriodUid = period.uid;
Object newPeriodUid = timeline.getUidOfPeriod(newPeriodIndex);
periodHolder.info = periodHolder.info.copyWithPeriodIndex(C.INDEX_UNSET);
while (periodHolder.next != null) {
periodHolder = periodHolder.next;
@ -1249,6 +1272,7 @@ import java.util.Collections;
if (!queue.updateQueuedPeriods(playingPeriodId, rendererPositionUs)) {
seekToCurrentPosition(/* sendDiscontinuity= */ false);
}
updateLoadingMediaPeriodId();
}
private void handleSourceInfoRefreshEndedPlayback() {
@ -1279,8 +1303,7 @@ import java.util.Collections;
// We've reached the end of the old timeline.
break;
}
newPeriodIndex = newTimeline.getIndexOfPeriod(
oldTimeline.getPeriod(oldPeriodIndex, period, true).uid);
newPeriodIndex = newTimeline.getIndexOfPeriod(oldTimeline.getUidOfPeriod(oldPeriodIndex));
}
return newPeriodIndex;
}
@ -1324,8 +1347,7 @@ import java.util.Collections;
return periodPosition;
}
// Attempt to find the mapped period in the internal timeline.
int periodIndex = timeline.getIndexOfPeriod(
seekTimeline.getPeriod(periodPosition.first, period, true).uid);
int periodIndex = timeline.getIndexOfPeriod(seekTimeline.getUidOfPeriod(periodPosition.first));
if (periodIndex != C.INDEX_UNSET) {
// We successfully located the period in the internal timeline.
return Pair.create(periodIndex, periodPosition.second);
@ -1483,7 +1505,7 @@ import java.util.Collections;
if (info == null) {
mediaSource.maybeThrowSourceInfoRefreshError();
} else {
Object uid = playbackInfo.timeline.getPeriod(info.id.periodIndex, period, true).uid;
Object uid = playbackInfo.timeline.getUidOfPeriod(info.id.periodIndex);
MediaPeriod mediaPeriod =
queue.enqueueNextMediaPeriod(
rendererCapabilities,
@ -1494,6 +1516,7 @@ import java.util.Collections;
info);
mediaPeriod.prepare(this, info.startPositionUs);
setIsLoading(true);
updateLoadingMediaPeriodId();
}
}
}
@ -1525,6 +1548,17 @@ import java.util.Collections;
maybeContinueLoading();
}
private void handlePlaybackParameters(PlaybackParameters playbackParameters)
throws ExoPlaybackException {
eventHandler.obtainMessage(MSG_PLAYBACK_PARAMETERS_CHANGED, playbackParameters).sendToTarget();
updateTrackSelectionPlaybackSpeed(playbackParameters.speed);
for (Renderer renderer : renderers) {
if (renderer != null) {
renderer.setOperatingRate(playbackParameters.speed);
}
}
}
private void maybeContinueLoading() {
MediaPeriodHolder loadingPeriodHolder = queue.getLoadingPeriod();
long nextLoadPositionUs = loadingPeriodHolder.getNextLoadPositionUs();
@ -1543,6 +1577,7 @@ import java.util.Collections;
}
}
@SuppressWarnings("ParameterNotNullable")
private void updatePlayingPeriodRenderers(@Nullable MediaPeriodHolder oldPlayingPeriodHolder)
throws ExoPlaybackException {
MediaPeriodHolder newPlayingPeriodHolder = queue.getPlayingPeriod();
@ -1619,6 +1654,13 @@ import java.util.Collections;
&& renderer.hasReadStreamToEnd();
}
private void updateLoadingMediaPeriodId() {
MediaPeriodHolder loadingMediaPeriodHolder = queue.getLoadingPeriod();
MediaPeriodId loadingMediaPeriodId =
loadingMediaPeriodHolder == null ? playbackInfo.periodId : loadingMediaPeriodHolder.info.id;
playbackInfo = playbackInfo.copyWithLoadingMediaPeriodId(loadingMediaPeriodId);
}
@NonNull
private static Format[] getFormats(TrackSelection newSelection) {
// Build an array of formats contained by the selection.

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import java.util.HashSet;
@ -29,11 +29,11 @@ public final class ExoPlayerLibraryInfo {
/** 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.
public static final String VERSION = "2.8.1";
public static final String VERSION = "2.8.3";
/** The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
public static final String VERSION_SLASHY = "ExoPlayerLib/2.8.1";
public static final String VERSION_SLASHY = "ExoPlayerLib/2.8.3";
/**
* The version of the library expressed as an integer, for example 1002003.
@ -43,16 +43,19 @@ public final class ExoPlayerLibraryInfo {
* integer version 123045006 (123-045-006).
*/
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
public static final int VERSION_INT = 2008001;
public static final int VERSION_INT = 2008003;
/**
* Whether the library was compiled with {@link org.telegram.messenger.exoplayer2.util.Assertions}
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}
* checks enabled.
*/
public static final boolean ASSERTIONS_ENABLED = true;
/** Whether an exception should be thrown in case of an OpenGl error. */
public static final boolean GL_ASSERTIONS_ENABLED = false;
/**
* Whether the library was compiled with {@link org.telegram.messenger.exoplayer2.util.TraceUtil}
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.TraceUtil}
* trace enabled.
*/
public static final boolean TRACE_ENABLED = true;

View file

@ -13,16 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.support.annotation.Nullable;
/**
* Holds a {@link Format}.
*/
public final class FormatHolder {
/**
* The held {@link Format}.
*/
public Format format;
/** The held {@link Format}. */
public @Nullable Format format;
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
/**
* Thrown when an attempt is made to seek to a position that does not exist in the player's

View file

@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import org.telegram.messenger.exoplayer2.source.TrackGroup;
import org.telegram.messenger.exoplayer2.source.TrackGroupArray;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelectionArray;
import org.telegram.messenger.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.Allocator;
/**
* Controls buffering of media.

View file

@ -13,21 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.util.Log;
import org.telegram.messenger.exoplayer2.source.ClippingMediaPeriod;
import org.telegram.messenger.exoplayer2.source.EmptySampleStream;
import org.telegram.messenger.exoplayer2.source.MediaPeriod;
import org.telegram.messenger.exoplayer2.source.MediaSource;
import org.telegram.messenger.exoplayer2.source.SampleStream;
import org.telegram.messenger.exoplayer2.source.TrackGroupArray;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelection;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelectionArray;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelector;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelectorResult;
import org.telegram.messenger.exoplayer2.upstream.Allocator;
import org.telegram.messenger.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.source.ClippingMediaPeriod;
import com.google.android.exoplayer2.source.EmptySampleStream;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.util.Assertions;
/** Holds a {@link MediaPeriod} with information required to play it as part of a timeline. */
/* package */ final class MediaPeriodHolder {
@ -82,13 +82,13 @@ import org.telegram.messenger.exoplayer2.util.Assertions;
sampleStreams = new SampleStream[rendererCapabilities.length];
mayRetainStreamFlags = new boolean[rendererCapabilities.length];
MediaPeriod mediaPeriod = mediaSource.createPeriod(info.id, allocator);
if (info.endPositionUs != C.TIME_END_OF_SOURCE) {
if (info.id.endPositionUs != C.TIME_END_OF_SOURCE) {
mediaPeriod =
new ClippingMediaPeriod(
mediaPeriod,
/* enableInitialDiscontinuity= */ true,
/* startUs= */ 0,
info.endPositionUs);
info.id.endPositionUs);
}
this.mediaPeriod = mediaPeriod;
}
@ -127,7 +127,8 @@ import org.telegram.messenger.exoplayer2.util.Assertions;
if (!prepared) {
return info.startPositionUs;
}
long bufferedPositionUs = mediaPeriod.getBufferedPositionUs();
long bufferedPositionUs =
hasEnabledTracks ? mediaPeriod.getBufferedPositionUs() : C.TIME_END_OF_SOURCE;
return bufferedPositionUs == C.TIME_END_OF_SOURCE && convertEosToDuration
? info.durationUs
: bufferedPositionUs;
@ -218,7 +219,7 @@ import org.telegram.messenger.exoplayer2.util.Assertions;
public void release() {
updatePeriodTrackSelectorResult(null);
try {
if (info.endPositionUs != C.TIME_END_OF_SOURCE) {
if (info.id.endPositionUs != C.TIME_END_OF_SOURCE) {
mediaSource.releasePeriod(((ClippingMediaPeriod) mediaPeriod).mediaPeriod);
} else {
mediaSource.releasePeriod(mediaPeriod);

View file

@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import org.telegram.messenger.exoplayer2.source.MediaPeriod;
import org.telegram.messenger.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
/** Stores the information required to load and play a {@link MediaPeriod}. */
/* package */ final class MediaPeriodInfo {
@ -25,18 +25,13 @@ import org.telegram.messenger.exoplayer2.source.MediaSource.MediaPeriodId;
public final MediaPeriodId id;
/** The start position of the media to play within the media period, in microseconds. */
public final long startPositionUs;
/**
* The end position of the media to play within the media period, in microseconds, or {@link
* C#TIME_END_OF_SOURCE} if the end position is the end of the media period.
*/
public final long endPositionUs;
/**
* If this is an ad, the position to play in the next content media period. {@link C#TIME_UNSET}
* otherwise.
*/
public final long contentPositionUs;
/**
* The duration of the media period, like {@link #endPositionUs} but with {@link
* The duration of the media period, like {@link MediaPeriodId#endPositionUs} but with {@link
* C#TIME_END_OF_SOURCE} resolved to the timeline period duration. May be {@link C#TIME_UNSET} if
* the end position is not known.
*/
@ -55,14 +50,12 @@ import org.telegram.messenger.exoplayer2.source.MediaSource.MediaPeriodId;
MediaPeriodInfo(
MediaPeriodId id,
long startPositionUs,
long endPositionUs,
long contentPositionUs,
long durationUs,
boolean isLastInTimelinePeriod,
boolean isFinal) {
this.id = id;
this.startPositionUs = startPositionUs;
this.endPositionUs = endPositionUs;
this.contentPositionUs = contentPositionUs;
this.durationUs = durationUs;
this.isLastInTimelinePeriod = isLastInTimelinePeriod;
@ -77,7 +70,6 @@ import org.telegram.messenger.exoplayer2.source.MediaSource.MediaPeriodId;
return new MediaPeriodInfo(
id.copyWithPeriodIndex(periodIndex),
startPositionUs,
endPositionUs,
contentPositionUs,
durationUs,
isLastInTimelinePeriod,
@ -89,7 +81,6 @@ import org.telegram.messenger.exoplayer2.source.MediaSource.MediaPeriodId;
return new MediaPeriodInfo(
id,
startPositionUs,
endPositionUs,
contentPositionUs,
durationUs,
isLastInTimelinePeriod,

View file

@ -13,17 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.support.annotation.Nullable;
import android.util.Pair;
import org.telegram.messenger.exoplayer2.Player.RepeatMode;
import org.telegram.messenger.exoplayer2.source.MediaPeriod;
import org.telegram.messenger.exoplayer2.source.MediaSource;
import org.telegram.messenger.exoplayer2.source.MediaSource.MediaPeriodId;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelector;
import org.telegram.messenger.exoplayer2.upstream.Allocator;
import org.telegram.messenger.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.Player.RepeatMode;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.util.Assertions;
/**
* Holds a queue of media periods, from the currently playing media period at the front to the
@ -47,17 +47,18 @@ import org.telegram.messenger.exoplayer2.util.Assertions;
private Timeline timeline;
private @RepeatMode int repeatMode;
private boolean shuffleModeEnabled;
private MediaPeriodHolder playing;
private MediaPeriodHolder reading;
private MediaPeriodHolder loading;
private @Nullable MediaPeriodHolder playing;
private @Nullable MediaPeriodHolder reading;
private @Nullable MediaPeriodHolder loading;
private int length;
private Object oldFrontPeriodUid;
private @Nullable Object oldFrontPeriodUid;
private long oldFrontPeriodWindowSequenceNumber;
/** Creates a new media period queue. */
public MediaPeriodQueue() {
period = new Timeline.Period();
window = new Timeline.Window();
timeline = Timeline.EMPTY;
}
/**
@ -228,11 +229,13 @@ import org.telegram.messenger.exoplayer2.util.Assertions;
reading = playing.next;
}
playing.release();
playing = playing.next;
length--;
if (length == 0) {
loading = null;
oldFrontPeriodUid = playing.uid;
oldFrontPeriodWindowSequenceNumber = playing.info.id.windowSequenceNumber;
}
playing = playing.next;
} else {
playing = loading;
reading = loading;
@ -312,7 +315,7 @@ import org.telegram.messenger.exoplayer2.util.Assertions;
} else {
// Check this period holder still follows the previous one, based on the new timeline.
if (periodIndex == C.INDEX_UNSET
|| !periodHolder.uid.equals(timeline.getPeriod(periodIndex, period, true).uid)) {
|| !periodHolder.uid.equals(timeline.getUidOfPeriod(periodIndex))) {
// The holder uid is inconsistent with the new timeline.
return !removeAfter(previousPeriodHolder);
}
@ -389,7 +392,12 @@ import org.telegram.messenger.exoplayer2.util.Assertions;
timeline.getPeriod(periodIndex, period);
int adGroupIndex = period.getAdGroupIndexForPositionUs(positionUs);
if (adGroupIndex == C.INDEX_UNSET) {
return new MediaPeriodId(periodIndex, windowSequenceNumber);
int nextAdGroupIndex = period.getAdGroupIndexAfterPositionUs(positionUs);
long endPositionUs =
nextAdGroupIndex == C.INDEX_UNSET
? C.TIME_END_OF_SOURCE
: period.getAdGroupTimeUs(nextAdGroupIndex);
return new MediaPeriodId(periodIndex, windowSequenceNumber, endPositionUs);
} else {
int adIndexInAdGroup = period.getFirstAdIndexToPlay(adGroupIndex);
return new MediaPeriodId(periodIndex, adGroupIndex, adIndexInAdGroup, windowSequenceNumber);
@ -448,7 +456,6 @@ import org.telegram.messenger.exoplayer2.util.Assertions;
private boolean canKeepMediaPeriodHolder(MediaPeriodHolder periodHolder, MediaPeriodInfo info) {
MediaPeriodInfo periodHolderInfo = periodHolder.info;
return periodHolderInfo.startPositionUs == info.startPositionUs
&& periodHolderInfo.endPositionUs == info.endPositionUs
&& periodHolderInfo.id.equals(info.id);
}
@ -591,14 +598,14 @@ import org.telegram.messenger.exoplayer2.util.Assertions;
mediaPeriodInfo.contentPositionUs,
currentPeriodId.windowSequenceNumber);
}
} else if (mediaPeriodInfo.endPositionUs != C.TIME_END_OF_SOURCE) {
} else if (mediaPeriodInfo.id.endPositionUs != C.TIME_END_OF_SOURCE) {
// Play the next ad group if it's available.
int nextAdGroupIndex = period.getAdGroupIndexForPositionUs(mediaPeriodInfo.endPositionUs);
int nextAdGroupIndex = period.getAdGroupIndexForPositionUs(mediaPeriodInfo.id.endPositionUs);
if (nextAdGroupIndex == C.INDEX_UNSET) {
// The next ad group can't be played. Play content from the ad group position instead.
return getMediaPeriodInfoForContent(
currentPeriodId.periodIndex,
mediaPeriodInfo.endPositionUs,
mediaPeriodInfo.id.endPositionUs,
currentPeriodId.windowSequenceNumber);
}
int adIndexInAdGroup = period.getFirstAdIndexToPlay(nextAdGroupIndex);
@ -608,7 +615,7 @@ import org.telegram.messenger.exoplayer2.util.Assertions;
currentPeriodId.periodIndex,
nextAdGroupIndex,
adIndexInAdGroup,
mediaPeriodInfo.endPositionUs,
mediaPeriodInfo.id.endPositionUs,
currentPeriodId.windowSequenceNumber);
} else {
// Check if the postroll ad should be played.
@ -637,18 +644,18 @@ import org.telegram.messenger.exoplayer2.util.Assertions;
private MediaPeriodInfo getUpdatedMediaPeriodInfo(MediaPeriodInfo info, MediaPeriodId newId) {
long startPositionUs = info.startPositionUs;
long endPositionUs = info.endPositionUs;
boolean isLastInPeriod = isLastInPeriod(newId, endPositionUs);
boolean isLastInPeriod = isLastInPeriod(newId);
boolean isLastInTimeline = isLastInTimeline(newId, isLastInPeriod);
timeline.getPeriod(newId.periodIndex, period);
long durationUs =
newId.isAd()
? period.getAdDurationUs(newId.adGroupIndex, newId.adIndexInAdGroup)
: (endPositionUs == C.TIME_END_OF_SOURCE ? period.getDurationUs() : endPositionUs);
: (newId.endPositionUs == C.TIME_END_OF_SOURCE
? period.getDurationUs()
: newId.endPositionUs);
return new MediaPeriodInfo(
newId,
startPositionUs,
endPositionUs,
info.contentPositionUs,
durationUs,
isLastInPeriod,
@ -681,7 +688,7 @@ import org.telegram.messenger.exoplayer2.util.Assertions;
long windowSequenceNumber) {
MediaPeriodId id =
new MediaPeriodId(periodIndex, adGroupIndex, adIndexInAdGroup, windowSequenceNumber);
boolean isLastInPeriod = isLastInPeriod(id, C.TIME_END_OF_SOURCE);
boolean isLastInPeriod = isLastInPeriod(id);
boolean isLastInTimeline = isLastInTimeline(id, isLastInPeriod);
long durationUs =
timeline
@ -694,7 +701,6 @@ import org.telegram.messenger.exoplayer2.util.Assertions;
return new MediaPeriodInfo(
id,
startPositionUs,
C.TIME_END_OF_SOURCE,
contentPositionUs,
durationUs,
isLastInPeriod,
@ -703,21 +709,22 @@ import org.telegram.messenger.exoplayer2.util.Assertions;
private MediaPeriodInfo getMediaPeriodInfoForContent(
int periodIndex, long startPositionUs, long windowSequenceNumber) {
MediaPeriodId id = new MediaPeriodId(periodIndex, windowSequenceNumber);
timeline.getPeriod(id.periodIndex, period);
int nextAdGroupIndex = period.getAdGroupIndexAfterPositionUs(startPositionUs);
long endUs =
long endPositionUs =
nextAdGroupIndex == C.INDEX_UNSET
? C.TIME_END_OF_SOURCE
: period.getAdGroupTimeUs(nextAdGroupIndex);
boolean isLastInPeriod = isLastInPeriod(id, endUs);
MediaPeriodId id = new MediaPeriodId(periodIndex, windowSequenceNumber, endPositionUs);
timeline.getPeriod(id.periodIndex, period);
boolean isLastInPeriod = isLastInPeriod(id);
boolean isLastInTimeline = isLastInTimeline(id, isLastInPeriod);
long durationUs = endUs == C.TIME_END_OF_SOURCE ? period.getDurationUs() : endUs;
long durationUs =
endPositionUs == C.TIME_END_OF_SOURCE ? period.getDurationUs() : endPositionUs;
return new MediaPeriodInfo(
id, startPositionUs, endUs, C.TIME_UNSET, durationUs, isLastInPeriod, isLastInTimeline);
id, startPositionUs, C.TIME_UNSET, durationUs, isLastInPeriod, isLastInTimeline);
}
private boolean isLastInPeriod(MediaPeriodId id, long endPositionUs) {
private boolean isLastInPeriod(MediaPeriodId id) {
int adGroupCount = timeline.getPeriod(id.periodIndex, period).getAdGroupCount();
if (adGroupCount == 0) {
return true;
@ -727,7 +734,7 @@ import org.telegram.messenger.exoplayer2.util.Assertions;
boolean isAd = id.isAd();
if (period.getAdGroupTimeUs(lastAdGroupIndex) != C.TIME_END_OF_SOURCE) {
// There's no postroll ad.
return !isAd && endPositionUs == C.TIME_END_OF_SOURCE;
return !isAd && id.endPositionUs == C.TIME_END_OF_SOURCE;
}
int postrollAdCount = period.getAdCountInAdGroup(lastAdGroupIndex);

View file

@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import org.telegram.messenger.exoplayer2.source.SampleStream;
import org.telegram.messenger.exoplayer2.util.Assertions;
import org.telegram.messenger.exoplayer2.util.MediaClock;
import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MediaClock;
import java.io.IOException;
/**

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import java.io.IOException;

View file

@ -0,0 +1,254 @@
/*
* 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;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
/**
* Information about an ongoing playback.
*/
/* package */ final class PlaybackInfo {
/**
* Dummy media period id used while the timeline is empty and no period id is specified. This id
* is used when playback infos are created with {@link #createDummy(long, TrackSelectorResult)}.
*/
public static final MediaPeriodId DUMMY_MEDIA_PERIOD_ID = new MediaPeriodId(/* periodIndex= */ 0);
/** The current {@link Timeline}. */
public final Timeline timeline;
/** The current manifest. */
public final @Nullable Object manifest;
/** The {@link MediaPeriodId} of the currently playing media period in the {@link #timeline}. */
public final MediaPeriodId periodId;
/**
* The start position at which playback started in {@link #periodId} relative to the start of the
* associated period in the {@link #timeline}, in microseconds.
*/
public final long startPositionUs;
/**
* If {@link #periodId} refers to an ad, the position of the suspended content relative to the
* start of the associated period in the {@link #timeline}, in microseconds. {@link C#TIME_UNSET}
* if {@link #periodId} does not refer to an ad.
*/
public final long contentPositionUs;
/** The current playback state. One of the {@link Player}.STATE_ constants. */
public final int playbackState;
/** Whether the player is currently loading. */
public final boolean isLoading;
/** The currently available track groups. */
public final TrackGroupArray trackGroups;
/** The result of the current track selection. */
public final TrackSelectorResult trackSelectorResult;
/** The {@link MediaPeriodId} of the currently loading media period in the {@link #timeline}. */
public final MediaPeriodId loadingMediaPeriodId;
/**
* Position up to which media is buffered in {@link #loadingMediaPeriodId) relative to the start
* of the associated period in the {@link #timeline}, in microseconds.
*/
public volatile long bufferedPositionUs;
/**
* Total duration of buffered media from {@link #positionUs} to {@link #bufferedPositionUs}
* including all ads.
*/
public volatile long totalBufferedDurationUs;
/**
* Current playback position in {@link #periodId} relative to the start of the associated period
* in the {@link #timeline}, in microseconds.
*/
public volatile long positionUs;
/**
* Creates empty dummy playback info which can be used for masking as long as no real playback
* info is available.
*
* @param startPositionUs The start position at which playback should start, in microseconds.
* @param emptyTrackSelectorResult An empty track selector result with null entries for each
* renderer.
* @return A dummy playback info.
*/
public static PlaybackInfo createDummy(
long startPositionUs, TrackSelectorResult emptyTrackSelectorResult) {
return new PlaybackInfo(
Timeline.EMPTY,
/* manifest= */ null,
DUMMY_MEDIA_PERIOD_ID,
startPositionUs,
/* contentPositionUs =*/ C.TIME_UNSET,
Player.STATE_IDLE,
/* isLoading= */ false,
TrackGroupArray.EMPTY,
emptyTrackSelectorResult,
DUMMY_MEDIA_PERIOD_ID,
startPositionUs,
/* totalBufferedDurationUs= */ 0,
startPositionUs);
}
public PlaybackInfo(
Timeline timeline,
@Nullable Object manifest,
MediaPeriodId periodId,
long startPositionUs,
long contentPositionUs,
int playbackState,
boolean isLoading,
TrackGroupArray trackGroups,
TrackSelectorResult trackSelectorResult,
MediaPeriodId loadingMediaPeriodId,
long bufferedPositionUs,
long totalBufferedDurationUs,
long positionUs) {
this.timeline = timeline;
this.manifest = manifest;
this.periodId = periodId;
this.startPositionUs = startPositionUs;
this.contentPositionUs = contentPositionUs;
this.playbackState = playbackState;
this.isLoading = isLoading;
this.trackGroups = trackGroups;
this.trackSelectorResult = trackSelectorResult;
this.loadingMediaPeriodId = loadingMediaPeriodId;
this.bufferedPositionUs = bufferedPositionUs;
this.totalBufferedDurationUs = totalBufferedDurationUs;
this.positionUs = positionUs;
}
public PlaybackInfo fromNewPosition(
MediaPeriodId periodId, long startPositionUs, long contentPositionUs) {
return new PlaybackInfo(
timeline,
manifest,
periodId,
startPositionUs,
periodId.isAd() ? contentPositionUs : C.TIME_UNSET,
playbackState,
isLoading,
trackGroups,
trackSelectorResult,
periodId,
startPositionUs,
/* totalBufferedDurationUs= */ 0,
startPositionUs);
}
public PlaybackInfo copyWithPeriodIndex(int periodIndex) {
return new PlaybackInfo(
timeline,
manifest,
periodId.copyWithPeriodIndex(periodIndex),
startPositionUs,
contentPositionUs,
playbackState,
isLoading,
trackGroups,
trackSelectorResult,
loadingMediaPeriodId,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs);
}
public PlaybackInfo copyWithTimeline(Timeline timeline, Object manifest) {
return new PlaybackInfo(
timeline,
manifest,
periodId,
startPositionUs,
contentPositionUs,
playbackState,
isLoading,
trackGroups,
trackSelectorResult,
loadingMediaPeriodId,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs);
}
public PlaybackInfo copyWithPlaybackState(int playbackState) {
return new PlaybackInfo(
timeline,
manifest,
periodId,
startPositionUs,
contentPositionUs,
playbackState,
isLoading,
trackGroups,
trackSelectorResult,
loadingMediaPeriodId,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs);
}
public PlaybackInfo copyWithIsLoading(boolean isLoading) {
return new PlaybackInfo(
timeline,
manifest,
periodId,
startPositionUs,
contentPositionUs,
playbackState,
isLoading,
trackGroups,
trackSelectorResult,
loadingMediaPeriodId,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs);
}
public PlaybackInfo copyWithTrackInfo(
TrackGroupArray trackGroups, TrackSelectorResult trackSelectorResult) {
return new PlaybackInfo(
timeline,
manifest,
periodId,
startPositionUs,
contentPositionUs,
playbackState,
isLoading,
trackGroups,
trackSelectorResult,
loadingMediaPeriodId,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs);
}
public PlaybackInfo copyWithLoadingMediaPeriodId(MediaPeriodId loadingMediaPeriodId) {
return new PlaybackInfo(
timeline,
manifest,
periodId,
startPositionUs,
contentPositionUs,
playbackState,
isLoading,
trackGroups,
trackSelectorResult,
loadingMediaPeriodId,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs);
}
}

View file

@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.support.annotation.Nullable;
import org.telegram.messenger.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Assertions;
/**
* The parameters that apply to playback.

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
/** Called to prepare a playback. */
public interface PlaybackPreparer {

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.os.Looper;
import android.support.annotation.IntDef;
@ -22,10 +22,13 @@ import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import org.telegram.messenger.exoplayer2.source.TrackGroupArray;
import org.telegram.messenger.exoplayer2.text.TextOutput;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelectionArray;
import org.telegram.messenger.exoplayer2.video.VideoListener;
import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.audio.AudioListener;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.text.TextOutput;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.VideoListener;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -50,6 +53,58 @@ import java.lang.annotation.RetentionPolicy;
*/
public interface Player {
/** The audio component of a {@link Player}. */
interface AudioComponent {
/**
* Adds a listener to receive audio events.
*
* @param listener The listener to register.
*/
void addAudioListener(AudioListener listener);
/**
* Removes a listener of audio events.
*
* @param listener The listener to unregister.
*/
void removeAudioListener(AudioListener listener);
/**
* Sets the attributes for audio playback, used by the underlying audio track. If not set, the
* default audio attributes will be used. They are suitable for general media playback.
*
* <p>Setting the audio attributes during playback may introduce a short gap in audio output as
* the audio track is recreated. A new audio session id will also be generated.
*
* <p>If tunneling is enabled by the track selector, the specified audio attributes will be
* ignored, but they will take effect if audio is later played without tunneling.
*
* <p>If the device is running a build before platform API version 21, audio attributes cannot
* be set directly on the underlying audio track. In this case, the usage will be mapped onto an
* equivalent stream type using {@link Util#getStreamTypeForAudioUsage(int)}.
*
* @param audioAttributes The attributes to use for audio playback.
*/
void setAudioAttributes(AudioAttributes audioAttributes);
/** Returns the attributes for audio playback. */
AudioAttributes getAudioAttributes();
/** Returns the audio session identifier, or {@link C#AUDIO_SESSION_ID_UNSET} if not set. */
int getAudioSessionId();
/**
* Sets the audio volume, with 0 being silence and 1 being unity gain.
*
* @param audioVolume The audio volume.
*/
void setVolume(float audioVolume);
/** Returns the audio volume, with 0 being silence and 1 being unity gain. */
float getVolume();
}
/** The video component of a {@link Player}. */
interface VideoComponent {
@ -97,7 +152,7 @@ public interface Player {
*
* @param surface The {@link Surface}.
*/
void setVideoSurface(Surface surface);
void setVideoSurface(@Nullable Surface surface);
/**
* Clears the {@link Surface} onto which video is being rendered if it matches the one passed.
@ -175,23 +230,25 @@ public interface Player {
}
/**
* Listener of changes in player state.
* Listener of changes in player state. All methods have no-op default implementations to allow
* selective overrides.
*/
interface EventListener {
/**
* Called when the timeline and/or manifest has been refreshed.
* <p>
* Note that if the timeline has changed then a position discontinuity may also have occurred.
* For example, the current period index may have changed as a result of periods being added or
* removed from the timeline. This will <em>not</em> be reported via a separate call to
*
* <p>Note that if the timeline has changed then a position discontinuity may also have
* occurred. For example, the current period index may have changed as a result of periods being
* added or removed from the timeline. This will <em>not</em> be reported via a separate call to
* {@link #onPositionDiscontinuity(int)}.
*
* @param timeline The latest timeline. Never null, but may be empty.
* @param manifest The latest manifest. May be null.
* @param reason The {@link TimelineChangeReason} responsible for this timeline change.
*/
void onTimelineChanged(Timeline timeline, Object manifest, @TimelineChangeReason int reason);
default void onTimelineChanged(
Timeline timeline, Object manifest, @TimelineChangeReason int reason) {}
/**
* Called when the available or selected tracks change.
@ -200,46 +257,47 @@ public interface Player {
* @param trackSelections The track selections for each renderer. Never null and always of
* length {@link #getRendererCount()}, but may contain null elements.
*/
void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections);
default void onTracksChanged(
TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {}
/**
* Called when the player starts or stops loading the source.
*
* @param isLoading Whether the source is currently being loaded.
*/
void onLoadingChanged(boolean isLoading);
default void onLoadingChanged(boolean isLoading) {}
/**
* Called when the value returned from either {@link #getPlayWhenReady()} or
* {@link #getPlaybackState()} changes.
* Called when the value returned from either {@link #getPlayWhenReady()} or {@link
* #getPlaybackState()} changes.
*
* @param playWhenReady Whether playback will proceed when ready.
* @param playbackState One of the {@code STATE} constants.
*/
void onPlayerStateChanged(boolean playWhenReady, int playbackState);
default void onPlayerStateChanged(boolean playWhenReady, int playbackState) {}
/**
* Called when the value of {@link #getRepeatMode()} changes.
*
* @param repeatMode The {@link RepeatMode} used for playback.
*/
void onRepeatModeChanged(@RepeatMode int repeatMode);
default void onRepeatModeChanged(@RepeatMode int repeatMode) {}
/**
* Called when the value of {@link #getShuffleModeEnabled()} changes.
*
* @param shuffleModeEnabled Whether shuffling of windows is enabled.
*/
void onShuffleModeEnabledChanged(boolean shuffleModeEnabled);
default void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {}
/**
* Called when an error occurs. The playback state will transition to {@link #STATE_IDLE}
* immediately after this method is called. The player instance can still be used, and
* {@link #release()} must still be called on the player should it no longer be required.
* immediately after this method is called. The player instance can still be used, and {@link
* #release()} must still be called on the player should it no longer be required.
*
* @param error The error.
*/
void onPlayerError(ExoPlaybackException error);
default void onPlayerError(ExoPlaybackException error) {}
/**
* Called when a position discontinuity occurs without a change to the timeline. A position
@ -247,14 +305,14 @@ public interface Player {
* transitioning from one period in the timeline to the next), or when the playback position
* jumps within the period currently being played (as a result of a seek being performed, or
* when the source introduces a discontinuity internally).
* <p>
* When a position discontinuity occurs as a result of a change to the timeline this method is
* <em>not</em> called. {@link #onTimelineChanged(Timeline, Object, int)} is called in this
*
* <p>When a position discontinuity occurs as a result of a change to the timeline this method
* is <em>not</em> called. {@link #onTimelineChanged(Timeline, Object, int)} is called in this
* case.
*
* @param reason The {@link DiscontinuityReason} responsible for the discontinuity.
*/
void onPositionDiscontinuity(@DiscontinuityReason int reason);
default void onPositionDiscontinuity(@DiscontinuityReason int reason) {}
/**
* Called when the current playback parameters change. The playback parameters may change due to
@ -264,20 +322,21 @@ public interface Player {
*
* @param playbackParameters The playback parameters.
*/
void onPlaybackParametersChanged(PlaybackParameters playbackParameters);
default void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {}
/**
* Called when all pending seek requests have been processed by the player. This is guaranteed
* to happen after any necessary changes to the player state were reported to
* {@link #onPlayerStateChanged(boolean, int)}.
* to happen after any necessary changes to the player state were reported to {@link
* #onPlayerStateChanged(boolean, int)}.
*/
void onSeekProcessed();
default void onSeekProcessed() {}
}
/**
* {@link EventListener} allowing selective overrides. All methods are implemented as no-ops.
* @deprecated Use {@link EventListener} interface directly for selective overrides as all methods
* are implemented as no-op default methods.
*/
@Deprecated
abstract class DefaultEventListener implements EventListener {
@Override
@ -287,60 +346,11 @@ public interface Player {
onTimelineChanged(timeline, manifest);
}
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
// Do nothing.
}
@Override
public void onLoadingChanged(boolean isLoading) {
// Do nothing.
}
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
// Do nothing.
}
@Override
public void onRepeatModeChanged(@RepeatMode int repeatMode) {
// Do nothing.
}
@Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
// Do nothing.
}
@Override
public void onPlayerError(ExoPlaybackException error) {
// Do nothing.
}
@Override
public void onPositionDiscontinuity(@DiscontinuityReason int reason) {
// Do nothing.
}
@Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
// Do nothing.
}
@Override
public void onSeekProcessed() {
// Do nothing.
}
/**
* @deprecated Use {@link DefaultEventListener#onTimelineChanged(Timeline, Object, int)}
* instead.
*/
/** @deprecated Use {@link EventListener#onTimelineChanged(Timeline, Object, int)} instead. */
@Deprecated
public void onTimelineChanged(Timeline timeline, Object manifest) {
// Do nothing.
}
}
/**
@ -428,6 +438,10 @@ public interface Player {
*/
int TIMELINE_CHANGE_REASON_DYNAMIC = 2;
/** Returns the component of this player for audio output, or null if audio is not supported. */
@Nullable
AudioComponent getAudioComponent();
/** Returns the component of this player for video output, or null if video is not supported. */
@Nullable
VideoComponent getVideoComponent();
@ -678,23 +692,27 @@ public interface Player {
*/
long getDuration();
/**
* Returns the playback position in the current window, in milliseconds.
*/
/** Returns the playback position in the current content window or ad, in milliseconds. */
long getCurrentPosition();
/**
* Returns an estimate of the position in the current window up to which data is buffered, in
* milliseconds.
* Returns an estimate of the position in the current content window or ad up to which data is
* buffered, in milliseconds.
*/
long getBufferedPosition();
/**
* Returns an estimate of the percentage in the current window up to which data is buffered, or 0
* if no estimate is available.
* Returns an estimate of the percentage in the current content window or ad up to which data is
* buffered, or 0 if no estimate is available.
*/
int getBufferedPercentage();
/**
* Returns an estimate of the total buffered duration from the current position, in milliseconds.
* This includes pre-buffered data for subsequent ads and windows.
*/
long getTotalBufferedDuration();
/**
* Returns whether the current window is dynamic, or {@code false} if the {@link Timeline} is
* empty.
@ -735,4 +753,10 @@ public interface Player {
*/
long getContentPosition();
/**
* If {@link #isPlayingAd()} returns {@code true}, returns an estimate of the content position in
* the current content window up to which data is buffered, in milliseconds. If there is no ad
* playing, the returned position is the same as that returned by {@link #getBufferedPosition()}.
*/
long getContentBufferedPosition();
}

View file

@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.os.Handler;
import android.support.annotation.Nullable;
import org.telegram.messenger.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Assertions;
/**
* Defines a player message which can be sent with a {@link Sender} and received by a {@link

View file

@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.support.annotation.IntDef;
import org.telegram.messenger.exoplayer2.source.SampleStream;
import org.telegram.messenger.exoplayer2.util.MediaClock;
import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.util.MediaClock;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -192,6 +192,18 @@ public interface Renderer extends PlayerMessage.Target {
*/
void resetPosition(long positionUs) throws ExoPlaybackException;
/**
* Sets the operating rate of this renderer, where 1 is the default rate, 2 is twice the default
* rate, 0.5 is half the default rate and so on. The operating rate is a hint to the renderer of
* the speed at which playback will proceed, and may be used for resource planning.
*
* <p>The default implementation is a no-op.
*
* @param operatingRate The operating rate.
* @throws ExoPlaybackException If an error occurs handling the operating rate.
*/
default void setOperatingRate(float operatingRate) throws ExoPlaybackException {};
/**
* Incrementally renders the {@link SampleStream}.
* <p>

View file

@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import org.telegram.messenger.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.MimeTypes;
/**
* Defines the capabilities of a {@link Renderer}.

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.support.annotation.Nullable;

View file

@ -13,16 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.os.Handler;
import android.support.annotation.Nullable;
import org.telegram.messenger.exoplayer2.audio.AudioRendererEventListener;
import org.telegram.messenger.exoplayer2.drm.DrmSessionManager;
import org.telegram.messenger.exoplayer2.drm.FrameworkMediaCrypto;
import org.telegram.messenger.exoplayer2.metadata.MetadataOutput;
import org.telegram.messenger.exoplayer2.text.TextOutput;
import org.telegram.messenger.exoplayer2.video.VideoRendererEventListener;
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.metadata.MetadataOutput;
import com.google.android.exoplayer2.text.TextOutput;
import com.google.android.exoplayer2.video.VideoRendererEventListener;
/**
* Builds {@link Renderer} instances for use by a {@link SimpleExoPlayer}.

View file

@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.support.annotation.Nullable;
import org.telegram.messenger.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Assertions;
/**
* Parameters that apply to seeking.

View file

@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.annotation.TargetApi;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.media.MediaCodec;
import android.media.PlaybackParams;
@ -27,25 +28,27 @@ import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import org.telegram.messenger.exoplayer2.analytics.AnalyticsCollector;
import org.telegram.messenger.exoplayer2.analytics.AnalyticsListener;
import org.telegram.messenger.exoplayer2.audio.AudioAttributes;
import org.telegram.messenger.exoplayer2.audio.AudioRendererEventListener;
import org.telegram.messenger.exoplayer2.decoder.DecoderCounters;
import org.telegram.messenger.exoplayer2.drm.DefaultDrmSessionManager;
import org.telegram.messenger.exoplayer2.drm.DrmSessionManager;
import org.telegram.messenger.exoplayer2.drm.FrameworkMediaCrypto;
import org.telegram.messenger.exoplayer2.metadata.Metadata;
import org.telegram.messenger.exoplayer2.metadata.MetadataOutput;
import org.telegram.messenger.exoplayer2.source.MediaSource;
import org.telegram.messenger.exoplayer2.source.TrackGroupArray;
import org.telegram.messenger.exoplayer2.text.Cue;
import org.telegram.messenger.exoplayer2.text.TextOutput;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelectionArray;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelector;
import org.telegram.messenger.exoplayer2.util.Clock;
import org.telegram.messenger.exoplayer2.util.Util;
import org.telegram.messenger.exoplayer2.video.VideoRendererEventListener;
import com.google.android.exoplayer2.analytics.AnalyticsCollector;
import com.google.android.exoplayer2.analytics.AnalyticsListener;
import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.audio.AudioListener;
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.MetadataOutput;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.TextOutput;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.VideoRendererEventListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -56,11 +59,12 @@ import java.util.concurrent.CopyOnWriteArraySet;
* be obtained from {@link ExoPlayerFactory}.
*/
@TargetApi(16)
public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player.TextComponent {
public class SimpleExoPlayer
implements ExoPlayer, Player.AudioComponent, Player.VideoComponent, Player.TextComponent {
/** @deprecated Use {@link org.telegram.messenger.exoplayer2.video.VideoListener}. */
/** @deprecated Use {@link com.google.android.exoplayer2.video.VideoListener}. */
@Deprecated
public interface VideoListener extends org.telegram.messenger.exoplayer2.video.VideoListener {}
public interface VideoListener extends com.google.android.exoplayer2.video.VideoListener {}
private static final String TAG = "SimpleExoPlayer";
@ -69,12 +73,14 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
private final ExoPlayer player;
private final Handler eventHandler;
private final ComponentListener componentListener;
private final CopyOnWriteArraySet<org.telegram.messenger.exoplayer2.video.VideoListener>
private final CopyOnWriteArraySet<com.google.android.exoplayer2.video.VideoListener>
videoListeners;
private final CopyOnWriteArraySet<AudioListener> audioListeners;
private final CopyOnWriteArraySet<TextOutput> textOutputs;
private final CopyOnWriteArraySet<MetadataOutput> metadataOutputs;
private final CopyOnWriteArraySet<VideoRendererEventListener> videoDebugListeners;
private final CopyOnWriteArraySet<AudioRendererEventListener> audioDebugListeners;
private final BandwidthMeter bandwidthMeter;
private final AnalyticsCollector analyticsCollector;
private Format videoFormat;
@ -84,10 +90,11 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
private Surface surface;
private boolean ownsSurface;
@C.VideoScalingMode
private int videoScalingMode;
private @C.VideoScalingMode int videoScalingMode;
private SurfaceHolder surfaceHolder;
private TextureView textureView;
private int surfaceWidth;
private int surfaceHeight;
private DecoderCounters videoDecoderCounters;
private DecoderCounters audioDecoderCounters;
private int audioSessionId;
@ -100,20 +107,27 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
* @param renderersFactory A factory for creating {@link Renderer}s to be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance.
* @param bandwidthMeter The {@link BandwidthMeter} that will be used by the instance.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance
* will not be used for DRM protected playbacks.
* @param looper The {@link Looper} which must be used for all calls to the player and which is
* used to call listeners on.
*/
protected SimpleExoPlayer(
RenderersFactory renderersFactory,
TrackSelector trackSelector,
LoadControl loadControl,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) {
BandwidthMeter bandwidthMeter,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
Looper looper) {
this(
renderersFactory,
trackSelector,
loadControl,
drmSessionManager,
new AnalyticsCollector.Factory());
bandwidthMeter,
new AnalyticsCollector.Factory(),
looper);
}
/**
@ -122,22 +136,29 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
* @param loadControl The {@link LoadControl} that will be used by the instance.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance
* will not be used for DRM protected playbacks.
* @param bandwidthMeter The {@link BandwidthMeter} that will be used by the instance.
* @param analyticsCollectorFactory A factory for creating the {@link AnalyticsCollector} that
* will collect and forward all player events.
* @param looper The {@link Looper} which must be used for all calls to the player and which is
* used to call listeners on.
*/
protected SimpleExoPlayer(
RenderersFactory renderersFactory,
TrackSelector trackSelector,
LoadControl loadControl,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
AnalyticsCollector.Factory analyticsCollectorFactory) {
BandwidthMeter bandwidthMeter,
AnalyticsCollector.Factory analyticsCollectorFactory,
Looper looper) {
this(
renderersFactory,
trackSelector,
loadControl,
drmSessionManager,
bandwidthMeter,
analyticsCollectorFactory,
Clock.DEFAULT);
Clock.DEFAULT,
looper);
}
/**
@ -146,26 +167,32 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
* @param loadControl The {@link LoadControl} that will be used by the instance.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance
* will not be used for DRM protected playbacks.
* @param bandwidthMeter The {@link BandwidthMeter} that will be used by the instance.
* @param analyticsCollectorFactory A factory for creating the {@link AnalyticsCollector} that
* will collect and forward all player events.
* @param clock The {@link Clock} that will be used by the instance. Should always be {@link
* Clock#DEFAULT}, unless the player is being used from a test.
* @param looper The {@link Looper} which must be used for all calls to the player and which is
* used to call listeners on.
*/
protected SimpleExoPlayer(
RenderersFactory renderersFactory,
TrackSelector trackSelector,
LoadControl loadControl,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
BandwidthMeter bandwidthMeter,
AnalyticsCollector.Factory analyticsCollectorFactory,
Clock clock) {
Clock clock,
Looper looper) {
this.bandwidthMeter = bandwidthMeter;
componentListener = new ComponentListener();
videoListeners = new CopyOnWriteArraySet<>();
audioListeners = new CopyOnWriteArraySet<>();
textOutputs = new CopyOnWriteArraySet<>();
metadataOutputs = new CopyOnWriteArraySet<>();
videoDebugListeners = new CopyOnWriteArraySet<>();
audioDebugListeners = new CopyOnWriteArraySet<>();
Looper eventLooper = Looper.myLooper() != null ? Looper.myLooper() : Looper.getMainLooper();
eventHandler = new Handler(eventLooper);
eventHandler = new Handler(looper);
renderers =
renderersFactory.createRenderers(
eventHandler,
@ -183,17 +210,26 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
currentCues = Collections.emptyList();
// Build the player and associated objects.
player = createExoPlayerImpl(renderers, trackSelector, loadControl, clock);
player =
createExoPlayerImpl(renderers, trackSelector, loadControl, bandwidthMeter, clock, looper);
analyticsCollector = analyticsCollectorFactory.createAnalyticsCollector(player, clock);
addListener(analyticsCollector);
videoDebugListeners.add(analyticsCollector);
videoListeners.add(analyticsCollector);
audioDebugListeners.add(analyticsCollector);
audioListeners.add(analyticsCollector);
addMetadataOutput(analyticsCollector);
bandwidthMeter.addEventListener(eventHandler, analyticsCollector);
if (drmSessionManager instanceof DefaultDrmSessionManager) {
((DefaultDrmSessionManager) drmSessionManager).addListener(eventHandler, analyticsCollector);
}
}
@Override
public AudioComponent getAudioComponent() {
return this;
}
@Override
public VideoComponent getVideoComponent() {
return this;
@ -240,6 +276,8 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
public void setVideoSurface(Surface surface) {
removeSurfaceCallbacks();
setVideoSurfaceInternal(surface, false);
int newSurfaceSize = surface == null ? 0 : C.LENGTH_UNSET;
maybeNotifySurfaceSizeChanged(/* width= */ newSurfaceSize, /* height= */ newSurfaceSize);
}
@Override
@ -255,10 +293,18 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
this.surfaceHolder = surfaceHolder;
if (surfaceHolder == null) {
setVideoSurfaceInternal(null, false);
maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0);
} else {
surfaceHolder.addCallback(componentListener);
Surface surface = surfaceHolder.getSurface();
setVideoSurfaceInternal(surface != null && surface.isValid() ? surface : null, false);
if (surface != null && surface.isValid()) {
setVideoSurfaceInternal(surface, /* ownsSurface= */ false);
Rect surfaceSize = surfaceHolder.getSurfaceFrame();
maybeNotifySurfaceSizeChanged(surfaceSize.width(), surfaceSize.height());
} else {
setVideoSurfaceInternal(/* surface= */ null, /* ownsSurface= */ false);
maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0);
}
}
}
@ -289,6 +335,7 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
needSetSurface = true;
if (textureView == null) {
setVideoSurfaceInternal(null, true);
maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0);
} else {
if (textureView.getSurfaceTextureListener() != null) {
Log.w(TAG, "Replacing existing SurfaceTextureListener.");
@ -296,7 +343,13 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
textureView.setSurfaceTextureListener(componentListener);
SurfaceTexture surfaceTexture = textureView.isAvailable() ? textureView.getSurfaceTexture()
: null;
setVideoSurfaceInternal(surfaceTexture == null ? null : new Surface(surfaceTexture), true);
if (surfaceTexture == null) {
setVideoSurfaceInternal(/* surface= */ null, /* ownsSurface= */ true);
maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0);
} else {
setVideoSurfaceInternal(new Surface(surfaceTexture), /* ownsSurface= */ true);
maybeNotifySurfaceSizeChanged(textureView.getWidth(), textureView.getHeight());
}
}
}
@ -307,6 +360,68 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
}
}
@Override
public void addAudioListener(AudioListener listener) {
audioListeners.add(listener);
}
@Override
public void removeAudioListener(AudioListener listener) {
audioListeners.remove(listener);
}
@Override
public void setAudioAttributes(AudioAttributes audioAttributes) {
if (Util.areEqual(this.audioAttributes, audioAttributes)) {
return;
}
this.audioAttributes = audioAttributes;
for (Renderer renderer : renderers) {
if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) {
player
.createMessage(renderer)
.setType(C.MSG_SET_AUDIO_ATTRIBUTES)
.setPayload(audioAttributes)
.send();
}
}
for (AudioListener audioListener : audioListeners) {
audioListener.onAudioAttributesChanged(audioAttributes);
}
}
@Override
public AudioAttributes getAudioAttributes() {
return audioAttributes;
}
@Override
public int getAudioSessionId() {
return audioSessionId;
}
@Override
public void setVolume(float audioVolume) {
audioVolume = Util.constrainValue(audioVolume, /* min= */ 0, /* max= */ 1);
if (this.audioVolume == audioVolume) {
return;
}
this.audioVolume = audioVolume;
for (Renderer renderer : renderers) {
if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) {
player.createMessage(renderer).setType(C.MSG_SET_VOLUME).setPayload(audioVolume).send();
}
}
for (AudioListener audioListener : audioListeners) {
audioListener.onVolumeChanged(audioVolume);
}
}
@Override
public float getVolume() {
return audioVolume;
}
/**
* Sets the stream type for audio playback, used by the underlying audio track.
* <p>
@ -361,63 +476,6 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
analyticsCollector.removeListener(listener);
}
/**
* Sets the attributes for audio playback, used by the underlying audio track. If not set, the
* default audio attributes will be used. They are suitable for general media playback.
* <p>
* Setting the audio attributes during playback may introduce a short gap in audio output as the
* audio track is recreated. A new audio session id will also be generated.
* <p>
* If tunneling is enabled by the track selector, the specified audio attributes will be ignored,
* but they will take effect if audio is later played without tunneling.
* <p>
* If the device is running a build before platform API version 21, audio attributes cannot be set
* directly on the underlying audio track. In this case, the usage will be mapped onto an
* equivalent stream type using {@link Util#getStreamTypeForAudioUsage(int)}.
*
* @param audioAttributes The attributes to use for audio playback.
*/
public void setAudioAttributes(AudioAttributes audioAttributes) {
this.audioAttributes = audioAttributes;
for (Renderer renderer : renderers) {
if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) {
player
.createMessage(renderer)
.setType(C.MSG_SET_AUDIO_ATTRIBUTES)
.setPayload(audioAttributes)
.send();
}
}
}
/**
* Returns the attributes for audio playback.
*/
public AudioAttributes getAudioAttributes() {
return audioAttributes;
}
/**
* Sets the audio volume, with 0 being silence and 1 being unity gain.
*
* @param audioVolume The audio volume.
*/
public void setVolume(float audioVolume) {
this.audioVolume = audioVolume;
for (Renderer renderer : renderers) {
if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) {
player.createMessage(renderer).setType(C.MSG_SET_VOLUME).setPayload(audioVolume).send();
}
}
}
/**
* Returns the audio volume, with 0 being silence and 1 being unity gain.
*/
public float getVolume() {
return audioVolume;
}
/**
* Sets the {@link PlaybackParams} governing audio playback.
*
@ -451,13 +509,6 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
return audioFormat;
}
/**
* Returns the audio session identifier, or {@link C#AUDIO_SESSION_ID_UNSET} if not set.
*/
public int getAudioSessionId() {
return audioSessionId;
}
/**
* Returns {@link DecoderCounters} for video, or null if no video is being played.
*/
@ -473,12 +524,12 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
}
@Override
public void addVideoListener(org.telegram.messenger.exoplayer2.video.VideoListener listener) {
public void addVideoListener(com.google.android.exoplayer2.video.VideoListener listener) {
videoListeners.add(listener);
}
@Override
public void removeVideoListener(org.telegram.messenger.exoplayer2.video.VideoListener listener) {
public void removeVideoListener(com.google.android.exoplayer2.video.VideoListener listener) {
videoListeners.remove(listener);
}
@ -486,7 +537,7 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
* Sets a listener to receive video events, removing all existing listeners.
*
* @param listener The listener.
* @deprecated Use {@link #addVideoListener(org.telegram.messenger.exoplayer2.video.VideoListener)}.
* @deprecated Use {@link #addVideoListener(com.google.android.exoplayer2.video.VideoListener)}.
*/
@Deprecated
public void setVideoListener(VideoListener listener) {
@ -497,11 +548,11 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
}
/**
* Equivalent to {@link #removeVideoListener(org.telegram.messenger.exoplayer2.video.VideoListener)}.
* Equivalent to {@link #removeVideoListener(com.google.android.exoplayer2.video.VideoListener)}.
*
* @param listener The listener to clear.
* @deprecated Use {@link
* #removeVideoListener(org.telegram.messenger.exoplayer2.video.VideoListener)}.
* #removeVideoListener(com.google.android.exoplayer2.video.VideoListener)}.
*/
@Deprecated
public void clearVideoListener(VideoListener listener) {
@ -656,6 +707,11 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
return player.getPlaybackLooper();
}
@Override
public Looper getApplicationLooper() {
return player.getApplicationLooper();
}
@Override
public void addListener(Player.EventListener listener) {
player.addListener(listener);
@ -768,6 +824,11 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
player.setSeekParameters(seekParameters);
}
@Override
public SeekParameters getSeekParameters() {
return player.getSeekParameters();
}
@Override
public @Nullable Object getCurrentTag() {
return player.getCurrentTag();
@ -802,6 +863,7 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
if (mediaSource != null) {
mediaSource.removeEventListener(analyticsCollector);
}
bandwidthMeter.removeEventListener(analyticsCollector);
currentCues = Collections.emptyList();
}
@ -890,6 +952,11 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
return player.getBufferedPercentage();
}
@Override
public long getTotalBufferedDuration() {
return player.getTotalBufferedDuration();
}
@Override
public boolean isCurrentWindowDynamic() {
return player.isCurrentWindowDynamic();
@ -920,6 +987,11 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
return player.getContentPosition();
}
@Override
public long getContentBufferedPosition() {
return player.getContentBufferedPosition();
}
// Internal methods.
/**
@ -928,12 +1000,20 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
* @param renderers The {@link Renderer}s that will be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance.
* @param bandwidthMeter The {@link BandwidthMeter} that will be used by the instance.
* @param clock The {@link Clock} that will be used by this instance.
* @param looper The {@link Looper} which must be used for all calls to the player and which is
* used to call listeners on.
* @return A new {@link ExoPlayer} instance.
*/
protected ExoPlayer createExoPlayerImpl(
Renderer[] renderers, TrackSelector trackSelector, LoadControl loadControl, Clock clock) {
return new ExoPlayerImpl(renderers, trackSelector, loadControl, clock);
Renderer[] renderers,
TrackSelector trackSelector,
LoadControl loadControl,
BandwidthMeter bandwidthMeter,
Clock clock,
Looper looper) {
return new ExoPlayerImpl(renderers, trackSelector, loadControl, bandwidthMeter, clock, looper);
}
private void removeSurfaceCallbacks() {
@ -979,6 +1059,16 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
this.ownsSurface = ownsSurface;
}
private void maybeNotifySurfaceSizeChanged(int width, int height) {
if (width != surfaceWidth || height != surfaceHeight) {
surfaceWidth = width;
surfaceHeight = height;
for (com.google.android.exoplayer2.video.VideoListener videoListener : videoListeners) {
videoListener.onSurfaceSizeChanged(width, height);
}
}
}
private final class ComponentListener implements VideoRendererEventListener,
AudioRendererEventListener, TextOutput, MetadataOutput, SurfaceHolder.Callback,
TextureView.SurfaceTextureListener {
@ -1020,9 +1110,13 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
@Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
float pixelWidthHeightRatio) {
for (org.telegram.messenger.exoplayer2.video.VideoListener videoListener : videoListeners) {
videoListener.onVideoSizeChanged(width, height, unappliedRotationDegrees,
pixelWidthHeightRatio);
for (com.google.android.exoplayer2.video.VideoListener videoListener : videoListeners) {
// Prevent duplicate notification if a listener is both a VideoRendererEventListener and
// a VideoListener, as they have the same method signature.
if (!videoDebugListeners.contains(videoListener)) {
videoListener.onVideoSizeChanged(
width, height, unappliedRotationDegrees, pixelWidthHeightRatio);
}
}
for (VideoRendererEventListener videoDebugListener : videoDebugListeners) {
videoDebugListener.onVideoSizeChanged(width, height, unappliedRotationDegrees,
@ -1033,7 +1127,7 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
@Override
public void onRenderedFirstFrame(Surface surface) {
if (SimpleExoPlayer.this.surface == surface) {
for (org.telegram.messenger.exoplayer2.video.VideoListener videoListener : videoListeners) {
for (com.google.android.exoplayer2.video.VideoListener videoListener : videoListeners) {
videoListener.onRenderedFirstFrame();
}
}
@ -1063,7 +1157,17 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
@Override
public void onAudioSessionId(int sessionId) {
if (audioSessionId == sessionId) {
return;
}
audioSessionId = sessionId;
for (AudioListener audioListener : audioListeners) {
// Prevent duplicate notification if a listener is both a AudioRendererEventListener and
// a AudioListener, as they have the same method signature.
if (!audioDebugListeners.contains(audioListener)) {
audioListener.onAudioSessionId(sessionId);
}
}
for (AudioRendererEventListener audioDebugListener : audioDebugListeners) {
audioDebugListener.onAudioSessionId(sessionId);
}
@ -1132,12 +1236,13 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// Do nothing.
maybeNotifySurfaceSizeChanged(width, height);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
setVideoSurfaceInternal(null, false);
maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0);
}
// TextureView.SurfaceTextureListener implementation
@ -1148,33 +1253,34 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
setVideoSurfaceInternal(new Surface(surfaceTexture), true);
needSetSurface = false;
}
maybeNotifySurfaceSizeChanged(width, height);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) {
// Do nothing.
maybeNotifySurfaceSizeChanged(width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
for (org.telegram.messenger.exoplayer2.video.VideoListener videoListener : videoListeners) {
for (com.google.android.exoplayer2.video.VideoListener videoListener : videoListeners) {
if (videoListener.onSurfaceDestroyed(surfaceTexture)) {
return false;
}
}
setVideoSurfaceInternal(null, true);
maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0);
needSetSurface = true;
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
for (org.telegram.messenger.exoplayer2.video.VideoListener videoListener : videoListeners) {
for (com.google.android.exoplayer2.video.VideoListener videoListener : videoListeners) {
videoListener.onSurfaceTextureUpdated(surfaceTexture);
}
// Do nothing.
}
}
}

View file

@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2;
package com.google.android.exoplayer2;
import android.support.annotation.Nullable;
import android.util.Pair;
import org.telegram.messenger.exoplayer2.source.ads.AdPlaybackState;
import org.telegram.messenger.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
import com.google.android.exoplayer2.util.Assertions;
/**
* A flexible representation of the structure of media. A timeline is able to represent the
@ -520,6 +520,11 @@ public abstract class Timeline {
public int getIndexOfPeriod(Object uid) {
return C.INDEX_UNSET;
}
@Override
public Object getUidOfPeriod(int periodIndex) {
throw new IndexOutOfBoundsException();
}
};
/**
@ -737,6 +742,17 @@ public abstract class Timeline {
return Pair.create(periodIndex, periodPositionUs);
}
/**
* Populates a {@link Period} with data for the period with the specified unique identifier.
*
* @param periodUid The unique identifier of the period.
* @param period The {@link Period} to populate. Must not be null.
* @return The populated {@link Period}, for convenience.
*/
public Period getPeriodByUid(Object periodUid, Period period) {
return getPeriod(getIndexOfPeriod(periodUid), period, /* setIds= */ true);
}
/**
* Populates a {@link Period} with data for the period at the specified index. Does not populate
* {@link Period#id} and {@link Period#uid}.
@ -770,4 +786,11 @@ public abstract class Timeline {
*/
public abstract int getIndexOfPeriod(Object uid);
/**
* Returns the unique id of the period identified by its index in the timeline.
*
* @param periodIndex The index of the period.
* @return The unique id of the period.
*/
public abstract Object getUidOfPeriod(int periodIndex);
}

View file

@ -13,39 +13,43 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.analytics;
package com.google.android.exoplayer2.analytics;
import android.net.NetworkInfo;
import android.graphics.SurfaceTexture;
import android.support.annotation.Nullable;
import android.view.Surface;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.ExoPlaybackException;
import org.telegram.messenger.exoplayer2.Format;
import org.telegram.messenger.exoplayer2.PlaybackParameters;
import org.telegram.messenger.exoplayer2.Player;
import org.telegram.messenger.exoplayer2.Timeline;
import org.telegram.messenger.exoplayer2.Timeline.Period;
import org.telegram.messenger.exoplayer2.Timeline.Window;
import org.telegram.messenger.exoplayer2.analytics.AnalyticsListener.EventTime;
import org.telegram.messenger.exoplayer2.audio.AudioRendererEventListener;
import org.telegram.messenger.exoplayer2.decoder.DecoderCounters;
import org.telegram.messenger.exoplayer2.drm.DefaultDrmSessionEventListener;
import org.telegram.messenger.exoplayer2.metadata.Metadata;
import org.telegram.messenger.exoplayer2.metadata.MetadataOutput;
import org.telegram.messenger.exoplayer2.source.MediaSource.MediaPeriodId;
import org.telegram.messenger.exoplayer2.source.MediaSourceEventListener;
import org.telegram.messenger.exoplayer2.source.TrackGroupArray;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelectionArray;
import org.telegram.messenger.exoplayer2.upstream.BandwidthMeter;
import org.telegram.messenger.exoplayer2.util.Assertions;
import org.telegram.messenger.exoplayer2.util.Clock;
import org.telegram.messenger.exoplayer2.video.VideoRendererEventListener;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period;
import com.google.android.exoplayer2.Timeline.Window;
import com.google.android.exoplayer2.analytics.AnalyticsListener.EventTime;
import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.audio.AudioListener;
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.drm.DefaultDrmSessionEventListener;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.MetadataOutput;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.MediaSourceEventListener;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.video.VideoListener;
import com.google.android.exoplayer2.video.VideoRendererEventListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/**
* Data collector which is able to forward analytics events to {@link AnalyticsListener}s by
@ -58,7 +62,9 @@ public class AnalyticsCollector
VideoRendererEventListener,
MediaSourceEventListener,
BandwidthMeter.EventListener,
DefaultDrmSessionEventListener {
DefaultDrmSessionEventListener,
VideoListener,
AudioListener {
/** Factory for an analytics collector. */
public static class Factory {
@ -66,29 +72,34 @@ public class AnalyticsCollector
/**
* Creates an analytics collector for the specified player.
*
* @param player The {@link Player} for which data will be collected.
* @param player The {@link Player} for which data will be collected. Can be null, if the player
* is set by calling {@link AnalyticsCollector#setPlayer(Player)} before using the analytics
* collector.
* @param clock A {@link Clock} used to generate timestamps.
* @return An analytics collector.
*/
public AnalyticsCollector createAnalyticsCollector(Player player, Clock clock) {
public AnalyticsCollector createAnalyticsCollector(@Nullable Player player, Clock clock) {
return new AnalyticsCollector(player, clock);
}
}
private final CopyOnWriteArraySet<AnalyticsListener> listeners;
private final Player player;
private final Clock clock;
private final Window window;
private final MediaPeriodQueueTracker mediaPeriodQueueTracker;
private @MonotonicNonNull Player player;
/**
* Creates an analytics collector for the specified player.
*
* @param player The {@link Player} for which data will be collected.
* @param player The {@link Player} for which data will be collected. Can be null, if the player
* is set by calling {@link AnalyticsCollector#setPlayer(Player)} before using the analytics
* collector.
* @param clock A {@link Clock} used to generate timestamps.
*/
protected AnalyticsCollector(Player player, Clock clock) {
this.player = Assertions.checkNotNull(player);
protected AnalyticsCollector(@Nullable Player player, Clock clock) {
this.player = player;
this.clock = Assertions.checkNotNull(clock);
listeners = new CopyOnWriteArraySet<>();
mediaPeriodQueueTracker = new MediaPeriodQueueTracker();
@ -113,6 +124,17 @@ public class AnalyticsCollector
listeners.remove(listener);
}
/**
* Sets the player for which data will be collected. Must only be called if no player has been set
* yet.
*
* @param player The {@link Player} for which data will be collected.
*/
public void setPlayer(Player player) {
Assertions.checkState(this.player == null);
this.player = Assertions.checkNotNull(player);
}
// External events.
/**
@ -129,31 +151,6 @@ public class AnalyticsCollector
}
}
/**
* Notify analytics collector that the viewport size changed.
*
* @param width The new width of the viewport in device-independent pixels (dp).
* @param height The new height of the viewport in device-independent pixels (dp).
*/
public final void notifyViewportSizeChanged(int width, int height) {
EventTime eventTime = generatePlayingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) {
listener.onViewportSizeChange(eventTime, width, height);
}
}
/**
* Notify analytics collector that the network type or connectivity changed.
*
* @param networkInfo The new network info, or null if no network connection exists.
*/
public final void notifyNetworkTypeChanged(@Nullable NetworkInfo networkInfo) {
EventTime eventTime = generatePlayingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) {
listener.onNetworkTypeChanged(eventTime, networkInfo);
}
}
/**
* Resets the analytics collector for a new media source. Should be called before the player is
* prepared with a new media source.
@ -188,14 +185,6 @@ public class AnalyticsCollector
}
}
@Override
public final void onAudioSessionId(int audioSessionId) {
EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) {
listener.onAudioSessionId(eventTime, audioSessionId);
}
}
@Override
public final void onAudioDecoderInitialized(
String decoderName, long initializedTimestampMs, long initializationDurationMs) {
@ -233,6 +222,32 @@ public class AnalyticsCollector
}
}
// AudioListener implementation.
@Override
public final void onAudioSessionId(int audioSessionId) {
EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) {
listener.onAudioSessionId(eventTime, audioSessionId);
}
}
@Override
public void onAudioAttributesChanged(AudioAttributes audioAttributes) {
EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) {
listener.onAudioAttributesChanged(eventTime, audioAttributes);
}
}
@Override
public void onVolumeChanged(float audioVolume) {
EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) {
listener.onVolumeChanged(eventTime, audioVolume);
}
}
// VideoRendererEventListener implementation.
@Override
@ -271,12 +286,12 @@ public class AnalyticsCollector
}
@Override
public final void onVideoSizeChanged(
int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
EventTime eventTime = generateReadingMediaPeriodEventTime();
public final void onVideoDisabled(DecoderCounters counters) {
// The renderers are disabled after we changed the playing media period on the playback thread
// but before this change is reported to the app thread.
EventTime eventTime = generateLastReportedPlayingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) {
listener.onVideoSizeChanged(
eventTime, width, height, unappliedRotationDegrees, pixelWidthHeightRatio);
listener.onDecoderDisabled(eventTime, C.TRACK_TYPE_VIDEO, counters);
}
}
@ -288,16 +303,31 @@ public class AnalyticsCollector
}
}
// VideoListener implementation.
@Override
public final void onVideoDisabled(DecoderCounters counters) {
// The renderers are disabled after we changed the playing media period on the playback thread
// but before this change is reported to the app thread.
EventTime eventTime = generateLastReportedPlayingMediaPeriodEventTime();
public final void onVideoSizeChanged(
int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) {
listener.onDecoderDisabled(eventTime, C.TRACK_TYPE_VIDEO, counters);
listener.onVideoSizeChanged(
eventTime, width, height, unappliedRotationDegrees, pixelWidthHeightRatio);
}
}
@Override
public void onSurfaceSizeChanged(int width, int height) {
EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) {
listener.onSurfaceSizeChanged(eventTime, width, height);
}
}
@Override
public final void onRenderedFirstFrame() {
// Do nothing. Already reported in VideoRendererEventListener.onRenderedFirstFrame.
}
// MediaSourceEventListener implementation.
@Override
@ -420,6 +450,16 @@ public class AnalyticsCollector
}
}
@Override
public boolean onSurfaceDestroyed(SurfaceTexture surfaceTexture) {
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
}
@Override
public final void onLoadingChanged(boolean isLoading) {
EventTime eventTime = generatePlayingMediaPeriodEventTime();
@ -541,6 +581,7 @@ public class AnalyticsCollector
/** Returns a new {@link EventTime} for the specified window index and media period id. */
protected EventTime generateEventTime(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
Assertions.checkNotNull(player);
long realtimeMs = clock.elapsedRealtime();
Timeline timeline = player.getCurrentTimeline();
long eventPositionMs;
@ -565,8 +606,6 @@ public class AnalyticsCollector
// This event is for content in a future window. Assume default start position.
eventPositionMs = timeline.getWindow(windowIndex, window).getDefaultPositionMs();
}
// TODO(b/30792113): implement this properly (player.getTotalBufferedDuration()).
long bufferedDurationMs = player.getBufferedPosition() - player.getContentPosition();
return new EventTime(
realtimeMs,
timeline,
@ -574,12 +613,12 @@ public class AnalyticsCollector
mediaPeriodId,
eventPositionMs,
player.getCurrentPosition(),
bufferedDurationMs);
player.getTotalBufferedDuration());
}
private EventTime generateEventTime(@Nullable WindowAndMediaPeriodId mediaPeriod) {
if (mediaPeriod == null) {
int windowIndex = player.getCurrentWindowIndex();
int windowIndex = Assertions.checkNotNull(player).getCurrentWindowIndex();
MediaPeriodId mediaPeriodId = mediaPeriodQueueTracker.tryResolveWindowIndex(windowIndex);
return generateEventTime(windowIndex, mediaPeriodId);
}
@ -756,8 +795,7 @@ public class AnalyticsCollector
if (newTimeline.isEmpty() || timeline.isEmpty()) {
return mediaPeriod;
}
Object uid =
timeline.getPeriod(mediaPeriod.mediaPeriodId.periodIndex, period, /* setIds= */ true).uid;
Object uid = timeline.getUidOfPeriod(mediaPeriod.mediaPeriodId.periodIndex);
int newPeriodIndex = newTimeline.getIndexOfPeriod(uid);
if (newPeriodIndex == C.INDEX_UNSET) {
return mediaPeriod;

View file

@ -13,27 +13,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.analytics;
package com.google.android.exoplayer2.analytics;
import android.net.NetworkInfo;
import android.support.annotation.Nullable;
import android.view.Surface;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.ExoPlaybackException;
import org.telegram.messenger.exoplayer2.Format;
import org.telegram.messenger.exoplayer2.PlaybackParameters;
import org.telegram.messenger.exoplayer2.Player;
import org.telegram.messenger.exoplayer2.Player.DiscontinuityReason;
import org.telegram.messenger.exoplayer2.Player.TimelineChangeReason;
import org.telegram.messenger.exoplayer2.Timeline;
import org.telegram.messenger.exoplayer2.audio.AudioSink;
import org.telegram.messenger.exoplayer2.decoder.DecoderCounters;
import org.telegram.messenger.exoplayer2.metadata.Metadata;
import org.telegram.messenger.exoplayer2.source.MediaSource.MediaPeriodId;
import org.telegram.messenger.exoplayer2.source.MediaSourceEventListener.LoadEventInfo;
import org.telegram.messenger.exoplayer2.source.MediaSourceEventListener.MediaLoadData;
import org.telegram.messenger.exoplayer2.source.TrackGroupArray;
import org.telegram.messenger.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Player.DiscontinuityReason;
import com.google.android.exoplayer2.Player.TimelineChangeReason;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.audio.AudioSink;
import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.MediaSourceEventListener.LoadEventInfo;
import com.google.android.exoplayer2.source.MediaSourceEventListener.MediaLoadData;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import java.io.IOException;
/**
@ -41,6 +41,8 @@ import java.io.IOException;
*
* <p>All events are recorded with an {@link EventTime} specifying the elapsed real time and media
* time at the time of the event.
*
* <p>All methods have no-op default implementations to allow selective overrides.
*/
public interface AnalyticsListener {
@ -127,7 +129,8 @@ public interface AnalyticsListener {
* @param playWhenReady Whether the playback will proceed when ready.
* @param playbackState One of the {@link Player}.STATE constants.
*/
void onPlayerStateChanged(EventTime eventTime, boolean playWhenReady, int playbackState);
default void onPlayerStateChanged(
EventTime eventTime, boolean playWhenReady, int playbackState) {}
/**
* Called when the timeline changed.
@ -135,7 +138,7 @@ public interface AnalyticsListener {
* @param eventTime The event time.
* @param reason The reason for the timeline change.
*/
void onTimelineChanged(EventTime eventTime, @TimelineChangeReason int reason);
default void onTimelineChanged(EventTime eventTime, @TimelineChangeReason int reason) {}
/**
* Called when a position discontinuity occurred.
@ -143,21 +146,21 @@ public interface AnalyticsListener {
* @param eventTime The event time.
* @param reason The reason for the position discontinuity.
*/
void onPositionDiscontinuity(EventTime eventTime, @DiscontinuityReason int reason);
default void onPositionDiscontinuity(EventTime eventTime, @DiscontinuityReason int reason) {}
/**
* Called when a seek operation started.
*
* @param eventTime The event time.
*/
void onSeekStarted(EventTime eventTime);
default void onSeekStarted(EventTime eventTime) {}
/**
* Called when a seek operation was processed.
*
* @param eventTime The event time.
*/
void onSeekProcessed(EventTime eventTime);
default void onSeekProcessed(EventTime eventTime) {}
/**
* Called when the playback parameters changed.
@ -165,7 +168,8 @@ public interface AnalyticsListener {
* @param eventTime The event time.
* @param playbackParameters The new playback parameters.
*/
void onPlaybackParametersChanged(EventTime eventTime, PlaybackParameters playbackParameters);
default void onPlaybackParametersChanged(
EventTime eventTime, PlaybackParameters playbackParameters) {}
/**
* Called when the repeat mode changed.
@ -173,7 +177,7 @@ public interface AnalyticsListener {
* @param eventTime The event time.
* @param repeatMode The new repeat mode.
*/
void onRepeatModeChanged(EventTime eventTime, @Player.RepeatMode int repeatMode);
default void onRepeatModeChanged(EventTime eventTime, @Player.RepeatMode int repeatMode) {}
/**
* Called when the shuffle mode changed.
@ -181,7 +185,7 @@ public interface AnalyticsListener {
* @param eventTime The event time.
* @param shuffleModeEnabled Whether the shuffle mode is enabled.
*/
void onShuffleModeChanged(EventTime eventTime, boolean shuffleModeEnabled);
default void onShuffleModeChanged(EventTime eventTime, boolean shuffleModeEnabled) {}
/**
* Called when the player starts or stops loading data from a source.
@ -189,7 +193,7 @@ public interface AnalyticsListener {
* @param eventTime The event time.
* @param isLoading Whether the player is loading.
*/
void onLoadingChanged(EventTime eventTime, boolean isLoading);
default void onLoadingChanged(EventTime eventTime, boolean isLoading) {}
/**
* Called when a fatal player error occurred.
@ -197,7 +201,7 @@ public interface AnalyticsListener {
* @param eventTime The event time.
* @param error The error.
*/
void onPlayerError(EventTime eventTime, ExoPlaybackException error);
default void onPlayerError(EventTime eventTime, ExoPlaybackException error) {}
/**
* Called when the available or selected tracks for the renderers changed.
@ -206,8 +210,8 @@ public interface AnalyticsListener {
* @param trackGroups The available tracks. May be empty.
* @param trackSelections The track selections for each renderer. May contain null elements.
*/
void onTracksChanged(
EventTime eventTime, TrackGroupArray trackGroups, TrackSelectionArray trackSelections);
default void onTracksChanged(
EventTime eventTime, TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {}
/**
* Called when a media source started loading data.
@ -216,7 +220,8 @@ public interface AnalyticsListener {
* @param loadEventInfo The {@link LoadEventInfo} defining the load event.
* @param mediaLoadData The {@link MediaLoadData} defining the data being loaded.
*/
void onLoadStarted(EventTime eventTime, LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData);
default void onLoadStarted(
EventTime eventTime, LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) {}
/**
* Called when a media source completed loading data.
@ -225,8 +230,8 @@ public interface AnalyticsListener {
* @param loadEventInfo The {@link LoadEventInfo} defining the load event.
* @param mediaLoadData The {@link MediaLoadData} defining the data being loaded.
*/
void onLoadCompleted(
EventTime eventTime, LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData);
default void onLoadCompleted(
EventTime eventTime, LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) {}
/**
* Called when a media source canceled loading data.
@ -235,8 +240,8 @@ public interface AnalyticsListener {
* @param loadEventInfo The {@link LoadEventInfo} defining the load event.
* @param mediaLoadData The {@link MediaLoadData} defining the data being loaded.
*/
void onLoadCanceled(
EventTime eventTime, LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData);
default void onLoadCanceled(
EventTime eventTime, LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) {}
/**
* Called when a media source loading error occurred. These errors are just for informational
@ -248,12 +253,12 @@ public interface AnalyticsListener {
* @param error The load error.
* @param wasCanceled Whether the load was canceled as a result of the error.
*/
void onLoadError(
default void onLoadError(
EventTime eventTime,
LoadEventInfo loadEventInfo,
MediaLoadData mediaLoadData,
IOException error,
boolean wasCanceled);
boolean wasCanceled) {}
/**
* Called when the downstream format sent to the renderers changed.
@ -261,7 +266,7 @@ public interface AnalyticsListener {
* @param eventTime The event time.
* @param mediaLoadData The {@link MediaLoadData} defining the newly selected media data.
*/
void onDownstreamFormatChanged(EventTime eventTime, MediaLoadData mediaLoadData);
default void onDownstreamFormatChanged(EventTime eventTime, MediaLoadData mediaLoadData) {}
/**
* Called when data is removed from the back of a media buffer, typically so that it can be
@ -270,28 +275,28 @@ public interface AnalyticsListener {
* @param eventTime The event time.
* @param mediaLoadData The {@link MediaLoadData} defining the media being discarded.
*/
void onUpstreamDiscarded(EventTime eventTime, MediaLoadData mediaLoadData);
default void onUpstreamDiscarded(EventTime eventTime, MediaLoadData mediaLoadData) {}
/**
* Called when a media source created a media period.
*
* @param eventTime The event time.
*/
void onMediaPeriodCreated(EventTime eventTime);
default void onMediaPeriodCreated(EventTime eventTime) {}
/**
* Called when a media source released a media period.
*
* @param eventTime The event time.
*/
void onMediaPeriodReleased(EventTime eventTime);
default void onMediaPeriodReleased(EventTime eventTime) {}
/**
* Called when the player started reading a media period.
*
* @param eventTime The event time.
*/
void onReadingStarted(EventTime eventTime);
default void onReadingStarted(EventTime eventTime) {}
/**
* Called when the bandwidth estimate for the current data source has been updated.
@ -301,25 +306,19 @@ public interface AnalyticsListener {
* @param totalBytesLoaded The total bytes loaded this update is based on.
* @param bitrateEstimate The bandwidth estimate, in bits per second.
*/
void onBandwidthEstimate(
EventTime eventTime, int totalLoadTimeMs, long totalBytesLoaded, long bitrateEstimate);
default void onBandwidthEstimate(
EventTime eventTime, int totalLoadTimeMs, long totalBytesLoaded, long bitrateEstimate) {}
/**
* Called when the viewport size of the output surface changed.
* Called when the output surface size changed.
*
* @param eventTime The event time.
* @param width The width of the viewport in device-independent pixels (dp).
* @param height The height of the viewport in device-independent pixels (dp).
* @param width The surface width in pixels. May be {@link C#LENGTH_UNSET} if unknown, or 0 if the
* video is not rendered onto a surface.
* @param height The surface height in pixels. May be {@link C#LENGTH_UNSET} if unknown, or 0 if
* the video is not rendered onto a surface.
*/
void onViewportSizeChange(EventTime eventTime, int width, int height);
/**
* Called when the type of the network connection changed.
*
* @param eventTime The event time.
* @param networkInfo The network info for the current connection, or null if disconnected.
*/
void onNetworkTypeChanged(EventTime eventTime, @Nullable NetworkInfo networkInfo);
default void onSurfaceSizeChanged(EventTime eventTime, int width, int height) {}
/**
* Called when there is {@link Metadata} associated with the current playback time.
@ -327,7 +326,7 @@ public interface AnalyticsListener {
* @param eventTime The event time.
* @param metadata The metadata.
*/
void onMetadata(EventTime eventTime, Metadata metadata);
default void onMetadata(EventTime eventTime, Metadata metadata) {}
/**
* Called when an audio or video decoder has been enabled.
@ -337,7 +336,8 @@ public interface AnalyticsListener {
* {@link C#TRACK_TYPE_VIDEO}.
* @param decoderCounters The accumulated event counters associated with this decoder.
*/
void onDecoderEnabled(EventTime eventTime, int trackType, DecoderCounters decoderCounters);
default void onDecoderEnabled(
EventTime eventTime, int trackType, DecoderCounters decoderCounters) {}
/**
* Called when an audio or video decoder has been initialized.
@ -348,8 +348,8 @@ public interface AnalyticsListener {
* @param decoderName The decoder that was created.
* @param initializationDurationMs Time taken to initialize the decoder, in milliseconds.
*/
void onDecoderInitialized(
EventTime eventTime, int trackType, String decoderName, long initializationDurationMs);
default void onDecoderInitialized(
EventTime eventTime, int trackType, String decoderName, long initializationDurationMs) {}
/**
* Called when an audio or video decoder input format changed.
@ -359,7 +359,7 @@ public interface AnalyticsListener {
* C#TRACK_TYPE_AUDIO} or {@link C#TRACK_TYPE_VIDEO}.
* @param format The new input format for the decoder.
*/
void onDecoderInputFormatChanged(EventTime eventTime, int trackType, Format format);
default void onDecoderInputFormatChanged(EventTime eventTime, int trackType, Format format) {}
/**
* Called when an audio or video decoder has been disabled.
@ -369,7 +369,8 @@ public interface AnalyticsListener {
* {@link C#TRACK_TYPE_VIDEO}.
* @param decoderCounters The accumulated event counters associated with this decoder.
*/
void onDecoderDisabled(EventTime eventTime, int trackType, DecoderCounters decoderCounters);
default void onDecoderDisabled(
EventTime eventTime, int trackType, DecoderCounters decoderCounters) {}
/**
* Called when the audio session id is set.
@ -377,7 +378,23 @@ public interface AnalyticsListener {
* @param eventTime The event time.
* @param audioSessionId The audio session id.
*/
void onAudioSessionId(EventTime eventTime, int audioSessionId);
default void onAudioSessionId(EventTime eventTime, int audioSessionId) {}
/**
* Called when the audio attributes change.
*
* @param eventTime The event time.
* @param audioAttributes The audio attributes.
*/
default void onAudioAttributesChanged(EventTime eventTime, AudioAttributes audioAttributes) {}
/**
* Called when the volume changes.
*
* @param eventTime The event time.
* @param volume The new volume, with 0 being silence and 1 being unity gain.
*/
default void onVolumeChanged(EventTime eventTime, float volume) {}
/**
* Called when an audio underrun occurred.
@ -389,8 +406,8 @@ public interface AnalyticsListener {
* as the buffered media can have a variable bitrate so the duration may be unknown.
* @param elapsedSinceLastFeedMs The time since the {@link AudioSink} was last fed data.
*/
void onAudioUnderrun(
EventTime eventTime, int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs);
default void onAudioUnderrun(
EventTime eventTime, int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {}
/**
* Called after video frames have been dropped.
@ -401,7 +418,7 @@ public interface AnalyticsListener {
* is timed from when the renderer was started or from when dropped frames were last reported
* (whichever was more recent), and not from when the first of the reported drops occurred.
*/
void onDroppedVideoFrames(EventTime eventTime, int droppedFrames, long elapsedMs);
default void onDroppedVideoFrames(EventTime eventTime, int droppedFrames, long elapsedMs) {}
/**
* Called before a frame is rendered for the first time since setting the surface, and each time
@ -416,12 +433,12 @@ public interface AnalyticsListener {
* since the renderer will apply all necessary rotations internally.
* @param pixelWidthHeightRatio The width to height ratio of each pixel.
*/
void onVideoSizeChanged(
default void onVideoSizeChanged(
EventTime eventTime,
int width,
int height,
int unappliedRotationDegrees,
float pixelWidthHeightRatio);
float pixelWidthHeightRatio) {}
/**
* Called when a frame is rendered for the first time since setting the surface, and when a frame
@ -431,14 +448,14 @@ public interface AnalyticsListener {
* @param surface The {@link Surface} to which a first frame has been rendered, or {@code null} if
* the renderer renders to something that isn't a {@link Surface}.
*/
void onRenderedFirstFrame(EventTime eventTime, Surface surface);
default void onRenderedFirstFrame(EventTime eventTime, Surface surface) {}
/**
* Called each time drm keys are loaded.
*
* @param eventTime The event time.
*/
void onDrmKeysLoaded(EventTime eventTime);
default void onDrmKeysLoaded(EventTime eventTime) {}
/**
* Called when a drm error occurs. These errors are just for informational purposes and the player
@ -447,19 +464,19 @@ public interface AnalyticsListener {
* @param eventTime The event time.
* @param error The error.
*/
void onDrmSessionManagerError(EventTime eventTime, Exception error);
default void onDrmSessionManagerError(EventTime eventTime, Exception error) {}
/**
* Called each time offline drm keys are restored.
*
* @param eventTime The event time.
*/
void onDrmKeysRestored(EventTime eventTime);
default void onDrmKeysRestored(EventTime eventTime) {}
/**
* Called each time offline drm keys are removed.
*
* @param eventTime The event time.
*/
void onDrmKeysRemoved(EventTime eventTime);
default void onDrmKeysRemoved(EventTime eventTime) {}
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (C) 2018 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.analytics;
/**
* @deprecated Use {@link AnalyticsListener} directly for selective overrides as all methods are
* implemented as no-op default methods.
*/
@Deprecated
public abstract class DefaultAnalyticsListener implements AnalyticsListener {}

View file

@ -13,16 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import android.support.annotation.IntDef;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.Format;
import org.telegram.messenger.exoplayer2.audio.Ac3Util.SyncFrameInfo.StreamType;
import org.telegram.messenger.exoplayer2.drm.DrmInitData;
import org.telegram.messenger.exoplayer2.util.MimeTypes;
import org.telegram.messenger.exoplayer2.util.ParsableBitArray;
import org.telegram.messenger.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.audio.Ac3Util.SyncFrameInfo.StreamType;
import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
@ -137,17 +137,17 @@ public final class Ac3Util {
121, 139, 174, 208, 243, 278, 348, 417, 487, 557, 696, 835, 975, 1114, 1253, 1393};
/**
* Returns the AC-3 format given {@code data} containing the AC3SpecificBox according to
* ETSI TS 102 366 Annex F. The reading position of {@code data} will be modified.
* Returns the AC-3 format given {@code data} containing the AC3SpecificBox according to ETSI TS
* 102 366 Annex F. The reading position of {@code data} will be modified.
*
* @param data The AC3SpecificBox to parse.
* @param trackId The track identifier to set on the format, or null.
* @param trackId The track identifier to set on the format.
* @param language The language to set on the format.
* @param drmInitData {@link DrmInitData} to be included in the format.
* @return The AC-3 format parsed from data in the header.
*/
public static Format parseAc3AnnexFFormat(ParsableByteArray data, String trackId,
String language, DrmInitData drmInitData) {
public static Format parseAc3AnnexFFormat(
ParsableByteArray data, String trackId, String language, DrmInitData drmInitData) {
int fscod = (data.readUnsignedByte() & 0xC0) >> 6;
int sampleRate = SAMPLE_RATE_BY_FSCOD[fscod];
int nextByte = data.readUnsignedByte();
@ -155,22 +155,32 @@ public final class Ac3Util {
if ((nextByte & 0x04) != 0) { // lfeon
channelCount++;
}
return Format.createAudioSampleFormat(trackId, MimeTypes.AUDIO_AC3, null, Format.NO_VALUE,
Format.NO_VALUE, channelCount, sampleRate, null, drmInitData, 0, language);
return Format.createAudioSampleFormat(
trackId,
MimeTypes.AUDIO_AC3,
/* codecs= */ null,
Format.NO_VALUE,
Format.NO_VALUE,
channelCount,
sampleRate,
/* initializationData= */ null,
drmInitData,
/* selectionFlags= */ 0,
language);
}
/**
* Returns the E-AC-3 format given {@code data} containing the EC3SpecificBox according to
* ETSI TS 102 366 Annex F. The reading position of {@code data} will be modified.
* Returns the E-AC-3 format given {@code data} containing the EC3SpecificBox according to ETSI TS
* 102 366 Annex F. The reading position of {@code data} will be modified.
*
* @param data The EC3SpecificBox to parse.
* @param trackId The track identifier to set on the format, or null.
* @param trackId The track identifier to set on the format.
* @param language The language to set on the format.
* @param drmInitData {@link DrmInitData} to be included in the format.
* @return The E-AC-3 format parsed from data in the header.
*/
public static Format parseEAc3AnnexFFormat(ParsableByteArray data, String trackId,
String language, DrmInitData drmInitData) {
public static Format parseEAc3AnnexFFormat(
ParsableByteArray data, String trackId, String language, DrmInitData drmInitData) {
data.skipBytes(2); // data_rate, num_ind_sub
// Read the first independent substream.
@ -200,8 +210,18 @@ public final class Ac3Util {
mimeType = MimeTypes.AUDIO_E_AC3_JOC;
}
}
return Format.createAudioSampleFormat(trackId, mimeType, null, Format.NO_VALUE,
Format.NO_VALUE, channelCount, sampleRate, null, drmInitData, 0, language);
return Format.createAudioSampleFormat(
trackId,
mimeType,
/* codecs= */ null,
Format.NO_VALUE,
Format.NO_VALUE,
channelCount,
sampleRate,
/* initializationData= */ null,
drmInitData,
/* selectionFlags= */ 0,
language);
}
/**

View file

@ -13,18 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import android.annotation.TargetApi;
import android.support.annotation.Nullable;
import org.telegram.messenger.exoplayer2.C;
import com.google.android.exoplayer2.C;
/**
* Attributes for audio playback, which configure the underlying platform
* {@link android.media.AudioTrack}.
* <p>
* To set the audio attributes, create an instance using the {@link Builder} and either pass it to
* {@link org.telegram.messenger.exoplayer2.SimpleExoPlayer#setAudioAttributes(AudioAttributes)} or
* {@link com.google.android.exoplayer2.SimpleExoPlayer#setAudioAttributes(AudioAttributes)} or
* send a message of type {@link C#MSG_SET_AUDIO_ATTRIBUTES} to the audio renderers.
* <p>
* This class is based on {@link android.media.AudioAttributes}, but can be used on all supported
@ -39,12 +39,9 @@ public final class AudioAttributes {
*/
public static final class Builder {
@C.AudioContentType
private int contentType;
@C.AudioFlags
private int flags;
@C.AudioUsage
private int usage;
private @C.AudioContentType int contentType;
private @C.AudioFlags int flags;
private @C.AudioUsage int usage;
/**
* Creates a new builder for {@link AudioAttributes}.
@ -91,14 +88,11 @@ public final class AudioAttributes {
}
@C.AudioContentType
public final int contentType;
@C.AudioFlags
public final int flags;
@C.AudioUsage
public final int usage;
public final @C.AudioContentType int contentType;
public final @C.AudioFlags int flags;
public final @C.AudioUsage int usage;
private android.media.AudioAttributes audioAttributesV21;
private @Nullable android.media.AudioAttributes audioAttributesV21;
private AudioAttributes(@C.AudioContentType int contentType, @C.AudioFlags int flags,
@C.AudioUsage int usage) {

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
@ -50,7 +50,7 @@ public final class AudioCapabilities {
}
@SuppressLint("InlinedApi")
/* package */ static AudioCapabilities getCapabilities(Intent intent) {
/* package */ static AudioCapabilities getCapabilities(@Nullable Intent intent) {
if (intent == null || intent.getIntExtra(AudioManager.EXTRA_AUDIO_PLUG_STATE, 0) == 0) {
return DEFAULT_AUDIO_CAPABILITIES;
}

View file

@ -13,15 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import org.telegram.messenger.exoplayer2.util.Assertions;
import org.telegram.messenger.exoplayer2.util.Util;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
/**
* Receives broadcast events indicating changes to the device's audio capabilities, notifying a
@ -45,9 +46,9 @@ public final class AudioCapabilitiesReceiver {
private final Context context;
private final Listener listener;
private final BroadcastReceiver receiver;
private final @Nullable BroadcastReceiver receiver;
/* package */ AudioCapabilities audioCapabilities;
/* package */ @Nullable AudioCapabilities audioCapabilities;
/**
* @param context A context for registering the receiver.

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
/** Thrown when an audio decoder error occurs. */
public class AudioDecoderException extends Exception {

View file

@ -0,0 +1,41 @@
/*
* Copyright (C) 2018 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.audio;
/** A listener for changes in audio configuration. */
public interface AudioListener {
/**
* Called when the audio session is set.
*
* @param audioSessionId The audio session id.
*/
default void onAudioSessionId(int audioSessionId) {}
/**
* Called when the audio attributes change.
*
* @param audioAttributes The audio attributes.
*/
default void onAudioAttributesChanged(AudioAttributes audioAttributes) {}
/**
* Called when the volume changes.
*
* @param volume The new volume, with 0 being silence and 1 being unity gain.
*/
default void onVolumeChanged(float volume) {}
}

View file

@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import org.telegram.messenger.exoplayer2.C;
import com.google.android.exoplayer2.C;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

View file

@ -13,16 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import android.os.Handler;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.Format;
import org.telegram.messenger.exoplayer2.Renderer;
import org.telegram.messenger.exoplayer2.decoder.DecoderCounters;
import org.telegram.messenger.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.util.Assertions;
/**
* Listener of audio {@link Renderer} events.

View file

@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import android.media.AudioTrack;
import android.support.annotation.Nullable;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.PlaybackParameters;
import java.nio.ByteBuffer;
/**

View file

@ -13,15 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import android.annotation.TargetApi;
import android.media.AudioTimestamp;
import android.media.AudioTrack;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.util.Util;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Util;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

View file

@ -13,15 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import static com.google.android.exoplayer2.util.Util.castNonNull;
import android.media.AudioTimestamp;
import android.media.AudioTrack;
import android.os.SystemClock;
import android.support.annotation.IntDef;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.util.Assertions;
import org.telegram.messenger.exoplayer2.util.Util;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
@ -128,10 +131,10 @@ import java.lang.reflect.Method;
private final Listener listener;
private final long[] playheadOffsets;
private AudioTrack audioTrack;
private @Nullable AudioTrack audioTrack;
private int outputPcmFrameSize;
private int bufferSize;
private AudioTimestampPoller audioTimestampPoller;
private @Nullable AudioTimestampPoller audioTimestampPoller;
private int outputSampleRate;
private boolean needsPassthroughWorkarounds;
private long bufferSizeUs;
@ -139,7 +142,7 @@ import java.lang.reflect.Method;
private long smoothedPlayheadOffsetUs;
private long lastPlayheadSampleTimeUs;
private Method getLatencyMethod;
private @Nullable Method getLatencyMethod;
private long latencyUs;
private boolean hasData;
@ -193,7 +196,7 @@ import java.lang.reflect.Method;
audioTimestampPoller = new AudioTimestampPoller(audioTrack);
outputSampleRate = audioTrack.getSampleRate();
needsPassthroughWorkarounds = needsPassthroughWorkarounds(outputEncoding);
isOutputPcm = Util.isEncodingPcm(outputEncoding);
isOutputPcm = Util.isEncodingLinearPcm(outputEncoding);
bufferSizeUs = isOutputPcm ? framesToDurationUs(bufferSize / outputPcmFrameSize) : C.TIME_UNSET;
lastRawPlaybackHeadPosition = 0;
rawPlaybackHeadWrapCount = 0;
@ -205,13 +208,14 @@ import java.lang.reflect.Method;
}
public long getCurrentPositionUs(boolean sourceEnded) {
if (audioTrack.getPlayState() == PLAYSTATE_PLAYING) {
if (Assertions.checkNotNull(this.audioTrack).getPlayState() == PLAYSTATE_PLAYING) {
maybeSampleSyncParams();
}
// If the device supports it, use the playback timestamp from AudioTrack.getTimestamp.
// Otherwise, derive a smoothed position by sampling the track's frame position.
long systemTimeUs = System.nanoTime() / 1000;
AudioTimestampPoller audioTimestampPoller = Assertions.checkNotNull(this.audioTimestampPoller);
if (audioTimestampPoller.hasTimestamp()) {
// Calculate the speed-adjusted position using the timestamp (which may be in the future).
long timestampPositionFrames = audioTimestampPoller.getTimestampPositionFrames();
@ -241,12 +245,12 @@ import java.lang.reflect.Method;
/** Starts position tracking. Must be called immediately before {@link AudioTrack#play()}. */
public void start() {
audioTimestampPoller.reset();
Assertions.checkNotNull(audioTimestampPoller).reset();
}
/** Returns whether the audio track is in the playing state. */
public boolean isPlaying() {
return audioTrack.getPlayState() == PLAYSTATE_PLAYING;
return Assertions.checkNotNull(audioTrack).getPlayState() == PLAYSTATE_PLAYING;
}
/**
@ -257,7 +261,7 @@ import java.lang.reflect.Method;
* @return Whether the caller can write data to the track.
*/
public boolean mayHandleBuffer(long writtenFrames) {
@PlayState int playState = audioTrack.getPlayState();
@PlayState int playState = Assertions.checkNotNull(audioTrack).getPlayState();
if (needsPassthroughWorkarounds) {
// An AC-3 audio track continues to play data written while it is paused. Stop writing so its
// buffer empties. See [Internal: b/18899620].
@ -339,7 +343,7 @@ import java.lang.reflect.Method;
if (stopTimestampUs == C.TIME_UNSET) {
// The audio track is going to be paused, so reset the timestamp poller to ensure it doesn't
// supply an advancing position.
audioTimestampPoller.reset();
Assertions.checkNotNull(audioTimestampPoller).reset();
return true;
}
// We've handled the end of the stream already, so there's no need to pause the track.
@ -388,6 +392,7 @@ import java.lang.reflect.Method;
}
private void maybePollAndCheckTimestamp(long systemTimeUs, long playbackPositionUs) {
AudioTimestampPoller audioTimestampPoller = Assertions.checkNotNull(this.audioTimestampPoller);
if (!audioTimestampPoller.maybePollTimestamp(systemTimeUs)) {
return;
}
@ -423,7 +428,9 @@ import java.lang.reflect.Method;
// Compute the audio track latency, excluding the latency due to the buffer (leaving
// latency due to the mixer and audio hardware driver).
latencyUs =
(Integer) getLatencyMethod.invoke(audioTrack, (Object[]) null) * 1000L - bufferSizeUs;
castNonNull((Integer) getLatencyMethod.invoke(Assertions.checkNotNull(audioTrack)))
* 1000L
- bufferSizeUs;
// Sanity check that the latency is non-negative.
latencyUs = Math.max(latencyUs, 0);
// Sanity check that the latency isn't too large.
@ -457,7 +464,7 @@ import java.lang.reflect.Method;
*/
private boolean forceHasPendingData() {
return needsPassthroughWorkarounds
&& audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PAUSED
&& Assertions.checkNotNull(audioTrack).getPlayState() == AudioTrack.PLAYSTATE_PAUSED
&& getPlaybackHeadPosition() == 0;
}
@ -483,6 +490,7 @@ import java.lang.reflect.Method;
* @return The playback head position, in frames.
*/
private long getPlaybackHeadPosition() {
AudioTrack audioTrack = Assertions.checkNotNull(this.audioTrack);
if (stopTimestampUs != C.TIME_UNSET) {
// Simulate the playback head position up to the total number of frames submitted.
long elapsedTimeSinceStopUs = (SystemClock.elapsedRealtime() * 1000) - stopTimestampUs;

View file

@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import android.support.annotation.Nullable;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.C.Encoding;
import org.telegram.messenger.exoplayer2.Format;
import org.telegram.messenger.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.C.Encoding;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.util.Assertions;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
@ -25,10 +25,10 @@ import android.os.SystemClock;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import android.util.Log;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.PlaybackParameters;
import org.telegram.messenger.exoplayer2.util.Assertions;
import org.telegram.messenger.exoplayer2.util.Util;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
@ -371,7 +371,7 @@ public final class DefaultAudioSink implements AudioSink {
@Override
public boolean isEncodingSupported(@C.Encoding int encoding) {
if (Util.isEncodingPcm(encoding)) {
if (Util.isEncodingLinearPcm(encoding)) {
// AudioTrack supports 16-bit integer PCM output in all platform API versions, and float
// output from platform API version 21 only. Other integer PCM encodings are resampled by this
// sink to 16-bit PCM.
@ -405,7 +405,7 @@ public final class DefaultAudioSink implements AudioSink {
this.inputSampleRate = inputSampleRate;
int channelCount = inputChannelCount;
int sampleRate = inputSampleRate;
isInputPcm = Util.isEncodingPcm(inputEncoding);
isInputPcm = Util.isEncodingLinearPcm(inputEncoding);
shouldConvertHighResIntPcmToFloat =
enableConvertHighResIntPcmToFloat
&& isEncodingSupported(C.ENCODING_PCM_32BIT)

View file

@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import org.telegram.messenger.exoplayer2.Format;
import org.telegram.messenger.exoplayer2.drm.DrmInitData;
import org.telegram.messenger.exoplayer2.util.MimeTypes;
import org.telegram.messenger.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableBitArray;
import java.nio.ByteBuffer;
import java.util.Arrays;
@ -74,13 +74,13 @@ public final class DtsUtil {
* subsections 5.3/5.4.
*
* @param frame The DTS frame to parse.
* @param trackId The track identifier to set on the format, or null.
* @param trackId The track identifier to set on the format.
* @param language The language to set on the format.
* @param drmInitData {@link DrmInitData} to be included in the format.
* @return The DTS format parsed from data in the header.
*/
public static Format parseDtsFormat(byte[] frame, String trackId, String language,
DrmInitData drmInitData) {
public static Format parseDtsFormat(
byte[] frame, String trackId, String language, DrmInitData drmInitData) {
ParsableBitArray frameBits = getNormalizedFrameHeader(frame);
frameBits.skipBits(32 + 1 + 5 + 1 + 7 + 14); // SYNC, FTYPE, SHORT, CPF, NBLKS, FSIZE
int amode = frameBits.readBits(6);

View file

@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.Format;
import org.telegram.messenger.exoplayer2.util.Util;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.util.Util;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
@ -25,26 +25,28 @@ import android.media.MediaFormat;
import android.media.audiofx.Virtualizer;
import android.os.Handler;
import android.support.annotation.Nullable;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.ExoPlaybackException;
import org.telegram.messenger.exoplayer2.ExoPlayer;
import org.telegram.messenger.exoplayer2.Format;
import org.telegram.messenger.exoplayer2.PlaybackParameters;
import org.telegram.messenger.exoplayer2.PlayerMessage.Target;
import org.telegram.messenger.exoplayer2.audio.AudioRendererEventListener.EventDispatcher;
import org.telegram.messenger.exoplayer2.decoder.DecoderInputBuffer;
import org.telegram.messenger.exoplayer2.drm.DrmInitData;
import org.telegram.messenger.exoplayer2.drm.DrmSessionManager;
import org.telegram.messenger.exoplayer2.drm.FrameworkMediaCrypto;
import org.telegram.messenger.exoplayer2.mediacodec.MediaCodecInfo;
import org.telegram.messenger.exoplayer2.mediacodec.MediaCodecRenderer;
import org.telegram.messenger.exoplayer2.mediacodec.MediaCodecSelector;
import org.telegram.messenger.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException;
import org.telegram.messenger.exoplayer2.mediacodec.MediaFormatUtil;
import org.telegram.messenger.exoplayer2.util.MediaClock;
import org.telegram.messenger.exoplayer2.util.MimeTypes;
import org.telegram.messenger.exoplayer2.util.Util;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.audio.AudioRendererEventListener.EventDispatcher;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer;
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer2.mediacodec.MediaFormatUtil;
import com.google.android.exoplayer2.util.MediaClock;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.List;
/**
* Decodes and renders audio using {@link MediaCodec} and an {@link AudioSink}.
@ -56,7 +58,7 @@ import java.nio.ByteBuffer;
* <li>Message with type {@link C#MSG_SET_VOLUME} to set the volume. The message payload should be
* a {@link Float} with 0 being silence and 1 being unity gain.
* <li>Message with type {@link C#MSG_SET_AUDIO_ATTRIBUTES} to set the audio attributes. The
* message payload should be an {@link org.telegram.messenger.exoplayer2.audio.AudioAttributes}
* message payload should be an {@link com.google.android.exoplayer2.audio.AudioAttributes}
* instance that will configure the underlying audio track.
* </ul>
*/
@ -229,7 +231,12 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
@Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener,
AudioSink audioSink) {
super(C.TRACK_TYPE_AUDIO, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys);
super(
C.TRACK_TYPE_AUDIO,
mediaCodecSelector,
drmSessionManager,
playClearSamplesWithoutKeys,
/* assumedMinimumCodecOperatingRate= */ 44100);
this.context = context.getApplicationContext();
this.audioSink = audioSink;
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
@ -262,15 +269,21 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
requiresSecureDecryption |= drmInitData.get(i).requiresSecureDecryption;
}
}
MediaCodecInfo decoderInfo = mediaCodecSelector.getDecoderInfo(mimeType,
requiresSecureDecryption);
if (decoderInfo == null) {
return requiresSecureDecryption && mediaCodecSelector.getDecoderInfo(mimeType, false) != null
? FORMAT_UNSUPPORTED_DRM : FORMAT_UNSUPPORTED_SUBTYPE;
List<MediaCodecInfo> decoderInfos =
mediaCodecSelector.getDecoderInfos(format, requiresSecureDecryption);
if (decoderInfos.isEmpty()) {
return requiresSecureDecryption
&& !mediaCodecSelector
.getDecoderInfos(format, /* requiresSecureDecoder= */ false)
.isEmpty()
? FORMAT_UNSUPPORTED_DRM
: FORMAT_UNSUPPORTED_SUBTYPE;
}
if (!supportsFormatDrm) {
return FORMAT_UNSUPPORTED_DRM;
}
// Check capabilities for the first decoder in the list, which takes priority.
MediaCodecInfo decoderInfo = decoderInfos.get(0);
// Note: We assume support for unknown sampleRate and channelCount.
boolean decoderCapable = Util.SDK_INT < 21
|| ((format.sampleRate == Format.NO_VALUE
@ -282,15 +295,16 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
}
@Override
protected MediaCodecInfo getDecoderInfo(MediaCodecSelector mediaCodecSelector,
Format format, boolean requiresSecureDecoder) throws DecoderQueryException {
protected List<MediaCodecInfo> getDecoderInfos(
MediaCodecSelector mediaCodecSelector, Format format, boolean requiresSecureDecoder)
throws DecoderQueryException {
if (allowPassthrough(format.sampleMimeType)) {
MediaCodecInfo passthroughDecoderInfo = mediaCodecSelector.getPassthroughDecoderInfo();
if (passthroughDecoderInfo != null) {
return passthroughDecoderInfo;
return Collections.singletonList(passthroughDecoderInfo);
}
}
return super.getDecoderInfo(mediaCodecSelector, format, requiresSecureDecoder);
return super.getDecoderInfos(mediaCodecSelector, format, requiresSecureDecoder);
}
/**
@ -307,13 +321,18 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
}
@Override
protected void configureCodec(MediaCodecInfo codecInfo, MediaCodec codec, Format format,
MediaCrypto crypto) {
protected void configureCodec(
MediaCodecInfo codecInfo,
MediaCodec codec,
Format format,
MediaCrypto crypto,
float codecOperatingRate) {
codecMaxInputSize = getCodecMaxInputSize(codecInfo, format, getStreamFormats());
codecNeedsDiscardChannelsWorkaround = codecNeedsDiscardChannelsWorkaround(codecInfo.name);
passthroughEnabled = codecInfo.passthrough;
String codecMimeType = codecInfo.mimeType == null ? MimeTypes.AUDIO_RAW : codecInfo.mimeType;
MediaFormat mediaFormat = getMediaFormat(format, codecMimeType, codecMaxInputSize);
MediaFormat mediaFormat =
getMediaFormat(format, codecMimeType, codecMaxInputSize, codecOperatingRate);
codec.configure(mediaFormat, /* surface= */ null, crypto, /* flags= */ 0);
if (passthroughEnabled) {
// Store the input MIME type if we're using the passthrough codec.
@ -341,6 +360,14 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
return this;
}
@Override
protected float getCodecOperatingRate(
float operatingRate, Format format, Format[] streamFormats) {
return format.sampleRate == Format.NO_VALUE
? CODEC_OPERATING_RATE_UNSET
: (format.sampleRate * operatingRate);
}
@Override
protected void onCodecInitialized(String name, long initializedTimestampMs,
long initializationDurationMs) {
@ -624,10 +651,13 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
* @param format The format of the media.
* @param codecMimeType The MIME type handled by the codec.
* @param codecMaxInputSize The maximum input size supported by the codec.
* @param codecOperatingRate The codec operating rate, or {@link #CODEC_OPERATING_RATE_UNSET} if
* no codec operating rate should be set.
* @return The framework media format.
*/
@SuppressLint("InlinedApi")
protected MediaFormat getMediaFormat(Format format, String codecMimeType, int codecMaxInputSize) {
protected MediaFormat getMediaFormat(
Format format, String codecMimeType, int codecMaxInputSize, float codecOperatingRate) {
MediaFormat mediaFormat = new MediaFormat();
// Set format parameters that should always be set.
mediaFormat.setString(MediaFormat.KEY_MIME, codecMimeType);
@ -639,6 +669,9 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
// Set codec configuration values.
if (Util.SDK_INT >= 23) {
mediaFormat.setInteger(MediaFormat.KEY_PRIORITY, 0 /* realtime priority */);
if (codecOperatingRate != CODEC_OPERATING_RATE_UNSET) {
mediaFormat.setFloat(MediaFormat.KEY_OPERATING_RATE, codecOperatingRate);
}
}
return mediaFormat;
}

View file

@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.Format;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

View file

@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import android.support.annotation.IntDef;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.Format;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;

View file

@ -13,35 +13,36 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import android.media.audiofx.Virtualizer;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.support.annotation.IntDef;
import org.telegram.messenger.exoplayer2.BaseRenderer;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.ExoPlaybackException;
import org.telegram.messenger.exoplayer2.ExoPlayer;
import org.telegram.messenger.exoplayer2.Format;
import org.telegram.messenger.exoplayer2.FormatHolder;
import org.telegram.messenger.exoplayer2.PlaybackParameters;
import org.telegram.messenger.exoplayer2.PlayerMessage.Target;
import org.telegram.messenger.exoplayer2.audio.AudioRendererEventListener.EventDispatcher;
import org.telegram.messenger.exoplayer2.decoder.DecoderCounters;
import org.telegram.messenger.exoplayer2.decoder.DecoderInputBuffer;
import org.telegram.messenger.exoplayer2.decoder.SimpleDecoder;
import org.telegram.messenger.exoplayer2.decoder.SimpleOutputBuffer;
import org.telegram.messenger.exoplayer2.drm.DrmSession;
import org.telegram.messenger.exoplayer2.drm.DrmSession.DrmSessionException;
import org.telegram.messenger.exoplayer2.drm.DrmSessionManager;
import org.telegram.messenger.exoplayer2.drm.ExoMediaCrypto;
import org.telegram.messenger.exoplayer2.util.Assertions;
import org.telegram.messenger.exoplayer2.util.MediaClock;
import org.telegram.messenger.exoplayer2.util.MimeTypes;
import org.telegram.messenger.exoplayer2.util.TraceUtil;
import org.telegram.messenger.exoplayer2.util.Util;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.BaseRenderer;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.audio.AudioRendererEventListener.EventDispatcher;
import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.decoder.SimpleDecoder;
import com.google.android.exoplayer2.decoder.SimpleOutputBuffer;
import com.google.android.exoplayer2.drm.DrmSession;
import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MediaClock;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.TraceUtil;
import com.google.android.exoplayer2.util.Util;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -55,7 +56,7 @@ import java.lang.annotation.RetentionPolicy;
* <li>Message with type {@link C#MSG_SET_VOLUME} to set the volume. The message payload should be
* a {@link Float} with 0 being silence and 1 being unity gain.
* <li>Message with type {@link C#MSG_SET_AUDIO_ATTRIBUTES} to set the audio attributes. The
* message payload should be an {@link org.telegram.messenger.exoplayer2.audio.AudioAttributes}
* message payload should be an {@link com.google.android.exoplayer2.audio.AudioAttributes}
* instance that will configure the underlying audio track.
* </ul>
*/
@ -121,7 +122,9 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
* @param eventListener A listener of events. May be null if delivery of events is not required.
* @param audioProcessors Optional {@link AudioProcessor}s that will process audio before output.
*/
public SimpleDecoderAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
public SimpleDecoderAudioRenderer(
@Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener,
AudioProcessor... audioProcessors) {
this(
eventHandler,
@ -139,8 +142,10 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
* @param audioCapabilities The audio capabilities for playback on this device. May be null if the
* default capabilities (no encoded audio passthrough support) should be assumed.
*/
public SimpleDecoderAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
AudioCapabilities audioCapabilities) {
public SimpleDecoderAudioRenderer(
@Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener,
@Nullable AudioCapabilities audioCapabilities) {
this(
eventHandler,
eventListener,
@ -164,9 +169,13 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
* has obtained the keys necessary to decrypt encrypted regions of the media.
* @param audioProcessors Optional {@link AudioProcessor}s that will process audio before output.
*/
public SimpleDecoderAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
AudioCapabilities audioCapabilities, DrmSessionManager<ExoMediaCrypto> drmSessionManager,
boolean playClearSamplesWithoutKeys, AudioProcessor... audioProcessors) {
public SimpleDecoderAudioRenderer(
@Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener,
@Nullable AudioCapabilities audioCapabilities,
@Nullable DrmSessionManager<ExoMediaCrypto> drmSessionManager,
boolean playClearSamplesWithoutKeys,
AudioProcessor... audioProcessors) {
this(eventHandler, eventListener, drmSessionManager,
playClearSamplesWithoutKeys, new DefaultAudioSink(audioCapabilities, audioProcessors));
}
@ -184,8 +193,11 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
* has obtained the keys necessary to decrypt encrypted regions of the media.
* @param audioSink The sink to which audio will be output.
*/
public SimpleDecoderAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
DrmSessionManager<ExoMediaCrypto> drmSessionManager, boolean playClearSamplesWithoutKeys,
public SimpleDecoderAudioRenderer(
@Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener,
@Nullable DrmSessionManager<ExoMediaCrypto> drmSessionManager,
boolean playClearSamplesWithoutKeys,
AudioSink audioSink) {
super(C.TRACK_TYPE_AUDIO);
this.drmSessionManager = drmSessionManager;

View file

@ -14,9 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import org.telegram.messenger.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Assertions;
import java.nio.ShortBuffer;
import java.util.Arrays;
@ -31,60 +31,6 @@ import java.util.Arrays;
private static final int MAXIMUM_PITCH = 400;
private static final int AMDF_FREQUENCY = 4000;
private static final int SINC_FILTER_POINTS = 12;
private static final int SINC_TABLE_SIZE = 601;
private static final short sincTable[] = {
0, 0, 0, 0, 0, 0, 0, -1, -1, -2, -2, -3, -4, -6, -7, -9, -10, -12, -14,
-17, -19, -21, -24, -26, -29, -32, -34, -37, -40, -42, -44, -47, -48, -50,
-51, -52, -53, -53, -53, -52, -50, -48, -46, -43, -39, -34, -29, -22, -16,
-8, 0, 9, 19, 29, 41, 53, 65, 79, 92, 107, 121, 137, 152, 168, 184, 200,
215, 231, 247, 262, 276, 291, 304, 317, 328, 339, 348, 357, 363, 369, 372,
374, 375, 373, 369, 363, 355, 345, 332, 318, 300, 281, 259, 234, 208, 178,
147, 113, 77, 39, 0, -41, -85, -130, -177, -225, -274, -324, -375, -426,
-478, -530, -581, -632, -682, -731, -779, -825, -870, -912, -951, -989,
-1023, -1053, -1080, -1104, -1123, -1138, -1149, -1154, -1155, -1151,
-1141, -1125, -1105, -1078, -1046, -1007, -963, -913, -857, -796, -728,
-655, -576, -492, -403, -309, -210, -107, 0, 111, 225, 342, 462, 584, 708,
833, 958, 1084, 1209, 1333, 1455, 1575, 1693, 1807, 1916, 2022, 2122, 2216,
2304, 2384, 2457, 2522, 2579, 2625, 2663, 2689, 2706, 2711, 2705, 2687,
2657, 2614, 2559, 2491, 2411, 2317, 2211, 2092, 1960, 1815, 1658, 1489,
1308, 1115, 912, 698, 474, 241, 0, -249, -506, -769, -1037, -1310, -1586,
-1864, -2144, -2424, -2703, -2980, -3254, -3523, -3787, -4043, -4291,
-4529, -4757, -4972, -5174, -5360, -5531, -5685, -5819, -5935, -6029,
-6101, -6150, -6175, -6175, -6149, -6096, -6015, -5905, -5767, -5599,
-5401, -5172, -4912, -4621, -4298, -3944, -3558, -3141, -2693, -2214,
-1705, -1166, -597, 0, 625, 1277, 1955, 2658, 3386, 4135, 4906, 5697, 6506,
7332, 8173, 9027, 9893, 10769, 11654, 12544, 13439, 14335, 15232, 16128,
17019, 17904, 18782, 19649, 20504, 21345, 22170, 22977, 23763, 24527,
25268, 25982, 26669, 27327, 27953, 28547, 29107, 29632, 30119, 30569,
30979, 31349, 31678, 31964, 32208, 32408, 32565, 32677, 32744, 32767,
32744, 32677, 32565, 32408, 32208, 31964, 31678, 31349, 30979, 30569,
30119, 29632, 29107, 28547, 27953, 27327, 26669, 25982, 25268, 24527,
23763, 22977, 22170, 21345, 20504, 19649, 18782, 17904, 17019, 16128,
15232, 14335, 13439, 12544, 11654, 10769, 9893, 9027, 8173, 7332, 6506,
5697, 4906, 4135, 3386, 2658, 1955, 1277, 625, 0, -597, -1166, -1705,
-2214, -2693, -3141, -3558, -3944, -4298, -4621, -4912, -5172, -5401,
-5599, -5767, -5905, -6015, -6096, -6149, -6175, -6175, -6150, -6101,
-6029, -5935, -5819, -5685, -5531, -5360, -5174, -4972, -4757, -4529,
-4291, -4043, -3787, -3523, -3254, -2980, -2703, -2424, -2144, -1864,
-1586, -1310, -1037, -769, -506, -249, 0, 241, 474, 698, 912, 1115, 1308,
1489, 1658, 1815, 1960, 2092, 2211, 2317, 2411, 2491, 2559, 2614, 2657,
2687, 2705, 2711, 2706, 2689, 2663, 2625, 2579, 2522, 2457, 2384, 2304,
2216, 2122, 2022, 1916, 1807, 1693, 1575, 1455, 1333, 1209, 1084, 958, 833,
708, 584, 462, 342, 225, 111, 0, -107, -210, -309, -403, -492, -576, -655,
-728, -796, -857, -913, -963, -1007, -1046, -1078, -1105, -1125, -1141,
-1151, -1155, -1154, -1149, -1138, -1123, -1104, -1080, -1053, -1023, -989,
-951, -912, -870, -825, -779, -731, -682, -632, -581, -530, -478, -426,
-375, -324, -274, -225, -177, -130, -85, -41, 0, 39, 77, 113, 147, 178,
208, 234, 259, 281, 300, 318, 332, 345, 355, 363, 369, 373, 375, 374, 372,
369, 363, 357, 348, 339, 328, 317, 304, 291, 276, 262, 247, 231, 215, 200,
184, 168, 152, 137, 121, 107, 92, 79, 65, 53, 41, 29, 19, 9, 0, -8, -16,
-22, -29, -34, -39, -43, -46, -48, -50, -52, -53, -53, -53, -52, -51, -50,
-48, -47, -44, -42, -40, -37, -34, -32, -29, -26, -24, -21, -19, -17, -14,
-12, -10, -9, -7, -6, -4, -3, -2, -2, -1, -1, 0, 0, 0, 0, 0, 0, 0
};
private final int inputSampleRateHz;
private final int channelCount;
private final float speed;
@ -264,14 +210,14 @@ import java.util.Arrays;
return frameCount;
}
private void downSampleInput(short samples[], int position, int skip) {
int numSamples = maxRequiredFrameCount / skip;
private void downSampleInput(short[] samples, int position, int skip) {
// If skip is greater than one, average skip samples together and write them to the down-sample
// buffer. If channelCount is greater than one, mix the channels together as we down sample.
int frameCount = maxRequiredFrameCount / skip;
int samplesPerValue = channelCount * skip;
int value;
position *= channelCount;
for (int i = 0; i < numSamples; i++) {
value = 0;
for (int i = 0; i < frameCount; i++) {
int value = 0;
for (int j = 0; j < samplesPerValue; j++) {
value += samples[position + i * samplesPerValue + j];
}
@ -280,23 +226,24 @@ import java.util.Arrays;
}
}
private int findPitchPeriodInRange(
short samples[],
int position,
int minPeriod,
int maxPeriod)
{
int bestPeriod = 0, worstPeriod = 255;
int minDiff = 1, maxDiff = 0;
private int findPitchPeriodInRange(short[] samples, int position, int minPeriod, int maxPeriod) {
// Find the best frequency match in the range, and given a sample skip multiple. For now, just
// find the pitch of the first channel.
int bestPeriod = 0;
int worstPeriod = 255;
int minDiff = 1;
int maxDiff = 0;
position *= channelCount;
for (int period = minPeriod; period <= maxPeriod; period++) {
int diff = 0;
for (int i = 0; i < period; i++) {
short sVal = samples[position + i];
short pVal = samples[position + period + i];
diff += sVal >= pVal? sVal - pVal : pVal - sVal;
diff += Math.abs(sVal - pVal);
}
// Note that the highest number of samples we add into diff will be less than 256, since we
// skip samples. Thus, diff is a 24 bit number, and we can safely multiply by numSamples
// without overflow.
if (diff * bestPeriod < minDiff * period) {
minDiff = diff;
bestPeriod = period;
@ -308,7 +255,6 @@ import java.util.Arrays;
}
this.minDiff = minDiff / bestPeriod;
this.maxDiff = maxDiff / worstPeriod;
return bestPeriod;
}
@ -316,28 +262,7 @@ import java.util.Arrays;
* Returns whether the previous pitch period estimate is a better approximation, which can occur
* at the abrupt end of voiced words.
*/
private boolean previousPeriodBetter(int minDiff, int maxDiff, boolean preferNewPeriod) {
if (minDiff == 0 || prevPeriod == 0) {
return false;
}
if (preferNewPeriod) {
if (maxDiff > minDiff * 3) {
// Got a reasonable match this period
return false;
}
if (minDiff * 2 <= prevMinDiff * 3) {
// Mismatch is not that much greater this period
return false;
}
} else {
if (minDiff <= prevMinDiff) {
return false;
}
}
return true;
}
/*private boolean previousPeriodBetter(int minDiff, int maxDiff) {
private boolean previousPeriodBetter(int minDiff, int maxDiff) {
if (minDiff == 0 || prevPeriod == 0) {
return false;
}
@ -350,51 +275,9 @@ import java.util.Arrays;
return false;
}
return true;
}*/
private int findPitchPeriod(short samples[], int position, boolean preferNewPeriod) {
int period, retPeriod;
int skip = 1;
int quality = 1;
if (inputSampleRateHz > AMDF_FREQUENCY && quality == 0) {
skip = inputSampleRateHz / AMDF_FREQUENCY;
}
if (channelCount == 1 && skip == 1) {
period = findPitchPeriodInRange(samples, position, minPeriod, maxPeriod);
} else {
downSampleInput(samples, position, skip);
period = findPitchPeriodInRange(downSampleBuffer, 0, minPeriod / skip,
maxPeriod / skip);
if (skip != 1) {
period *= skip;
int minP = period - (skip << 2);
int maxP = period + (skip << 2);
if (minP < minPeriod) {
minP = minPeriod;
}
if (maxP > maxPeriod) {
maxP = maxPeriod;
}
if (channelCount == 1) {
period = findPitchPeriodInRange(samples, position, minP, maxP);
} else {
downSampleInput(samples, position, 1);
period = findPitchPeriodInRange(downSampleBuffer, 0, minP, maxP);
}
}
}
if (previousPeriodBetter(minDiff, maxDiff, preferNewPeriod)) {
retPeriod = prevPeriod;
} else {
retPeriod = period;
}
prevMinDiff = minDiff;
prevPeriod = period;
return retPeriod;
}
/*private int findPitchPeriod(short[] samples, int position) {
private int findPitchPeriod(short[] samples, int position) {
// Find the pitch period. This is a critical step, and we may have to try multiple ways to get a
// good answer. This version uses AMDF. To improve speed, we down sample by an integer factor
// get in the 11 kHz range, and then do it again with a narrower frequency range without down
@ -433,7 +316,7 @@ import java.util.Arrays;
prevMinDiff = minDiff;
prevPeriod = period;
return retPeriod;
}*/
}
private void moveNewSamplesToPitchBuffer(int originalOutputFrameCount) {
int frameCount = outputFrameCount - originalOutputFrameCount;
@ -461,78 +344,6 @@ import java.util.Arrays;
pitchFrameCount -= frameCount;
}
private void adjustPitch(int originalNumOutputSamples) {
int period, newPeriod, separation;
int position = 0;
if (outputFrameCount == originalNumOutputSamples) {
return;
}
moveNewSamplesToPitchBuffer(originalNumOutputSamples);
while (pitchFrameCount - position >= maxRequiredFrameCount) {
period = findPitchPeriod(pitchBuffer, position, false);
newPeriod = (int) (period / pitch);
outputBuffer = ensureSpaceForAdditionalFrames(outputBuffer, outputFrameCount, newPeriod);
if (pitch >= 1.0f) {
overlapAdd(newPeriod, channelCount, outputBuffer, outputFrameCount, pitchBuffer,
position, pitchBuffer, position + period - newPeriod);
} else {
separation = newPeriod - period;
overlapAddWithSeparation(period, channelCount, separation, outputBuffer, outputFrameCount,
pitchBuffer, position, pitchBuffer, position);
}
outputFrameCount += newPeriod;
position += period;
}
removePitchFrames(position);
}
// Aproximate the sinc function times a Hann window from the sinc table.
private int findSincCoefficient(int i, int ratio, int width) {
int lobePoints = (SINC_TABLE_SIZE - 1) / SINC_FILTER_POINTS;
int left = i * lobePoints + (ratio * lobePoints) / width;
int right = left + 1;
int position = i * lobePoints * width + ratio * lobePoints - left * width;
int leftVal = sincTable[left];
int rightVal = sincTable[right];
return ((leftVal * (width - position) + rightVal * position) << 1) / width;
}
// Return 1 if value >= 0, else -1. This represents the sign of value.
private int getSign(int value) {
return value >= 0 ? 1 : 0;
}
/*private short interpolate(short in[], int inPos, int oldSampleRate, int newSampleRate) {
int i;
int total = 0;
int position = newRatePosition * oldSampleRate;
int leftPosition = oldRatePosition * newSampleRate;
int rightPosition = (oldRatePosition + 1) * newSampleRate;
int ratio = rightPosition - position - 1;
int width = rightPosition - leftPosition;
int weight, value;
int oldSign;
int overflowCount = 0;
for (i = 0; i < SINC_FILTER_POINTS; i++) {
weight = findSincCoefficient(i, ratio, width);
value = in[inPos + i * channelCount] * weight;
oldSign = getSign(total);
total += value;
if (oldSign != getSign(total) && getSign(value) == oldSign) {
overflowCount += oldSign;
}
}
if (overflowCount > 0) {
return Short.MAX_VALUE;
} else if (overflowCount < 0) {
return Short.MIN_VALUE;
}
return (short) (total >> 16);
}*/
private short interpolate(short[] in, int inPos, int oldSampleRate, int newSampleRate) {
short left = in[inPos];
short right = in[inPos + channelCount];
@ -544,26 +355,27 @@ import java.util.Arrays;
return (short) ((ratio * left + (width - ratio) * right) / width);
}
private void adjustRate(float rate, int originalNumOutputSamples) {
private void adjustRate(float rate, int originalOutputFrameCount) {
if (outputFrameCount == originalOutputFrameCount) {
return;
}
int newSampleRate = (int) (inputSampleRateHz / rate);
int oldSampleRate = inputSampleRateHz;
int position;
// Set these values to help with the integer math
// Set these values to help with the integer math.
while (newSampleRate > (1 << 14) || oldSampleRate > (1 << 14)) {
newSampleRate >>= 1;
oldSampleRate >>= 1;
newSampleRate /= 2;
oldSampleRate /= 2;
}
moveNewSamplesToPitchBuffer(originalNumOutputSamples);
// Leave at least one pitch sample in the buffer
for (position = 0; position < pitchFrameCount - 1; position++) {
moveNewSamplesToPitchBuffer(originalOutputFrameCount);
// Leave at least one pitch sample in the buffer.
for (int position = 0; position < pitchFrameCount - 1; position++) {
while ((oldRatePosition + 1) * newSampleRate > newRatePosition * oldSampleRate) {
outputBuffer =
ensureSpaceForAdditionalFrames(
outputBuffer, outputFrameCount, /* additionalFrameCount= */ 1);
for (int i = 0; i < channelCount; i++) {
outputBuffer[outputFrameCount * channelCount + i] = interpolate(pitchBuffer,
position * channelCount + i, oldSampleRate, newSampleRate);
outputBuffer[outputFrameCount * channelCount + i] =
interpolate(pitchBuffer, position * channelCount + i, oldSampleRate, newSampleRate);
}
newRatePosition++;
outputFrameCount++;
@ -575,7 +387,7 @@ import java.util.Arrays;
newRatePosition = 0;
}
}
removePitchFrames(position);
removePitchFrames(pitchFrameCount - 1);
}
private int skipPitchPeriod(short[] samples, int position, float speed, int period) {
@ -635,49 +447,36 @@ import java.util.Arrays;
if (inputFrameCount < maxRequiredFrameCount) {
return;
}
int numSamples = inputFrameCount;
int position = 0, period, newSamples;
int frameCount = inputFrameCount;
int positionFrames = 0;
do {
if (remainingInputToCopyFrameCount > 0) {
newSamples = copyInputToOutput(position);
position += newSamples;
positionFrames += copyInputToOutput(positionFrames);
} else {
period = findPitchPeriod(inputBuffer, position, true);
int period = findPitchPeriod(inputBuffer, positionFrames);
if (speed > 1.0) {
newSamples = skipPitchPeriod(inputBuffer, position, speed, period);
position += period + newSamples;
positionFrames += period + skipPitchPeriod(inputBuffer, positionFrames, speed, period);
} else {
newSamples = insertPitchPeriod(inputBuffer, position, speed, period);
position += newSamples;
positionFrames += insertPitchPeriod(inputBuffer, positionFrames, speed, period);
}
}
} while (position + maxRequiredFrameCount <= numSamples);
removeProcessedInputFrames(position);
} while (positionFrames + maxRequiredFrameCount <= frameCount);
removeProcessedInputFrames(positionFrames);
}
private void processStreamInput() {
int originalNumOutputSamples = outputFrameCount;
// Resample as many pitch periods as we have buffered on the input.
int originalOutputFrameCount = outputFrameCount;
float s = speed / pitch;
float r = rate;
boolean useChordPitch = false;
if (!useChordPitch) {
r *= pitch;
}
float r = rate * pitch;
if (s > 1.00001 || s < 0.99999) {
changeSpeed(s);
} else {
copyToOutput(inputBuffer, 0, inputFrameCount);
inputFrameCount = 0;
}
if (useChordPitch) {
if (pitch != 1.0f) {
adjustPitch(originalNumOutputSamples);
}
} else if (r != 1.0f) {
adjustRate(r, originalNumOutputSamples);
if (r != 1.0f) {
adjustRate(r, originalOutputFrameCount);
}
}
@ -703,35 +502,4 @@ import java.util.Arrays;
}
}
private static void overlapAddWithSeparation(
int numSamples,
int numChannels,
int separation,
short out[],
int outPos,
short rampDown[],
int rampDownPos,
short rampUp[],
int rampUpPos)
{
for(int i = 0; i < numChannels; i++) {
int o = outPos*numChannels + i;
int u = rampUpPos*numChannels + i;
int d = rampDownPos*numChannels + i;
for(int t = 0; t < numSamples + separation; t++) {
if(t < separation) {
out[o] = (short)(rampDown[d]*(numSamples - t)/numSamples);
d += numChannels;
} else if(t < numSamples) {
out[o] = (short)((rampDown[d]*(numSamples - t) + rampUp[u]*(t - separation))/numSamples);
d += numChannels;
u += numChannels;
} else {
out[o] = (short)(rampUp[u]*(t - separation)/numSamples);
u += numChannels;
}
o += numChannels;
}
}
}
}

View file

@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import android.support.annotation.Nullable;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.C.Encoding;
import org.telegram.messenger.exoplayer2.Format;
import org.telegram.messenger.exoplayer2.util.Assertions;
import org.telegram.messenger.exoplayer2.util.Util;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.C.Encoding;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;

View file

@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.audio;
package com.google.android.exoplayer2.audio;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.C.Encoding;
import org.telegram.messenger.exoplayer2.Format;
import org.telegram.messenger.exoplayer2.util.Util;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.C.Encoding;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.util.Util;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

View file

@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.decoder;
package com.google.android.exoplayer2.decoder;
import org.telegram.messenger.exoplayer2.C;
import com.google.android.exoplayer2.C;
/**
* Base class for buffers with flags.

View file

@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.decoder;
package com.google.android.exoplayer2.decoder;
import android.annotation.TargetApi;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.util.Util;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Util;
/**
* Compatibility wrapper for {@link android.media.MediaCodec.CryptoInfo}.

View file

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

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.decoder;
package com.google.android.exoplayer2.decoder;
/**
* Maintains decoder event counts, for debugging purposes only.

View file

@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.decoder;
package com.google.android.exoplayer2.decoder;
import android.support.annotation.IntDef;
import org.telegram.messenger.exoplayer2.C;
import com.google.android.exoplayer2.C;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;

View file

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

View file

@ -13,11 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.decoder;
package com.google.android.exoplayer2.decoder;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.util.Assertions;
import java.util.LinkedList;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Assertions;
import java.util.ArrayDeque;
/**
* Base class for {@link Decoder}s that use their own decode thread.
@ -28,8 +29,8 @@ public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends Outp
private final Thread decodeThread;
private final Object lock;
private final LinkedList<I> queuedInputBuffers;
private final LinkedList<O> queuedOutputBuffers;
private final ArrayDeque<I> queuedInputBuffers;
private final ArrayDeque<O> queuedOutputBuffers;
private final I[] availableInputBuffers;
private final O[] availableOutputBuffers;
@ -48,8 +49,8 @@ public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends Outp
*/
protected SimpleDecoder(I[] inputBuffers, O[] outputBuffers) {
lock = new Object();
queuedInputBuffers = new LinkedList<>();
queuedOutputBuffers = new LinkedList<>();
queuedInputBuffers = new ArrayDeque<>();
queuedOutputBuffers = new ArrayDeque<>();
availableInputBuffers = inputBuffers;
availableInputBufferCount = inputBuffers.length;
for (int i = 0; i < availableInputBufferCount; i++) {
@ -142,7 +143,7 @@ public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends Outp
releaseInputBufferInternal(queuedInputBuffers.removeFirst());
}
while (!queuedOutputBuffers.isEmpty()) {
releaseOutputBufferInternal(queuedOutputBuffers.removeFirst());
queuedOutputBuffers.removeFirst().release();
}
}
}
@ -240,10 +241,10 @@ public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends Outp
synchronized (lock) {
if (flushed) {
releaseOutputBufferInternal(outputBuffer);
outputBuffer.release();
} else if (outputBuffer.isDecodeOnly()) {
skippedOutputBufferCount++;
releaseOutputBufferInternal(outputBuffer);
outputBuffer.release();
} else {
outputBuffer.skippedOutputBufferCount = skippedOutputBufferCount;
skippedOutputBufferCount = 0;
@ -292,14 +293,13 @@ public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends Outp
* Decodes the {@code inputBuffer} and stores any decoded output in {@code outputBuffer}.
*
* @param inputBuffer The buffer to decode.
* @param outputBuffer The output buffer to store decoded data. The flag
* {@link C#BUFFER_FLAG_DECODE_ONLY} will be set if the same flag is set on
* {@code inputBuffer}, but may be set/unset as required. If the flag is set when the call
* returns then the output buffer will not be made available to dequeue. The output buffer
* may not have been populated in this case.
* @param outputBuffer The output buffer to store decoded data. The flag {@link
* C#BUFFER_FLAG_DECODE_ONLY} will be set if the same flag is set on {@code inputBuffer}, but
* may be set/unset as required. If the flag is set when the call returns then the output
* buffer will not be made available to dequeue. The output buffer may not have been populated
* in this case.
* @param reset Whether the decoder must be reset before decoding.
* @return A decoder exception if an error occurred, or null if decoding was successful.
*/
protected abstract E decode(I inputBuffer, O outputBuffer, boolean reset);
protected abstract @Nullable E decode(I inputBuffer, O outputBuffer, boolean reset);
}

View file

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

View file

@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.drm;
package com.google.android.exoplayer2.drm;
import android.util.Log;
import org.telegram.messenger.exoplayer2.util.Util;
import com.google.android.exoplayer2.util.Util;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.drm;
package com.google.android.exoplayer2.drm;
/**
* Thrown when a non-platform component fails to decrypt data.

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.drm;
package com.google.android.exoplayer2.drm;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
@ -22,13 +22,14 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.Nullable;
import android.util.Log;
import android.util.Pair;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.drm.DefaultDrmSessionEventListener.EventDispatcher;
import org.telegram.messenger.exoplayer2.drm.ExoMediaDrm.DefaultKeyRequest;
import org.telegram.messenger.exoplayer2.drm.ExoMediaDrm.KeyRequest;
import org.telegram.messenger.exoplayer2.drm.ExoMediaDrm.ProvisionRequest;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
import com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest;
import com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest;
import com.google.android.exoplayer2.util.EventDispatcher;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@ -77,11 +78,10 @@ import java.util.UUID;
private final ExoMediaDrm<T> mediaDrm;
private final ProvisioningManager<T> provisioningManager;
private final byte[] initData;
private final String mimeType;
private final SchemeData schemeData;
private final @DefaultDrmSessionManager.Mode int mode;
private final HashMap<String, String> optionalKeyRequestParameters;
private final EventDispatcher eventDispatcher;
private final EventDispatcher<DefaultDrmSessionEventListener> eventDispatcher;
private final int initialDrmRequestRetryCount;
/* package */ final MediaDrmCallback callback;
@ -97,15 +97,20 @@ import java.util.UUID;
private byte[] sessionId;
private byte[] offlineLicenseKeySetId;
private Object currentKeyRequest;
private Object currentProvisionRequest;
/**
* Instantiates a new DRM session.
*
* @param uuid The UUID of the drm scheme.
* @param mediaDrm The media DRM.
* @param provisioningManager The manager for provisioning.
* @param initData The DRM init data.
* @param schemeData The DRM data for this session, or null if a {@code offlineLicenseKeySetId} is
* provided.
* @param mode The DRM mode.
* @param offlineLicenseKeySetId The offlineLicense KeySetId.
* @param offlineLicenseKeySetId The offline license key set identifier, or null when not using
* offline keys.
* @param optionalKeyRequestParameters The optional key request parameters.
* @param callback The media DRM callback.
* @param playbackLooper The playback looper.
@ -117,20 +122,20 @@ import java.util.UUID;
UUID uuid,
ExoMediaDrm<T> mediaDrm,
ProvisioningManager<T> provisioningManager,
byte[] initData,
String mimeType,
@Nullable SchemeData schemeData,
@DefaultDrmSessionManager.Mode int mode,
byte[] offlineLicenseKeySetId,
@Nullable byte[] offlineLicenseKeySetId,
HashMap<String, String> optionalKeyRequestParameters,
MediaDrmCallback callback,
Looper playbackLooper,
EventDispatcher eventDispatcher,
EventDispatcher<DefaultDrmSessionEventListener> eventDispatcher,
int initialDrmRequestRetryCount) {
this.uuid = uuid;
this.provisioningManager = provisioningManager;
this.mediaDrm = mediaDrm;
this.mode = mode;
this.offlineLicenseKeySetId = offlineLicenseKeySetId;
this.schemeData = offlineLicenseKeySetId == null ? schemeData : null;
this.optionalKeyRequestParameters = optionalKeyRequestParameters;
this.callback = callback;
this.initialDrmRequestRetryCount = initialDrmRequestRetryCount;
@ -141,14 +146,6 @@ import java.util.UUID;
requestHandlerThread = new HandlerThread("DrmRequestHandler");
requestHandlerThread.start();
postRequestHandler = new PostRequestHandler(requestHandlerThread.getLooper());
if (offlineLicenseKeySetId == null) {
this.initData = initData;
this.mimeType = mimeType;
} else {
this.initData = null;
this.mimeType = null;
}
}
// Life cycle.
@ -177,6 +174,8 @@ import java.util.UUID;
requestHandlerThread = null;
mediaCrypto = null;
lastException = null;
currentKeyRequest = null;
currentProvisionRequest = null;
if (sessionId != null) {
mediaDrm.closeSession(sessionId);
sessionId = null;
@ -187,18 +186,42 @@ import java.util.UUID;
}
public boolean hasInitData(byte[] initData) {
return Arrays.equals(this.initData, initData);
return Arrays.equals(schemeData != null ? schemeData.data : null, initData);
}
public boolean hasSessionId(byte[] sessionId) {
return Arrays.equals(this.sessionId, sessionId);
}
@SuppressWarnings("deprecation")
public void onMediaDrmEvent(int what) {
if (!isOpen()) {
return;
}
switch (what) {
case ExoMediaDrm.EVENT_KEY_REQUIRED:
doLicense(false);
break;
case ExoMediaDrm.EVENT_KEY_EXPIRED:
// When an already expired key is loaded MediaDrm sends this event immediately. Ignore
// this event if the state isn't STATE_OPENED_WITH_KEYS yet which means we're still
// waiting for key response.
onKeysExpired();
break;
case ExoMediaDrm.EVENT_PROVISION_REQUIRED:
state = STATE_OPENED;
provisioningManager.provisionRequired(this);
break;
default:
break;
}
}
// Provisioning implementation.
public void provision() {
ProvisionRequest request = mediaDrm.getProvisionRequest();
postRequestHandler.obtainMessage(MSG_PROVISION, request, true).sendToTarget();
currentProvisionRequest = mediaDrm.getProvisionRequest();
postRequestHandler.post(MSG_PROVISION, currentProvisionRequest, /* allowRetry= */ true);
}
public void onProvisionCompleted() {
@ -271,11 +294,12 @@ import java.util.UUID;
return false;
}
private void onProvisionResponse(Object response) {
if (state != STATE_OPENING && !isOpen()) {
private void onProvisionResponse(Object request, Object response) {
if (request != currentProvisionRequest || (state != STATE_OPENING && !isOpen())) {
// This event is stale.
return;
}
currentProvisionRequest = null;
if (response instanceof Exception) {
provisioningManager.onProvisionError((Exception) response);
@ -309,7 +333,7 @@ import java.util.UUID;
onError(new KeysExpiredException());
} else {
state = STATE_OPENED_WITH_KEYS;
eventDispatcher.drmKeysRestored();
eventDispatcher.dispatch(DefaultDrmSessionEventListener::onDrmKeysRestored);
}
}
break;
@ -356,24 +380,30 @@ import java.util.UUID;
private void postKeyRequest(int type, boolean allowRetry) {
byte[] scope = type == ExoMediaDrm.KEY_TYPE_RELEASE ? offlineLicenseKeySetId : sessionId;
try {
KeyRequest request = mediaDrm.getKeyRequest(scope, initData, mimeType, type,
optionalKeyRequestParameters);
if (C.CLEARKEY_UUID.equals(uuid)) {
request = new DefaultKeyRequest(ClearKeyUtil.adjustRequestData(request.getData()),
request.getDefaultUrl());
byte[] initData = null;
String mimeType = null;
String licenseServerUrl = null;
if (schemeData != null) {
initData = schemeData.data;
mimeType = schemeData.mimeType;
licenseServerUrl = schemeData.licenseServerUrl;
}
postRequestHandler.obtainMessage(MSG_KEYS, request, allowRetry).sendToTarget();
try {
KeyRequest mediaDrmKeyRequest =
mediaDrm.getKeyRequest(scope, initData, mimeType, type, optionalKeyRequestParameters);
currentKeyRequest = Pair.create(mediaDrmKeyRequest, licenseServerUrl);
postRequestHandler.post(MSG_KEYS, currentKeyRequest, allowRetry);
} catch (Exception e) {
onKeysError(e);
}
}
private void onKeyResponse(Object response) {
if (!isOpen()) {
private void onKeyResponse(Object request, Object response) {
if (request != currentKeyRequest || !isOpen()) {
// This event is stale.
return;
}
currentKeyRequest = null;
if (response instanceof Exception) {
onKeysError((Exception) response);
@ -382,12 +412,9 @@ import java.util.UUID;
try {
byte[] responseData = (byte[]) response;
if (C.CLEARKEY_UUID.equals(uuid)) {
responseData = ClearKeyUtil.adjustResponseData(responseData);
}
if (mode == DefaultDrmSessionManager.MODE_RELEASE) {
mediaDrm.provideKeyResponse(offlineLicenseKeySetId, responseData);
eventDispatcher.drmKeysRemoved();
eventDispatcher.dispatch(DefaultDrmSessionEventListener::onDrmKeysRestored);
} else {
byte[] keySetId = mediaDrm.provideKeyResponse(sessionId, responseData);
if ((mode == DefaultDrmSessionManager.MODE_DOWNLOAD
@ -396,7 +423,7 @@ import java.util.UUID;
offlineLicenseKeySetId = keySetId;
}
state = STATE_OPENED_WITH_KEYS;
eventDispatcher.drmKeysLoaded();
eventDispatcher.dispatch(DefaultDrmSessionEventListener::onDrmKeysLoaded);
}
} catch (Exception e) {
onKeysError(e);
@ -420,7 +447,7 @@ import java.util.UUID;
private void onError(final Exception e) {
lastException = new DrmSessionException(e);
eventDispatcher.drmSessionManagerError(e);
eventDispatcher.dispatch(listener -> listener.onDrmSessionManagerError(e));
if (state != STATE_OPENED_WITH_KEYS) {
state = STATE_ERROR;
}
@ -430,30 +457,7 @@ import java.util.UUID;
return state == STATE_OPENED || state == STATE_OPENED_WITH_KEYS;
}
@SuppressWarnings("deprecation")
public void onMediaDrmEvent(int what) {
if (!isOpen()) {
return;
}
switch (what) {
case ExoMediaDrm.EVENT_KEY_REQUIRED:
doLicense(false);
break;
case ExoMediaDrm.EVENT_KEY_EXPIRED:
// When an already expired key is loaded MediaDrm sends this event immediately. Ignore
// this event if the state isn't STATE_OPENED_WITH_KEYS yet which means we're still
// waiting for key response.
onKeysExpired();
break;
case ExoMediaDrm.EVENT_PROVISION_REQUIRED:
state = STATE_OPENED;
provisioningManager.provisionRequired(this);
break;
default:
break;
}
}
// Internal classes.
@SuppressLint("HandlerLeak")
private class PostResponseHandler extends Handler {
@ -464,12 +468,15 @@ import java.util.UUID;
@Override
public void handleMessage(Message msg) {
Pair<?, ?> requestAndResponse = (Pair<?, ?>) msg.obj;
Object request = requestAndResponse.first;
Object response = requestAndResponse.second;
switch (msg.what) {
case MSG_PROVISION:
onProvisionResponse(msg.obj);
onProvisionResponse(request, response);
break;
case MSG_KEYS:
onKeyResponse(msg.obj);
onKeyResponse(request, response);
break;
default:
break;
@ -486,21 +493,27 @@ import java.util.UUID;
super(backgroundLooper);
}
Message obtainMessage(int what, Object object, boolean allowRetry) {
return obtainMessage(what, allowRetry ? 1 : 0 /* allow retry*/, 0 /* error count */,
object);
void post(int what, Object request, boolean allowRetry) {
int allowRetryInt = allowRetry ? 1 : 0;
int errorCount = 0;
obtainMessage(what, allowRetryInt, errorCount, request).sendToTarget();
}
@Override
@SuppressWarnings("unchecked")
public void handleMessage(Message msg) {
Object request = msg.obj;
Object response;
try {
switch (msg.what) {
case MSG_PROVISION:
response = callback.executeProvisionRequest(uuid, (ProvisionRequest) msg.obj);
response = callback.executeProvisionRequest(uuid, (ProvisionRequest) request);
break;
case MSG_KEYS:
response = callback.executeKeyRequest(uuid, (KeyRequest) msg.obj);
Pair<KeyRequest, String> keyRequest = (Pair<KeyRequest, String>) request;
KeyRequest mediaDrmKeyRequest = keyRequest.first;
String licenseServerUrl = keyRequest.second;
response = callback.executeKeyRequest(uuid, mediaDrmKeyRequest, licenseServerUrl);
break;
default:
throw new RuntimeException();
@ -511,7 +524,7 @@ import java.util.UUID;
}
response = e;
}
postResponseHandler.obtainMessage(msg.what, response).sendToTarget();
postResponseHandler.obtainMessage(msg.what, Pair.create(request, response)).sendToTarget();
}
private boolean maybeRetryRequest(Message originalMsg) {
@ -534,5 +547,4 @@ import java.util.UUID;
}
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (C) 2018 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.drm;
import com.google.android.exoplayer2.Player;
/** Listener of {@link DefaultDrmSessionManager} events. */
public interface DefaultDrmSessionEventListener {
/** Called each time keys are loaded. */
void onDrmKeysLoaded();
/**
* Called when a drm error occurs.
*
* <p>This method being called does not indicate that playback has failed, or that it will fail.
* The player may be able to recover from the error and continue. Hence applications should
* <em>not</em> implement this method to display a user visible error or initiate an application
* level retry ({@link Player.EventListener#onPlayerError} is the appropriate place to implement
* such behavior). This method is called to provide the application with an opportunity to log the
* error if it wishes to do so.
*
* @param error The corresponding exception.
*/
void onDrmSessionManagerError(Exception error);
/** Called each time offline keys are restored. */
void onDrmKeysRestored();
/** Called each time offline keys are removed. */
void onDrmKeysRemoved();
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.drm;
package com.google.android.exoplayer2.drm;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
@ -24,16 +24,15 @@ import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.drm.DefaultDrmSession.ProvisioningManager;
import org.telegram.messenger.exoplayer2.drm.DefaultDrmSessionEventListener.EventDispatcher;
import org.telegram.messenger.exoplayer2.drm.DrmInitData.SchemeData;
import org.telegram.messenger.exoplayer2.drm.DrmSession.DrmSessionException;
import org.telegram.messenger.exoplayer2.drm.ExoMediaDrm.OnEventListener;
import org.telegram.messenger.exoplayer2.extractor.mp4.PsshAtomUtil;
import org.telegram.messenger.exoplayer2.util.Assertions;
import org.telegram.messenger.exoplayer2.util.MimeTypes;
import org.telegram.messenger.exoplayer2.util.Util;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.drm.DefaultDrmSession.ProvisioningManager;
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException;
import com.google.android.exoplayer2.drm.ExoMediaDrm.OnEventListener;
import com.google.android.exoplayer2.extractor.mp4.PsshAtomUtil;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.EventDispatcher;
import com.google.android.exoplayer2.util.Util;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@ -89,13 +88,12 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
public static final int INITIAL_DRM_REQUEST_RETRY_COUNT = 3;
private static final String TAG = "DefaultDrmSessionMgr";
private static final String CENC_SCHEME_MIME_TYPE = "cenc";
private final UUID uuid;
private final ExoMediaDrm<T> mediaDrm;
private final MediaDrmCallback callback;
private final HashMap<String, String> optionalKeyRequestParameters;
private final EventDispatcher eventDispatcher;
private final EventDispatcher<DefaultDrmSessionEventListener> eventDispatcher;
private final boolean multiSession;
private final int initialDrmRequestRetryCount;
@ -356,7 +354,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
this.mediaDrm = mediaDrm;
this.callback = callback;
this.optionalKeyRequestParameters = optionalKeyRequestParameters;
this.eventDispatcher = new EventDispatcher();
this.eventDispatcher = new EventDispatcher<>();
this.multiSession = multiSession;
this.initialDrmRequestRetryCount = initialDrmRequestRetryCount;
mode = MODE_PLAYBACK;
@ -509,17 +507,14 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
}
}
byte[] initData = null;
String mimeType = null;
SchemeData schemeData = null;
if (offlineLicenseKeySetId == null) {
SchemeData data = getSchemeData(drmInitData, uuid, false);
if (data == null) {
schemeData = getSchemeData(drmInitData, uuid, false);
if (schemeData == null) {
final MissingSchemeDataException error = new MissingSchemeDataException(uuid);
eventDispatcher.drmSessionManagerError(error);
eventDispatcher.dispatch(listener -> listener.onDrmSessionManagerError(error));
return new ErrorStateDrmSession<>(new DrmSessionException(error));
}
initData = getSchemeInitData(data, uuid);
mimeType = getSchemeMimeType(data, uuid);
}
DefaultDrmSession<T> session;
@ -528,6 +523,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
} else {
// Only use an existing session if it has matching init data.
session = null;
byte[] initData = schemeData != null ? schemeData.data : null;
for (DefaultDrmSession<T> existingSession : sessions) {
if (existingSession.hasInitData(initData)) {
session = existingSession;
@ -543,8 +539,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
uuid,
mediaDrm,
this,
initData,
mimeType,
schemeData,
mode,
offlineLicenseKeySetId,
optionalKeyRequestParameters,
@ -650,31 +645,6 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
return matchingSchemeDatas.get(0);
}
private static byte[] getSchemeInitData(SchemeData data, UUID uuid) {
byte[] schemeInitData = data.data;
if (Util.SDK_INT < 21) {
// Prior to L the Widevine CDM required data to be extracted from the PSSH atom.
byte[] psshData = PsshAtomUtil.parseSchemeSpecificData(schemeInitData, uuid);
if (psshData == null) {
// Extraction failed. schemeData isn't a Widevine PSSH atom, so leave it unchanged.
} else {
schemeInitData = psshData;
}
}
return schemeInitData;
}
private static String getSchemeMimeType(SchemeData data, UUID uuid) {
String schemeMimeType = data.mimeType;
if (Util.SDK_INT < 26 && C.CLEARKEY_UUID.equals(uuid)
&& (MimeTypes.VIDEO_MP4.equals(schemeMimeType)
|| MimeTypes.AUDIO_MP4.equals(schemeMimeType))) {
// Prior to API level 26 the ClearKey CDM only accepted "cenc" as the scheme for MP4.
schemeMimeType = CENC_SCHEME_MIME_TYPE;
}
return schemeMimeType;
}
@SuppressLint("HandlerLeak")
private class MediaDrmHandler extends Handler {

View file

@ -13,15 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.drm;
package com.google.android.exoplayer2.drm;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.drm.DrmInitData.SchemeData;
import org.telegram.messenger.exoplayer2.util.Assertions;
import org.telegram.messenger.exoplayer2.util.Util;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
@ -266,9 +266,9 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
* applies to all schemes).
*/
private final UUID uuid;
/**
* The mimeType of {@link #data}.
*/
/** The URL of the server to which license requests should be made. May be null if unknown. */
public final @Nullable String licenseServerUrl;
/** The mimeType of {@link #data}. */
public final String mimeType;
/**
* The initialization data. May be null for scheme support checks only.
@ -297,7 +297,25 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
* @param requiresSecureDecryption See {@link #requiresSecureDecryption}.
*/
public SchemeData(UUID uuid, String mimeType, byte[] data, boolean requiresSecureDecryption) {
this(uuid, /* licenseServerUrl= */ null, mimeType, data, requiresSecureDecryption);
}
/**
* @param uuid The {@link UUID} of the DRM scheme, or {@link C#UUID_NIL} if the data is
* universal (i.e. applies to all schemes).
* @param licenseServerUrl See {@link #licenseServerUrl}.
* @param mimeType See {@link #mimeType}.
* @param data See {@link #data}.
* @param requiresSecureDecryption See {@link #requiresSecureDecryption}.
*/
public SchemeData(
UUID uuid,
@Nullable String licenseServerUrl,
String mimeType,
byte[] data,
boolean requiresSecureDecryption) {
this.uuid = Assertions.checkNotNull(uuid);
this.licenseServerUrl = licenseServerUrl;
this.mimeType = Assertions.checkNotNull(mimeType);
this.data = data;
this.requiresSecureDecryption = requiresSecureDecryption;
@ -305,6 +323,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
/* package */ SchemeData(Parcel in) {
uuid = new UUID(in.readLong(), in.readLong());
licenseServerUrl = in.readString();
mimeType = in.readString();
data = in.createByteArray();
requiresSecureDecryption = in.readByte() != 0;
@ -346,7 +365,9 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
return true;
}
SchemeData other = (SchemeData) obj;
return mimeType.equals(other.mimeType) && Util.areEqual(uuid, other.uuid)
return Util.areEqual(licenseServerUrl, other.licenseServerUrl)
&& Util.areEqual(mimeType, other.mimeType)
&& Util.areEqual(uuid, other.uuid)
&& Arrays.equals(data, other.data);
}
@ -354,6 +375,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
public int hashCode() {
if (hashCode == 0) {
int result = uuid.hashCode();
result = 31 * result + (licenseServerUrl == null ? 0 : licenseServerUrl.hashCode());
result = 31 * result + mimeType.hashCode();
result = 31 * result + Arrays.hashCode(data);
hashCode = result;
@ -372,6 +394,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(uuid.getMostSignificantBits());
dest.writeLong(uuid.getLeastSignificantBits());
dest.writeString(licenseServerUrl);
dest.writeString(mimeType);
dest.writeByteArray(data);
dest.writeByte((byte) (requiresSecureDecryption ? 1 : 0));

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.drm;
package com.google.android.exoplayer2.drm;
import android.annotation.TargetApi;
import android.media.MediaDrm;

View file

@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.drm;
package com.google.android.exoplayer2.drm;
import android.annotation.TargetApi;
import android.os.Looper;
import org.telegram.messenger.exoplayer2.drm.DrmInitData.SchemeData;
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
/**
* Manages a DRM session.

View file

@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.drm;
package com.google.android.exoplayer2.drm;
import org.telegram.messenger.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Assertions;
import java.util.Map;
/** A {@link DrmSession} that's in a terminal error state. */

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.drm;
package com.google.android.exoplayer2.drm;
/**
* An opaque {@link android.media.MediaCrypto} equivalent.

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.drm;
package com.google.android.exoplayer2.drm;
import android.media.DeniedByServerException;
import android.media.MediaCryptoException;

View file

@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.drm;
package com.google.android.exoplayer2.drm;
import android.annotation.TargetApi;
import android.media.MediaCrypto;
import org.telegram.messenger.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Assertions;
/**
* An {@link ExoMediaCrypto} implementation that wraps the framework {@link MediaCrypto}.

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.drm;
package com.google.android.exoplayer2.drm;
import android.annotation.TargetApi;
import android.media.DeniedByServerException;
@ -25,9 +25,11 @@ import android.media.NotProvisionedException;
import android.media.UnsupportedSchemeException;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.util.Assertions;
import org.telegram.messenger.exoplayer2.util.Util;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.extractor.mp4.PsshAtomUtil;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -40,6 +42,8 @@ import java.util.UUID;
@TargetApi(23)
public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto> {
private static final String CENC_SCHEME_MIME_TYPE = "cenc";
private final UUID uuid;
private final MediaDrm mediaDrm;
@ -67,6 +71,9 @@ public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto
uuid = Util.SDK_INT < 27 && C.CLEARKEY_UUID.equals(uuid) ? C.COMMON_PSSH_UUID : uuid;
this.uuid = uuid;
this.mediaDrm = new MediaDrm(uuid);
if (C.WIDEVINE_UUID.equals(uuid) && needsForceL3Workaround()) {
mediaDrm.setPropertyString("securityLevel", "L3");
}
}
@Override
@ -116,14 +123,49 @@ public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto
@Override
public KeyRequest getKeyRequest(byte[] scope, byte[] init, String mimeType, int keyType,
HashMap<String, String> optionalParameters) throws NotProvisionedException {
// Prior to L the Widevine CDM required data to be extracted from the PSSH atom. Some Amazon
// devices also required data to be extracted from the PSSH atom for PlayReady.
if ((Util.SDK_INT < 21 && C.WIDEVINE_UUID.equals(uuid))
|| (C.PLAYREADY_UUID.equals(uuid)
&& "Amazon".equals(Util.MANUFACTURER)
&& ("AFTB".equals(Util.MODEL) // Fire TV Gen 1
|| "AFTS".equals(Util.MODEL) // Fire TV Gen 2
|| "AFTM".equals(Util.MODEL)))) { // Fire TV Stick Gen 1
byte[] psshData = PsshAtomUtil.parseSchemeSpecificData(init, uuid);
if (psshData == null) {
// Extraction failed. schemeData isn't a PSSH atom, so leave it unchanged.
} else {
init = psshData;
}
}
// Prior to API level 26 the ClearKey CDM only accepted "cenc" as the scheme for MP4.
if (Util.SDK_INT < 26
&& C.CLEARKEY_UUID.equals(uuid)
&& (MimeTypes.VIDEO_MP4.equals(mimeType) || MimeTypes.AUDIO_MP4.equals(mimeType))) {
mimeType = CENC_SCHEME_MIME_TYPE;
}
final MediaDrm.KeyRequest request = mediaDrm.getKeyRequest(scope, init, mimeType, keyType,
optionalParameters);
return new DefaultKeyRequest(request.getData(), request.getDefaultUrl());
byte[] requestData = request.getData();
if (C.CLEARKEY_UUID.equals(uuid)) {
requestData = ClearKeyUtil.adjustRequestData(requestData);
}
return new DefaultKeyRequest(requestData, request.getDefaultUrl());
}
@Override
public byte[] provideKeyResponse(byte[] scope, byte[] response)
throws NotProvisionedException, DeniedByServerException {
if (C.CLEARKEY_UUID.equals(uuid)) {
response = ClearKeyUtil.adjustResponseData(response);
}
return mediaDrm.provideKeyResponse(scope, response);
}
@ -183,4 +225,12 @@ public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto
forceAllowInsecureDecoderComponents);
}
/**
* Returns whether the device codec is known to fail if security level L1 is used.
*
* <p>See <a href="https://github.com/google/ExoPlayer/issues/4413">GitHub issue #4413</a>.
*/
private static boolean needsForceL3Workaround() {
return "ASUS_Z00AD".equals(Util.MODEL);
}
}

View file

@ -13,20 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.drm;
package com.google.android.exoplayer2.drm;
import android.annotation.TargetApi;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.drm.ExoMediaDrm.KeyRequest;
import org.telegram.messenger.exoplayer2.drm.ExoMediaDrm.ProvisionRequest;
import org.telegram.messenger.exoplayer2.upstream.DataSourceInputStream;
import org.telegram.messenger.exoplayer2.upstream.DataSpec;
import org.telegram.messenger.exoplayer2.upstream.HttpDataSource;
import org.telegram.messenger.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException;
import org.telegram.messenger.exoplayer2.util.Assertions;
import org.telegram.messenger.exoplayer2.util.Util;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest;
import com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest;
import com.google.android.exoplayer2.upstream.DataSourceInputStream;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
@ -108,13 +109,19 @@ public final class HttpMediaDrmCallback implements MediaDrmCallback {
@Override
public byte[] executeProvisionRequest(UUID uuid, ProvisionRequest request) throws IOException {
String url = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData());
String url =
request.getDefaultUrl() + "&signedRequest=" + Util.fromUtf8Bytes(request.getData());
return executePost(dataSourceFactory, url, new byte[0], null);
}
@Override
public byte[] executeKeyRequest(UUID uuid, KeyRequest request) throws Exception {
public byte[] executeKeyRequest(
UUID uuid, KeyRequest request, @Nullable String mediaProvidedLicenseServerUrl)
throws Exception {
String url = request.getDefaultUrl();
if (TextUtils.isEmpty(url)) {
url = mediaProvidedLicenseServerUrl;
}
if (forceDefaultLicenseUrl || TextUtils.isEmpty(url)) {
url = defaultLicenseUrl;
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.drm;
package com.google.android.exoplayer2.drm;
/**
* Thrown when the drm keys loaded into an open session expire.

View file

@ -13,11 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.drm;
package com.google.android.exoplayer2.drm;
import org.telegram.messenger.exoplayer2.drm.ExoMediaDrm.KeyRequest;
import org.telegram.messenger.exoplayer2.drm.ExoMediaDrm.ProvisionRequest;
import org.telegram.messenger.exoplayer2.util.Assertions;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest;
import com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest;
import com.google.android.exoplayer2.util.Assertions;
import java.io.IOException;
import java.util.UUID;
@ -44,7 +45,9 @@ public final class LocalMediaDrmCallback implements MediaDrmCallback {
}
@Override
public byte[] executeKeyRequest(UUID uuid, KeyRequest request) throws Exception {
public byte[] executeKeyRequest(
UUID uuid, KeyRequest request, @Nullable String mediaProvidedLicenseServerUrl)
throws Exception {
return keyResponse;
}

View file

@ -13,10 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.drm;
package com.google.android.exoplayer2.drm;
import org.telegram.messenger.exoplayer2.drm.ExoMediaDrm.KeyRequest;
import org.telegram.messenger.exoplayer2.drm.ExoMediaDrm.ProvisionRequest;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest;
import com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest;
import java.util.UUID;
/**
@ -38,10 +39,13 @@ public interface MediaDrmCallback {
* Executes a key request.
*
* @param uuid The UUID of the content protection scheme.
* @param request The request.
* @param request The request generated by the content decryption module.
* @param mediaProvidedLicenseServerUrl A license server URL provided by the media, or null if the
* media does not include any license server URL.
* @return The response data.
* @throws Exception If an error occurred executing the request.
*/
byte[] executeKeyRequest(UUID uuid, KeyRequest request) throws Exception;
byte[] executeKeyRequest(
UUID uuid, KeyRequest request, @Nullable String mediaProvidedLicenseServerUrl)
throws Exception;
}

View file

@ -13,19 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.drm;
package com.google.android.exoplayer2.drm;
import android.media.MediaDrm;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Pair;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.drm.DefaultDrmSessionManager.Mode;
import org.telegram.messenger.exoplayer2.drm.DrmSession.DrmSessionException;
import org.telegram.messenger.exoplayer2.upstream.HttpDataSource;
import org.telegram.messenger.exoplayer2.upstream.HttpDataSource.Factory;
import org.telegram.messenger.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager.Mode;
import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException;
import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource.Factory;
import com.google.android.exoplayer2.util.Assertions;
import java.util.HashMap;
import java.util.UUID;

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.drm;
package com.google.android.exoplayer2.drm;
import android.support.annotation.IntDef;
import java.lang.annotation.Retention;

View file

@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.drm;
package com.google.android.exoplayer2.drm;
import android.util.Pair;
import org.telegram.messenger.exoplayer2.C;
import com.google.android.exoplayer2.C;
import java.util.Map;
/**

View file

@ -13,42 +13,41 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.ext.ffmpeg;
package com.google.android.exoplayer2.ext.ffmpeg;
import android.os.Handler;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.ExoPlaybackException;
import org.telegram.messenger.exoplayer2.Format;
import org.telegram.messenger.exoplayer2.audio.AudioProcessor;
import org.telegram.messenger.exoplayer2.audio.AudioRendererEventListener;
import org.telegram.messenger.exoplayer2.audio.AudioSink;
import org.telegram.messenger.exoplayer2.audio.DefaultAudioSink;
import org.telegram.messenger.exoplayer2.audio.SimpleDecoderAudioRenderer;
import org.telegram.messenger.exoplayer2.drm.DrmSessionManager;
import org.telegram.messenger.exoplayer2.drm.ExoMediaCrypto;
import org.telegram.messenger.exoplayer2.util.MimeTypes;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.audio.AudioProcessor;
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.audio.AudioSink;
import com.google.android.exoplayer2.audio.DefaultAudioSink;
import com.google.android.exoplayer2.audio.SimpleDecoderAudioRenderer;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes;
import java.util.Collections;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/**
* Decodes and renders audio using FFmpeg.
*/
public final class FfmpegAudioRenderer extends SimpleDecoderAudioRenderer {
/**
* The number of input and output buffers.
*/
/** The number of input and output buffers. */
private static final int NUM_BUFFERS = 16;
/**
* The initial input buffer size. Input buffers are reallocated dynamically if this value is
* insufficient.
*/
private static final int INITIAL_INPUT_BUFFER_SIZE = 960 * 6;
/** The default input buffer size. */
private static final int DEFAULT_INPUT_BUFFER_SIZE = 960 * 6;
private final boolean enableFloatOutput;
private FfmpegDecoder decoder;
private @MonotonicNonNull FfmpegDecoder decoder;
public FfmpegAudioRenderer() {
this(null, null);
this(/* eventHandler= */ null, /* eventListener= */ null);
}
/**
@ -57,9 +56,15 @@ public final class FfmpegAudioRenderer extends SimpleDecoderAudioRenderer {
* @param eventListener A listener of events. May be null if delivery of events is not required.
* @param audioProcessors Optional {@link AudioProcessor}s that will process audio before output.
*/
public FfmpegAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
public FfmpegAudioRenderer(
@Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener,
AudioProcessor... audioProcessors) {
this(eventHandler, eventListener, new DefaultAudioSink(null, audioProcessors), false);
this(
eventHandler,
eventListener,
new DefaultAudioSink(/* audioCapabilities= */ null, audioProcessors),
/* enableFloatOutput= */ false);
}
/**
@ -72,8 +77,11 @@ public final class FfmpegAudioRenderer extends SimpleDecoderAudioRenderer {
* 32-bit float output, any audio processing will be disabled, including playback speed/pitch
* adjustment.
*/
public FfmpegAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
AudioSink audioSink, boolean enableFloatOutput) {
public FfmpegAudioRenderer(
@Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener,
AudioSink audioSink,
boolean enableFloatOutput) {
super(
eventHandler,
eventListener,
@ -86,10 +94,11 @@ public final class FfmpegAudioRenderer extends SimpleDecoderAudioRenderer {
@Override
protected int supportsFormatInternal(DrmSessionManager<ExoMediaCrypto> drmSessionManager,
Format format) {
String sampleMimeType = format.sampleMimeType;
if (!MimeTypes.isAudio(sampleMimeType)) {
Assertions.checkNotNull(format.sampleMimeType);
if (!MimeTypes.isAudio(format.sampleMimeType)) {
return FORMAT_UNSUPPORTED_TYPE;
} else if (!FfmpegLibrary.supportsFormat(sampleMimeType) || !isOutputSupported(format)) {
} else if (!FfmpegLibrary.supportsFormat(format.sampleMimeType, format.pcmEncoding)
|| !isOutputSupported(format)) {
return FORMAT_UNSUPPORTED_SUBTYPE;
} else if (!supportsFormatDrm(drmSessionManager, format.drmInitData)) {
return FORMAT_UNSUPPORTED_DRM;
@ -106,18 +115,33 @@ public final class FfmpegAudioRenderer extends SimpleDecoderAudioRenderer {
@Override
protected FfmpegDecoder createDecoder(Format format, ExoMediaCrypto mediaCrypto)
throws FfmpegDecoderException {
decoder = new FfmpegDecoder(NUM_BUFFERS, NUM_BUFFERS, INITIAL_INPUT_BUFFER_SIZE,
format.sampleMimeType, format.initializationData, shouldUseFloatOutput(format));
int initialInputBufferSize =
format.maxInputSize != Format.NO_VALUE ? format.maxInputSize : DEFAULT_INPUT_BUFFER_SIZE;
decoder =
new FfmpegDecoder(
NUM_BUFFERS, NUM_BUFFERS, initialInputBufferSize, format, shouldUseFloatOutput(format));
return decoder;
}
@Override
public Format getOutputFormat() {
Assertions.checkNotNull(decoder);
int channelCount = decoder.getChannelCount();
int sampleRate = decoder.getSampleRate();
@C.PcmEncoding int encoding = decoder.getEncoding();
return Format.createAudioSampleFormat(null, MimeTypes.AUDIO_RAW, null, Format.NO_VALUE,
Format.NO_VALUE, channelCount, sampleRate, encoding, null, null, 0, null);
return Format.createAudioSampleFormat(
/* id= */ null,
MimeTypes.AUDIO_RAW,
/* codecs= */ null,
Format.NO_VALUE,
Format.NO_VALUE,
channelCount,
sampleRate,
encoding,
Collections.emptyList(),
/* drmInitData= */ null,
/* selectionFlags= */ 0,
/* language= */ null);
}
private boolean isOutputSupported(Format inputFormat) {
@ -125,6 +149,7 @@ public final class FfmpegAudioRenderer extends SimpleDecoderAudioRenderer {
}
private boolean shouldUseFloatOutput(Format inputFormat) {
Assertions.checkNotNull(inputFormat.sampleMimeType);
if (!enableFloatOutput || !supportsOutputEncoding(C.ENCODING_PCM_FLOAT)) {
return false;
}

View file

@ -13,14 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.exoplayer2.ext.ffmpeg;
package com.google.android.exoplayer2.ext.ffmpeg;
import org.telegram.messenger.exoplayer2.C;
import org.telegram.messenger.exoplayer2.decoder.DecoderInputBuffer;
import org.telegram.messenger.exoplayer2.decoder.SimpleDecoder;
import org.telegram.messenger.exoplayer2.decoder.SimpleOutputBuffer;
import org.telegram.messenger.exoplayer2.util.MimeTypes;
import org.telegram.messenger.exoplayer2.util.ParsableByteArray;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.decoder.SimpleDecoder;
import com.google.android.exoplayer2.decoder.SimpleOutputBuffer;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.nio.ByteBuffer;
import java.util.List;
@ -30,13 +33,12 @@ import java.util.List;
/* package */ final class FfmpegDecoder extends
SimpleDecoder<DecoderInputBuffer, SimpleOutputBuffer, FfmpegDecoderException> {
// Space for 64 ms of 48 kHz 8 channel 16-bit PCM audio.
private static final int OUTPUT_BUFFER_SIZE_16BIT = 64 * 48 * 8 * 2;
// Space for 64 ms of 48 KhZ 8 channel 32-bit PCM audio.
// Output buffer sizes when decoding PCM mu-law streams, which is the maximum FFmpeg outputs.
private static final int OUTPUT_BUFFER_SIZE_16BIT = 65536;
private static final int OUTPUT_BUFFER_SIZE_32BIT = OUTPUT_BUFFER_SIZE_16BIT * 2;
private final String codecName;
private final byte[] extraData;
private final @Nullable byte[] extraData;
private final @C.Encoding int encoding;
private final int outputBufferSize;
@ -45,15 +47,23 @@ import java.util.List;
private volatile int channelCount;
private volatile int sampleRate;
public FfmpegDecoder(int numInputBuffers, int numOutputBuffers, int initialInputBufferSize,
String mimeType, List<byte[]> initializationData, boolean outputFloat)
public FfmpegDecoder(
int numInputBuffers,
int numOutputBuffers,
int initialInputBufferSize,
Format format,
boolean outputFloat)
throws FfmpegDecoderException {
super(new DecoderInputBuffer[numInputBuffers], new SimpleOutputBuffer[numOutputBuffers]);
codecName = FfmpegLibrary.getCodecName(mimeType);
extraData = getExtraData(mimeType, initializationData);
Assertions.checkNotNull(format.sampleMimeType);
codecName =
Assertions.checkNotNull(
FfmpegLibrary.getCodecName(format.sampleMimeType, format.pcmEncoding));
extraData = getExtraData(format.sampleMimeType, format.initializationData);
encoding = outputFloat ? C.ENCODING_PCM_FLOAT : C.ENCODING_PCM_16BIT;
outputBufferSize = outputFloat ? OUTPUT_BUFFER_SIZE_32BIT : OUTPUT_BUFFER_SIZE_16BIT;
nativeContext = ffmpegInitialize(codecName, extraData, outputFloat);
nativeContext =
ffmpegInitialize(codecName, extraData, outputFloat, format.sampleRate, format.channelCount);
if (nativeContext == 0) {
throw new FfmpegDecoderException("Initialization failed.");
}
@ -81,7 +91,7 @@ import java.util.List;
}
@Override
protected FfmpegDecoderException decode(
protected @Nullable FfmpegDecoderException decode(
DecoderInputBuffer inputBuffer, SimpleOutputBuffer outputBuffer, boolean reset) {
if (reset) {
nativeContext = ffmpegReset(nativeContext, extraData);
@ -100,6 +110,7 @@ import java.util.List;
channelCount = ffmpegGetChannelCount(nativeContext);
sampleRate = ffmpegGetSampleRate(nativeContext);
if (sampleRate == 0 && "alac".equals(codecName)) {
Assertions.checkNotNull(extraData);
// ALAC decoder did not set the sample rate in earlier versions of FFMPEG.
// See https://trac.ffmpeg.org/ticket/6096
ParsableByteArray parsableExtraData = new ParsableByteArray(extraData);
@ -145,7 +156,7 @@ import java.util.List;
* Returns FFmpeg-compatible codec-specific initialization data ("extra data"), or {@code null} if
* not required.
*/
private static byte[] getExtraData(String mimeType, List<byte[]> initializationData) {
private static @Nullable byte[] getExtraData(String mimeType, List<byte[]> initializationData) {
switch (mimeType) {
case MimeTypes.AUDIO_AAC:
case MimeTypes.AUDIO_ALAC:
@ -170,12 +181,20 @@ import java.util.List;
}
}
private native long ffmpegInitialize(String codecName, byte[] extraData, boolean outputFloat);
private native long ffmpegInitialize(
String codecName,
@Nullable byte[] extraData,
boolean outputFloat,
int rawSampleRate,
int rawChannelCount);
private native int ffmpegDecode(long context, ByteBuffer inputData, int inputSize,
ByteBuffer outputData, int outputSize);
private native int ffmpegGetChannelCount(long context);
private native int ffmpegGetSampleRate(long context);
private native long ffmpegReset(long context, byte[] extraData);
private native long ffmpegReset(long context, @Nullable byte[] extraData);
private native void ffmpegRelease(long context);
}

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