1
0
mirror of https://github.com/MGislv/NekoX.git synced 2024-06-30 10:14:04 +00:00

Update to 5.4.0

This commit is contained in:
DrKLO 2019-03-03 20:40:48 +00:00
parent f338a88eb4
commit e397bd9afd
200 changed files with 16433 additions and 7823 deletions

View File

@ -13,7 +13,7 @@ configurations {
dependencies { dependencies {
compileOnly 'org.checkerframework:checker-qual:2.5.2' compileOnly 'org.checkerframework:checker-qual:2.5.2'
compileOnly 'org.checkerframework:checker-compat-qual:2.5.0' compileOnly 'org.checkerframework:checker-compat-qual:2.5.0'
implementation 'com.google.firebase:firebase-core:16.0.6' implementation 'com.google.firebase:firebase-core:16.0.7'
implementation 'com.google.firebase:firebase-messaging:17.3.4' implementation 'com.google.firebase:firebase-messaging:17.3.4'
implementation 'com.google.firebase:firebase-config:16.1.3' implementation 'com.google.firebase:firebase-config:16.1.3'
implementation 'com.google.android.gms:play-services-maps:16.0.0' implementation 'com.google.android.gms:play-services-maps:16.0.0'
@ -86,6 +86,16 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
debugMultidex {
initWith debug
minifyEnabled false
multiDexEnabled true
dependencies{
implementation 'com.android.support:multidex:1.0.3'
}
manifestPlaceholders = [applicationClassName:"MultiDexApplicationLoader"]
}
HA { HA {
debuggable false debuggable false
jniDebuggable false jniDebuggable false
@ -103,20 +113,19 @@ android {
shrinkResources false shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
foss {
debuggable false
jniDebuggable false
signingConfig signingConfigs.release
}
} }
defaultConfig.versionCode = 1497 defaultConfig.versionCode = 1517
sourceSets.debug { sourceSets.debug {
manifest.srcFile 'config/debug/AndroidManifest.xml' manifest.srcFile 'config/debug/AndroidManifest.xml'
} }
sourceSets.debugMultidex {
manifest.srcFile 'config/debug/AndroidManifest.xml'
java.srcDirs = ['src/multidex/java']
}
sourceSets.HA { sourceSets.HA {
manifest.srcFile 'config/debug/AndroidManifest.xml' manifest.srcFile 'config/debug/AndroidManifest.xml'
} }
@ -125,10 +134,6 @@ android {
manifest.srcFile 'config/release/AndroidManifest.xml' manifest.srcFile 'config/release/AndroidManifest.xml'
} }
sourceSets.foss {
manifest.srcFile 'config/foss/AndroidManifest.xml'
}
flavorDimensions "minApi" flavorDimensions "minApi"
productFlavors { productFlavors {
@ -249,10 +254,17 @@ android {
} }
} }
variantFilter { variant ->
def names = variant.flavors*.name
if(variant.buildType.name!="release" && !names.contains("afat")){
setIgnore(true)
}
}
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 27 targetSdkVersion 27
versionName "5.3.1" versionName "5.4.0"
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi'] vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
@ -262,6 +274,8 @@ android {
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64" abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
} }
} }
manifestPlaceholders = [applicationClassName:"ApplicationLoader"]
} }
} }

View File

@ -65,6 +65,22 @@ include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE := swscale
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_SRC_FILES := ./ffmpeg/armv7-a/libswscale.a
else ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
LOCAL_SRC_FILES := ./ffmpeg/arm64/libswscale.a
else ifeq ($(TARGET_ARCH_ABI),x86)
LOCAL_SRC_FILES := ./ffmpeg/i686/libswscale.a
else ifeq ($(TARGET_ARCH_ABI),x86_64)
LOCAL_SRC_FILES := ./ffmpeg/x86_64/libswscale.a
endif
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := crypto LOCAL_MODULE := crypto
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
@ -255,13 +271,13 @@ include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_PRELINK_MODULE := false LOCAL_PRELINK_MODULE := false
LOCAL_MODULE := tmessages.29 LOCAL_MODULE := tmessages.30
LOCAL_CFLAGS := -w -std=c11 -Os -DNULL=0 -DSOCKLEN_T=socklen_t -DLOCALE_NOT_USED -D_LARGEFILE_SOURCE=1 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 += -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 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
LOCAL_CPPFLAGS := -DBSD=1 -ffast-math -Os -funroll-loops -std=c++11 LOCAL_CPPFLAGS := -DBSD=1 -ffast-math -Os -funroll-loops -std=c++11
LOCAL_LDLIBS := -ljnigraphics -llog -lz -latomic -lEGL -lGLESv2 -landroid LOCAL_LDLIBS := -ljnigraphics -llog -lz -latomic -lEGL -lGLESv2 -landroid
LOCAL_STATIC_LIBRARIES := webp sqlite tgnet avformat avcodec avresample avutil voip flac LOCAL_STATIC_LIBRARIES := webp sqlite tgnet swscale avformat avcodec avresample avutil voip flac
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
./opus/src/opus.c \ ./opus/src/opus.c \
@ -523,10 +539,10 @@ endif
LOCAL_SRC_FILES += \ LOCAL_SRC_FILES += \
./jni.c \ ./jni.c \
./audio.c \ ./audio.c \
./utils.c \
./image.c \ ./image.c \
./video.c \ ./video.c \
./intro/IntroRenderer.c \ ./intro/IntroRenderer.c \
./utilities.cpp \
./gifvideo.cpp \ ./gifvideo.cpp \
./SqliteWrapper.cpp \ ./SqliteWrapper.cpp \
./TgNetWrapper.cpp \ ./TgNetWrapper.cpp \

View File

@ -6,7 +6,7 @@
#include <time.h> #include <time.h>
#include <opusfile.h> #include <opusfile.h>
#include <math.h> #include <math.h>
#include "utils.h" #include "c_utils.h"
typedef struct { typedef struct {
int version; int version;
@ -335,7 +335,7 @@ int initRecorder(const char *path) {
} }
#ifdef OPUS_SET_LSB_DEPTH #ifdef OPUS_SET_LSB_DEPTH
result = opus_encoder_ctl(_encoder, OPUS_SET_LSB_DEPTH(max(8, min(24, inopt.samplesize)))); result = opus_encoder_ctl(_encoder, OPUS_SET_LSB_DEPTH(MAX(8, MIN(24, inopt.samplesize))));
if (result != OPUS_OK) { if (result != OPUS_OK) {
LOGE("Warning OPUS_SET_LSB_DEPTH returned: %s", opus_strerror(result)); LOGE("Warning OPUS_SET_LSB_DEPTH returned: %s", opus_strerror(result));
} }
@ -447,7 +447,7 @@ int writeFrame(uint8_t *framePcmBytes, uint32_t frameByteCount) {
enc_granulepos += cur_frame_size * 48000 / coding_rate; enc_granulepos += cur_frame_size * 48000 / coding_rate;
size_segments = (nbBytes + 255) / 255; size_segments = (nbBytes + 255) / 255;
min_bytes = min(nbBytes, min_bytes); min_bytes = MIN(nbBytes, min_bytes);
} }
while ((((size_segments <= 255) && (last_segments + size_segments > 255)) || (enc_granulepos - last_granulepos > max_ogg_delay)) && ogg_stream_flush_fill(&os, &og, 255 * 255)) { while ((((size_segments <= 255) && (last_segments + size_segments > 255)) || (enc_granulepos - last_granulepos > max_ogg_delay)) && ogg_stream_flush_fill(&os, &og, 255 * 255)) {
@ -548,7 +548,7 @@ JNIEXPORT jbyteArray Java_org_telegram_messenger_MediaController_getWaveform2(JN
uint16_t *samples = malloc(100 * 2); uint16_t *samples = malloc(100 * 2);
uint64_t sampleIndex = 0; uint64_t sampleIndex = 0;
uint16_t peakSample = 0; uint16_t peakSample = 0;
int32_t sampleRate = (int32_t) max(1, length / resultSamples); int32_t sampleRate = (int32_t) MAX(1, length / resultSamples);
int32_t index = 0; int32_t index = 0;
for (int32_t i = 0; i < length; i++) { for (int32_t i = 0; i < length; i++) {
@ -588,7 +588,7 @@ JNIEXPORT jbyteArray Java_org_telegram_messenger_MediaController_getWaveform2(JN
uint8_t *bytes = malloc(bitstreamLength + 4); uint8_t *bytes = malloc(bitstreamLength + 4);
memset(bytes, 0, bitstreamLength + 4); memset(bytes, 0, bitstreamLength + 4);
for (int32_t i = 0; i < resultSamples; i++) { for (int32_t i = 0; i < resultSamples; i++) {
int32_t value = min(31, abs((int32_t) samples[i]) * 31 / peak); int32_t value = MIN(31, abs((int32_t) samples[i]) * 31 / peak);
set_bits(bytes, i * 5, value & 31); set_bits(bytes, i * 5, value & 31);
} }
(*env)->SetByteArrayRegion(env, result, 0, bitstreamLength, (jbyte *) bytes); (*env)->SetByteArrayRegion(env, result, 0, bitstreamLength, (jbyte *) bytes);
@ -609,7 +609,7 @@ JNIEXPORT jbyteArray Java_org_telegram_messenger_MediaController_getWaveform(JNI
if (opusFile != NULL && error == OPUS_OK) { if (opusFile != NULL && error == OPUS_OK) {
int64_t totalSamples = op_pcm_total(opusFile, -1); int64_t totalSamples = op_pcm_total(opusFile, -1);
const uint32_t resultSamples = 100; const uint32_t resultSamples = 100;
int32_t sampleRate = max(1, (int32_t) (totalSamples / resultSamples)); int32_t sampleRate = MAX(1, (int32_t) (totalSamples / resultSamples));
uint16_t *samples = malloc(100 * 2); uint16_t *samples = malloc(100 * 2);
@ -667,7 +667,7 @@ JNIEXPORT jbyteArray Java_org_telegram_messenger_MediaController_getWaveform(JNI
memset(bytes, 0, bitstreamLength + 4); memset(bytes, 0, bitstreamLength + 4);
for (int32_t i = 0; i < resultSamples; i++) { for (int32_t i = 0; i < resultSamples; i++) {
int32_t value = min(31, abs((int32_t) samples[i]) * 31 / peak); int32_t value = MIN(31, abs((int32_t) samples[i]) * 31 / peak);
set_bits(bytes, i * 5, value & 31); set_bits(bytes, i * 5, value & 31);
} }

View File

@ -17,13 +17,11 @@
#define LOGV(...) #define LOGV(...)
#endif #endif
#ifndef max #ifndef MAX
#define max(x, y) ((x) > (y)) ? (x) : (y) #define MAX(x, y) ((x) > (y)) ? (x) : (y)
#endif #endif
#ifndef min #ifndef MIN
#define min(x, y) ((x) < (y)) ? (x) : (y) #define MIN(x, y) ((x) < (y)) ? (x) : (y)
#endif #endif
void throwException(JNIEnv *env, char *format, ...);
#endif #endif

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -409,6 +409,7 @@ enum AVCodecID {
AV_CODEC_ID_DXV, AV_CODEC_ID_DXV,
AV_CODEC_ID_SCREENPRESSO, AV_CODEC_ID_SCREENPRESSO,
AV_CODEC_ID_RSCC, AV_CODEC_ID_RSCC,
AV_CODEC_ID_AVS2,
AV_CODEC_ID_Y41P = 0x8000, AV_CODEC_ID_Y41P = 0x8000,
AV_CODEC_ID_AVRP, AV_CODEC_ID_AVRP,
@ -446,6 +447,13 @@ enum AVCodecID {
AV_CODEC_ID_SVG, AV_CODEC_ID_SVG,
AV_CODEC_ID_GDV, AV_CODEC_ID_GDV,
AV_CODEC_ID_FITS, AV_CODEC_ID_FITS,
AV_CODEC_ID_IMM4,
AV_CODEC_ID_PROSUMER,
AV_CODEC_ID_MWSC,
AV_CODEC_ID_WCMV,
AV_CODEC_ID_RASC,
AV_CODEC_ID_HYMT,
AV_CODEC_ID_ARBC,
/* various PCM "codecs" */ /* various PCM "codecs" */
AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs
@ -485,6 +493,7 @@ enum AVCodecID {
AV_CODEC_ID_PCM_S64BE, AV_CODEC_ID_PCM_S64BE,
AV_CODEC_ID_PCM_F16LE, AV_CODEC_ID_PCM_F16LE,
AV_CODEC_ID_PCM_F24LE, AV_CODEC_ID_PCM_F24LE,
AV_CODEC_ID_PCM_VIDC,
/* various ADPCM codecs */ /* various ADPCM codecs */
AV_CODEC_ID_ADPCM_IMA_QT = 0x11000, AV_CODEC_ID_ADPCM_IMA_QT = 0x11000,
@ -615,6 +624,7 @@ enum AVCodecID {
AV_CODEC_ID_PAF_AUDIO, AV_CODEC_ID_PAF_AUDIO,
AV_CODEC_ID_ON2AVC, AV_CODEC_ID_ON2AVC,
AV_CODEC_ID_DSS_SP, AV_CODEC_ID_DSS_SP,
AV_CODEC_ID_CODEC2,
AV_CODEC_ID_FFWAVESYNTH = 0x15800, AV_CODEC_ID_FFWAVESYNTH = 0x15800,
AV_CODEC_ID_SONIC, AV_CODEC_ID_SONIC,
@ -634,6 +644,10 @@ enum AVCodecID {
AV_CODEC_ID_ATRAC3PAL, AV_CODEC_ID_ATRAC3PAL,
AV_CODEC_ID_DOLBY_E, AV_CODEC_ID_DOLBY_E,
AV_CODEC_ID_APTX, AV_CODEC_ID_APTX,
AV_CODEC_ID_APTX_HD,
AV_CODEC_ID_SBC,
AV_CODEC_ID_ATRAC9,
AV_CODEC_ID_HCOM,
/* subtitle codecs */ /* subtitle codecs */
AV_CODEC_ID_FIRST_SUBTITLE = 0x17000, ///< A dummy ID pointing at the start of subtitle codecs. AV_CODEC_ID_FIRST_SUBTITLE = 0x17000, ///< A dummy ID pointing at the start of subtitle codecs.
@ -662,6 +676,8 @@ enum AVCodecID {
AV_CODEC_ID_PJS, AV_CODEC_ID_PJS,
AV_CODEC_ID_ASS, AV_CODEC_ID_ASS,
AV_CODEC_ID_HDMV_TEXT_SUBTITLE, AV_CODEC_ID_HDMV_TEXT_SUBTITLE,
AV_CODEC_ID_TTML,
AV_CODEC_ID_ARIB_CAPTION,
/* other specific kind of codecs (generally used for attachments) */ /* other specific kind of codecs (generally used for attachments) */
AV_CODEC_ID_FIRST_UNKNOWN = 0x18000, ///< A dummy ID pointing at the start of various fake codecs. AV_CODEC_ID_FIRST_UNKNOWN = 0x18000, ///< A dummy ID pointing at the start of various fake codecs.
@ -1059,6 +1075,13 @@ typedef struct RcOverride{
*/ */
#define AV_CODEC_CAP_HYBRID (1 << 19) #define AV_CODEC_CAP_HYBRID (1 << 19)
/**
* This codec takes the reordered_opaque field from input AVFrames
* and returns it in the corresponding field in AVCodecContext after
* encoding.
*/
#define AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE (1 << 20)
/** /**
* Pan Scan area. * Pan Scan area.
* This specifies the area which should be displayed. * This specifies the area which should be displayed.
@ -1098,17 +1121,29 @@ typedef struct AVCPBProperties {
* Maximum bitrate of the stream, in bits per second. * Maximum bitrate of the stream, in bits per second.
* Zero if unknown or unspecified. * Zero if unknown or unspecified.
*/ */
#if FF_API_UNSANITIZED_BITRATES
int max_bitrate; int max_bitrate;
#else
int64_t max_bitrate;
#endif
/** /**
* Minimum bitrate of the stream, in bits per second. * Minimum bitrate of the stream, in bits per second.
* Zero if unknown or unspecified. * Zero if unknown or unspecified.
*/ */
#if FF_API_UNSANITIZED_BITRATES
int min_bitrate; int min_bitrate;
#else
int64_t min_bitrate;
#endif
/** /**
* Average bitrate of the stream, in bits per second. * Average bitrate of the stream, in bits per second.
* Zero if unknown or unspecified. * Zero if unknown or unspecified.
*/ */
#if FF_API_UNSANITIZED_BITRATES
int avg_bitrate; int avg_bitrate;
#else
int64_t avg_bitrate;
#endif
/** /**
* The size of the buffer to which the ratecontrol is applied, in bits. * The size of the buffer to which the ratecontrol is applied, in bits.
@ -1309,7 +1344,7 @@ enum AVPacketSideDataType {
AV_PKT_DATA_METADATA_UPDATE, AV_PKT_DATA_METADATA_UPDATE,
/** /**
* MPEGTS stream ID, this is required to pass the stream ID * MPEGTS stream ID as uint8_t, this is required to pass the stream ID
* information from the demuxer to the corresponding muxer. * information from the demuxer to the corresponding muxer.
*/ */
AV_PKT_DATA_MPEGTS_STREAM_ID, AV_PKT_DATA_MPEGTS_STREAM_ID,
@ -1341,6 +1376,25 @@ enum AVPacketSideDataType {
*/ */
AV_PKT_DATA_A53_CC, AV_PKT_DATA_A53_CC,
/**
* This side data is encryption initialization data.
* The format is not part of ABI, use av_encryption_init_info_* methods to
* access.
*/
AV_PKT_DATA_ENCRYPTION_INIT_INFO,
/**
* This side data contains encryption info for how to decrypt the packet.
* The format is not part of ABI, use av_encryption_info_* methods to access.
*/
AV_PKT_DATA_ENCRYPTION_INFO,
/**
* Active Format Description data consisting of a single byte as specified
* in ETSI TS 101 154 using AVActiveFormatDescription enum.
*/
AV_PKT_DATA_AFD,
/** /**
* The number of side data types. * The number of side data types.
* This is not part of the public API/ABI in the sense that it may * This is not part of the public API/ABI in the sense that it may
@ -1596,6 +1650,7 @@ typedef struct AVCodecContext {
* The allocated memory should be AV_INPUT_BUFFER_PADDING_SIZE bytes larger * The allocated memory should be AV_INPUT_BUFFER_PADDING_SIZE bytes larger
* than extradata_size to avoid problems if it is read with the bitstream reader. * than extradata_size to avoid problems if it is read with the bitstream reader.
* The bytewise contents of extradata must not depend on the architecture or CPU endianness. * The bytewise contents of extradata must not depend on the architecture or CPU endianness.
* Must be allocated with the av_malloc() family of functions.
* - encoding: Set/allocated/freed by libavcodec. * - encoding: Set/allocated/freed by libavcodec.
* - decoding: Set/allocated/freed by user. * - decoding: Set/allocated/freed by user.
*/ */
@ -2645,7 +2700,10 @@ typedef struct AVCodecContext {
/** /**
* opaque 64-bit number (generally a PTS) that will be reordered and * opaque 64-bit number (generally a PTS) that will be reordered and
* output in AVFrame.reordered_opaque * output in AVFrame.reordered_opaque
* - encoding: unused * - encoding: Set by libavcodec to the reordered_opaque of the input
* frame corresponding to the last returned packet. Only
* supported by encoders with the
* AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE capability.
* - decoding: Set by user. * - decoding: Set by user.
*/ */
int64_t reordered_opaque; int64_t reordered_opaque;
@ -2917,6 +2975,28 @@ typedef struct AVCodecContext {
#define FF_PROFILE_HEVC_MAIN_STILL_PICTURE 3 #define FF_PROFILE_HEVC_MAIN_STILL_PICTURE 3
#define FF_PROFILE_HEVC_REXT 4 #define FF_PROFILE_HEVC_REXT 4
#define FF_PROFILE_AV1_MAIN 0
#define FF_PROFILE_AV1_HIGH 1
#define FF_PROFILE_AV1_PROFESSIONAL 2
#define FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT 0xc0
#define FF_PROFILE_MJPEG_HUFFMAN_EXTENDED_SEQUENTIAL_DCT 0xc1
#define FF_PROFILE_MJPEG_HUFFMAN_PROGRESSIVE_DCT 0xc2
#define FF_PROFILE_MJPEG_HUFFMAN_LOSSLESS 0xc3
#define FF_PROFILE_MJPEG_JPEG_LS 0xf7
#define FF_PROFILE_SBC_MSBC 1
#define FF_PROFILE_PRORES_PROXY 0
#define FF_PROFILE_PRORES_LT 1
#define FF_PROFILE_PRORES_STANDARD 2
#define FF_PROFILE_PRORES_HQ 3
#define FF_PROFILE_PRORES_4444 4
#define FF_PROFILE_PRORES_XQ 5
#define FF_PROFILE_ARIB_PROFILE_A 0
#define FF_PROFILE_ARIB_PROFILE_C 1
/** /**
* level * level
* - encoding: Set by user. * - encoding: Set by user.
@ -3068,6 +3148,7 @@ typedef struct AVCodecContext {
#define FF_SUB_CHARENC_MODE_DO_NOTHING -1 ///< do nothing (demuxer outputs a stream supposed to be already in UTF-8, or the codec is bitmap for instance) #define FF_SUB_CHARENC_MODE_DO_NOTHING -1 ///< do nothing (demuxer outputs a stream supposed to be already in UTF-8, or the codec is bitmap for instance)
#define FF_SUB_CHARENC_MODE_AUTOMATIC 0 ///< libavcodec will select the mode itself #define FF_SUB_CHARENC_MODE_AUTOMATIC 0 ///< libavcodec will select the mode itself
#define FF_SUB_CHARENC_MODE_PRE_DECODER 1 ///< the AVPacket data needs to be recoded to UTF-8 before being fed to the decoder, requires iconv #define FF_SUB_CHARENC_MODE_PRE_DECODER 1 ///< the AVPacket data needs to be recoded to UTF-8 before being fed to the decoder, requires iconv
#define FF_SUB_CHARENC_MODE_IGNORE 2 ///< neither convert the subtitles, nor check them for valid UTF-8
/** /**
* Skip processing alpha if supported by codec. * Skip processing alpha if supported by codec.
@ -3254,6 +3335,28 @@ typedef struct AVCodecContext {
* (with the display dimensions being determined by the crop_* fields). * (with the display dimensions being determined by the crop_* fields).
*/ */
int apply_cropping; int apply_cropping;
/*
* Video decoding only. Sets the number of extra hardware frames which
* the decoder will allocate for use by the caller. This must be set
* before avcodec_open2() is called.
*
* Some hardware decoders require all frames that they will use for
* output to be defined in advance before decoding starts. For such
* decoders, the hardware frame pool must therefore be of a fixed size.
* The extra frames set here are on top of any number that the decoder
* needs internally in order to operate normally (for example, frames
* used as reference pictures).
*/
int extra_hw_frames;
/**
* The percentage of damaged samples to discard a frame.
*
* - decoding: set by user
* - encoding: unused
*/
int discard_damaged_percentage;
} AVCodecContext; } AVCodecContext;
#if FF_API_CODEC_GET_SET #if FF_API_CODEC_GET_SET
@ -3439,6 +3542,9 @@ typedef struct AVCodec {
/** /**
* Initialize codec static data, called from avcodec_register(). * Initialize codec static data, called from avcodec_register().
*
* This is not intended for time consuming operations as it is
* run for every codec regardless of that codec being used.
*/ */
void (*init_static_data)(struct AVCodec *codec); void (*init_static_data)(struct AVCodec *codec);
@ -4319,7 +4425,7 @@ int av_packet_from_data(AVPacket *pkt, uint8_t *data, int size);
* @warning This is a hack - the packet memory allocation stuff is broken. The * @warning This is a hack - the packet memory allocation stuff is broken. The
* packet is allocated if it was not really allocated. * packet is allocated if it was not really allocated.
* *
* @deprecated Use av_packet_ref * @deprecated Use av_packet_ref or av_packet_make_refcounted
*/ */
attribute_deprecated attribute_deprecated
int av_dup_packet(AVPacket *pkt); int av_dup_packet(AVPacket *pkt);
@ -4490,6 +4596,33 @@ void av_packet_move_ref(AVPacket *dst, AVPacket *src);
*/ */
int av_packet_copy_props(AVPacket *dst, const AVPacket *src); int av_packet_copy_props(AVPacket *dst, const AVPacket *src);
/**
* Ensure the data described by a given packet is reference counted.
*
* @note This function does not ensure that the reference will be writable.
* Use av_packet_make_writable instead for that purpose.
*
* @see av_packet_ref
* @see av_packet_make_writable
*
* @param pkt packet whose data should be made reference counted.
*
* @return 0 on success, a negative AVERROR on error. On failure, the
* packet is unchanged.
*/
int av_packet_make_refcounted(AVPacket *pkt);
/**
* Create a writable reference for the data described by a given packet,
* avoiding data copy if possible.
*
* @param pkt Packet whose data should be made writable.
*
* @return 0 on success, a negative AVERROR on failure. On failure, the
* packet is unchanged.
*/
int av_packet_make_writable(AVPacket *pkt);
/** /**
* Convert valid timing fields (timestamps / durations) in a packet from one * Convert valid timing fields (timestamps / durations) in a packet from one
* timebase to another. Timestamps with unknown values (AV_NOPTS_VALUE) will be * timebase to another. Timestamps with unknown values (AV_NOPTS_VALUE) will be
@ -5693,6 +5826,7 @@ typedef struct AVBitStreamFilter {
int (*init)(AVBSFContext *ctx); int (*init)(AVBSFContext *ctx);
int (*filter)(AVBSFContext *ctx, AVPacket *pkt); int (*filter)(AVBSFContext *ctx, AVPacket *pkt);
void (*close)(AVBSFContext *ctx); void (*close)(AVBSFContext *ctx);
void (*flush)(AVBSFContext *ctx);
} AVBitStreamFilter; } AVBitStreamFilter;
#if FF_API_OLD_BSF #if FF_API_OLD_BSF
@ -5819,6 +5953,11 @@ int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt);
*/ */
int av_bsf_receive_packet(AVBSFContext *ctx, AVPacket *pkt); int av_bsf_receive_packet(AVBSFContext *ctx, AVPacket *pkt);
/**
* Reset the internal bitstream filter state / flush internal buffers.
*/
void av_bsf_flush(AVBSFContext *ctx);
/** /**
* Free a bitstream filter context and everything associated with it; write NULL * Free a bitstream filter context and everything associated with it; write NULL
* into the supplied pointer. * into the supplied pointer.

View File

@ -85,4 +85,17 @@ typedef struct MediaCodecBuffer AVMediaCodecBuffer;
*/ */
int av_mediacodec_release_buffer(AVMediaCodecBuffer *buffer, int render); int av_mediacodec_release_buffer(AVMediaCodecBuffer *buffer, int render);
/**
* Release a MediaCodec buffer and render it at the given time to the surface
* that is associated with the decoder. The timestamp must be within one second
* of the current java/lang/System#nanoTime() (which is implemented using
* CLOCK_MONOTONIC on Android). See the Android MediaCodec documentation
* of android/media/MediaCodec#releaseOutputBuffer(int,long) for more details.
*
* @param buffer the buffer to render
* @param time timestamp in nanoseconds of when to render the buffer
* @return 0 on success, < 0 otherwise
*/
int av_mediacodec_render_buffer_at_time(AVMediaCodecBuffer *buffer, int64_t time);
#endif /* AVCODEC_MEDIACODEC_H */ #endif /* AVCODEC_MEDIACODEC_H */

View File

@ -28,8 +28,8 @@
#include "libavutil/version.h" #include "libavutil/version.h"
#define LIBAVCODEC_VERSION_MAJOR 58 #define LIBAVCODEC_VERSION_MAJOR 58
#define LIBAVCODEC_VERSION_MINOR 9 #define LIBAVCODEC_VERSION_MINOR 47
#define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_MICRO 102
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
LIBAVCODEC_VERSION_MINOR, \ LIBAVCODEC_VERSION_MINOR, \
@ -132,6 +132,9 @@
#ifndef FF_API_NEXT #ifndef FF_API_NEXT
#define FF_API_NEXT (LIBAVCODEC_VERSION_MAJOR < 59) #define FF_API_NEXT (LIBAVCODEC_VERSION_MAJOR < 59)
#endif #endif
#ifndef FF_API_UNSANITIZED_BITRATES
#define FF_API_UNSANITIZED_BITRATES (LIBAVCODEC_VERSION_MAJOR < 59)
#endif
#endif /* AVCODEC_VERSION_H */ #endif /* AVCODEC_VERSION_H */

View File

@ -845,6 +845,8 @@ typedef struct AVStreamInternal AVStreamInternal;
#define AV_DISPOSITION_CAPTIONS 0x10000 #define AV_DISPOSITION_CAPTIONS 0x10000
#define AV_DISPOSITION_DESCRIPTIONS 0x20000 #define AV_DISPOSITION_DESCRIPTIONS 0x20000
#define AV_DISPOSITION_METADATA 0x40000 #define AV_DISPOSITION_METADATA 0x40000
#define AV_DISPOSITION_DEPENDENT 0x80000 ///< dependent audio stream (mix_type=0 in mpegts)
#define AV_DISPOSITION_STILL_IMAGE 0x100000 ///< still images in video stream (still_picture_flag=1 in mpegts)
/** /**
* Options for behavior on timestamp wrap detection. * Options for behavior on timestamp wrap detection.
@ -1101,6 +1103,13 @@ typedef struct AVStream {
*/ */
int stream_identifier; int stream_identifier;
/**
* Details of the MPEG-TS program which created this stream.
*/
int program_num;
int pmt_version;
int pmt_stream_idx;
int64_t interleaver_chunk_size; int64_t interleaver_chunk_size;
int64_t interleaver_chunk_duration; int64_t interleaver_chunk_duration;
@ -1258,6 +1267,7 @@ typedef struct AVProgram {
int program_num; int program_num;
int pmt_pid; int pmt_pid;
int pcr_pid; int pcr_pid;
int pmt_version;
/***************************************************************** /*****************************************************************
* All fields below this line are not part of the public API. They * All fields below this line are not part of the public API. They
@ -1473,7 +1483,9 @@ typedef struct AVFormatContext {
* This flag is mainly intended for testing. * This flag is mainly intended for testing.
*/ */
#define AVFMT_FLAG_BITEXACT 0x0400 #define AVFMT_FLAG_BITEXACT 0x0400
#define AVFMT_FLAG_MP4A_LATM 0x8000 ///< Enable RTP MP4A-LATM payload #if FF_API_LAVF_MP4A_LATM
#define AVFMT_FLAG_MP4A_LATM 0x8000 ///< Deprecated, does nothing.
#endif
#define AVFMT_FLAG_SORT_DTS 0x10000 ///< try to interleave outputted packets by dts (using this flag can slow demuxing down) #define AVFMT_FLAG_SORT_DTS 0x10000 ///< try to interleave outputted packets by dts (using this flag can slow demuxing down)
#define AVFMT_FLAG_PRIV_OPT 0x20000 ///< Enable use of private options by delaying codec open (this could be made default once all code is converted) #define AVFMT_FLAG_PRIV_OPT 0x20000 ///< Enable use of private options by delaying codec open (this could be made default once all code is converted)
#if FF_API_LAVF_KEEPSIDE_FLAG #if FF_API_LAVF_KEEPSIDE_FLAG
@ -1925,6 +1937,13 @@ typedef struct AVFormatContext {
* - decoding: set by user * - decoding: set by user
*/ */
int max_streams; int max_streams;
/**
* Skip duration calcuation in estimate_timings_from_pts.
* - encoding: unused
* - decoding: set by user
*/
int skip_estimate_duration_from_pts;
} AVFormatContext; } AVFormatContext;
#if FF_API_FORMAT_GET_SET #if FF_API_FORMAT_GET_SET

View File

@ -236,7 +236,7 @@ typedef struct AVIOContext {
int (*write_packet)(void *opaque, uint8_t *buf, int buf_size); int (*write_packet)(void *opaque, uint8_t *buf, int buf_size);
int64_t (*seek)(void *opaque, int64_t offset, int whence); int64_t (*seek)(void *opaque, int64_t offset, int whence);
int64_t pos; /**< position in the file of the current buffer */ int64_t pos; /**< position in the file of the current buffer */
int eof_reached; /**< true if eof reached */ int eof_reached; /**< true if was unable to read due to error or eof */
int write_flag; /**< true if open for writing */ int write_flag; /**< true if open for writing */
int max_packet_size; int max_packet_size;
unsigned long checksum; unsigned long checksum;
@ -566,8 +566,8 @@ static av_always_inline int64_t avio_tell(AVIOContext *s)
int64_t avio_size(AVIOContext *s); int64_t avio_size(AVIOContext *s);
/** /**
* feof() equivalent for AVIOContext. * Similar to feof() but also returns nonzero on read errors.
* @return non zero if and only if end of file * @return non zero if and only if at end of file or a read error happened when reading.
*/ */
int avio_feof(AVIOContext *s); int avio_feof(AVIOContext *s);

View File

@ -32,8 +32,8 @@
// Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium) // Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium)
// Also please add any ticket numbers that you believe might be affected here // Also please add any ticket numbers that you believe might be affected here
#define LIBAVFORMAT_VERSION_MAJOR 58 #define LIBAVFORMAT_VERSION_MAJOR 58
#define LIBAVFORMAT_VERSION_MINOR 7 #define LIBAVFORMAT_VERSION_MINOR 26
#define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_MICRO 101
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
LIBAVFORMAT_VERSION_MINOR, \ LIBAVFORMAT_VERSION_MINOR, \
@ -70,6 +70,9 @@
#ifndef FF_API_HLS_WRAP #ifndef FF_API_HLS_WRAP
#define FF_API_HLS_WRAP (LIBAVFORMAT_VERSION_MAJOR < 59) #define FF_API_HLS_WRAP (LIBAVFORMAT_VERSION_MAJOR < 59)
#endif #endif
#ifndef FF_API_HLS_USE_LOCALTIME
#define FF_API_HLS_USE_LOCALTIME (LIBAVFORMAT_VERSION_MAJOR < 59)
#endif
#ifndef FF_API_LAVF_KEEPSIDE_FLAG #ifndef FF_API_LAVF_KEEPSIDE_FLAG
#define FF_API_LAVF_KEEPSIDE_FLAG (LIBAVFORMAT_VERSION_MAJOR < 59) #define FF_API_LAVF_KEEPSIDE_FLAG (LIBAVFORMAT_VERSION_MAJOR < 59)
#endif #endif
@ -94,6 +97,12 @@
#ifndef FF_API_NEXT #ifndef FF_API_NEXT
#define FF_API_NEXT (LIBAVFORMAT_VERSION_MAJOR < 59) #define FF_API_NEXT (LIBAVFORMAT_VERSION_MAJOR < 59)
#endif #endif
#ifndef FF_API_DASH_MIN_SEG_DURATION
#define FF_API_DASH_MIN_SEG_DURATION (LIBAVFORMAT_VERSION_MAJOR < 59)
#endif
#ifndef FF_API_LAVF_MP4A_LATM
#define FF_API_LAVF_MP4A_LATM (LIBAVFORMAT_VERSION_MAJOR < 59)
#endif
#ifndef FF_API_R_FRAME_RATE #ifndef FF_API_R_FRAME_RATE

View File

@ -66,7 +66,7 @@
#endif #endif
/** /**
* Assert that floating point opperations can be executed. * Assert that floating point operations can be executed.
* *
* This will av_assert0() that the cpu is not in MMX state on X86 * This will av_assert0() that the cpu is not in MMX state on X86
*/ */

View File

@ -400,6 +400,12 @@ int av_utf8_decode(int32_t *codep, const uint8_t **bufp, const uint8_t *buf_end,
*/ */
int av_match_list(const char *name, const char *list, char separator); int av_match_list(const char *name, const char *list, char separator);
/**
* See libc sscanf manual for more information.
* Locale-independent sscanf implementation.
*/
int av_sscanf(const char *string, const char *format, ...);
/** /**
* @} * @}
*/ */

View File

@ -158,7 +158,7 @@ static av_always_inline av_const int64_t av_clip64_c(int64_t a, int64_t amin, in
*/ */
static av_always_inline av_const uint8_t av_clip_uint8_c(int a) static av_always_inline av_const uint8_t av_clip_uint8_c(int a)
{ {
if (a&(~0xFF)) return (-a)>>31; if (a&(~0xFF)) return (~a)>>31;
else return a; else return a;
} }
@ -180,7 +180,7 @@ static av_always_inline av_const int8_t av_clip_int8_c(int a)
*/ */
static av_always_inline av_const uint16_t av_clip_uint16_c(int a) static av_always_inline av_const uint16_t av_clip_uint16_c(int a)
{ {
if (a&(~0xFFFF)) return (-a)>>31; if (a&(~0xFFFF)) return (~a)>>31;
else return a; else return a;
} }
@ -228,7 +228,7 @@ static av_always_inline av_const int av_clip_intp2_c(int a, int p)
*/ */
static av_always_inline av_const unsigned av_clip_uintp2_c(int a, int p) static av_always_inline av_const unsigned av_clip_uintp2_c(int a, int p)
{ {
if (a & ~((1<<p) - 1)) return -a >> 31 & ((1<<p) - 1); if (a & ~((1<<p) - 1)) return (~a) >> 31 & ((1<<p) - 1);
else return a; else return a;
} }

View File

@ -54,6 +54,7 @@ typedef enum {
AV_CRC_32_IEEE_LE, /*< reversed bitorder version of AV_CRC_32_IEEE */ AV_CRC_32_IEEE_LE, /*< reversed bitorder version of AV_CRC_32_IEEE */
AV_CRC_16_ANSI_LE, /*< reversed bitorder version of AV_CRC_16_ANSI */ AV_CRC_16_ANSI_LE, /*< reversed bitorder version of AV_CRC_16_ANSI */
AV_CRC_24_IEEE, AV_CRC_24_IEEE,
AV_CRC_8_EBU,
AV_CRC_MAX, /*< Not part of public API! Do not use outside libavutil. */ AV_CRC_MAX, /*< Not part of public API! Do not use outside libavutil. */
}AVCRCId; }AVCRCId;

View File

@ -0,0 +1,205 @@
/**
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef AVUTIL_ENCRYPTION_INFO_H
#define AVUTIL_ENCRYPTION_INFO_H
#include <stddef.h>
#include <stdint.h>
typedef struct AVSubsampleEncryptionInfo {
/** The number of bytes that are clear. */
unsigned int bytes_of_clear_data;
/**
* The number of bytes that are protected. If using pattern encryption,
* the pattern applies to only the protected bytes; if not using pattern
* encryption, all these bytes are encrypted.
*/
unsigned int bytes_of_protected_data;
} AVSubsampleEncryptionInfo;
/**
* This describes encryption info for a packet. This contains frame-specific
* info for how to decrypt the packet before passing it to the decoder.
*
* The size of this struct is not part of the public ABI.
*/
typedef struct AVEncryptionInfo {
/** The fourcc encryption scheme, in big-endian byte order. */
uint32_t scheme;
/**
* Only used for pattern encryption. This is the number of 16-byte blocks
* that are encrypted.
*/
uint32_t crypt_byte_block;
/**
* Only used for pattern encryption. This is the number of 16-byte blocks
* that are clear.
*/
uint32_t skip_byte_block;
/**
* The ID of the key used to encrypt the packet. This should always be
* 16 bytes long, but may be changed in the future.
*/
uint8_t *key_id;
uint32_t key_id_size;
/**
* The initialization vector. This may have been zero-filled to be the
* correct block size. This should always be 16 bytes long, but may be
* changed in the future.
*/
uint8_t *iv;
uint32_t iv_size;
/**
* An array of subsample encryption info specifying how parts of the sample
* are encrypted. If there are no subsamples, then the whole sample is
* encrypted.
*/
AVSubsampleEncryptionInfo *subsamples;
uint32_t subsample_count;
} AVEncryptionInfo;
/**
* This describes info used to initialize an encryption key system.
*
* The size of this struct is not part of the public ABI.
*/
typedef struct AVEncryptionInitInfo {
/**
* A unique identifier for the key system this is for, can be NULL if it
* is not known. This should always be 16 bytes, but may change in the
* future.
*/
uint8_t* system_id;
uint32_t system_id_size;
/**
* An array of key IDs this initialization data is for. All IDs are the
* same length. Can be NULL if there are no known key IDs.
*/
uint8_t** key_ids;
/** The number of key IDs. */
uint32_t num_key_ids;
/**
* The number of bytes in each key ID. This should always be 16, but may
* change in the future.
*/
uint32_t key_id_size;
/**
* Key-system specific initialization data. This data is copied directly
* from the file and the format depends on the specific key system. This
* can be NULL if there is no initialization data; in that case, there
* will be at least one key ID.
*/
uint8_t* data;
uint32_t data_size;
/**
* An optional pointer to the next initialization info in the list.
*/
struct AVEncryptionInitInfo *next;
} AVEncryptionInitInfo;
/**
* Allocates an AVEncryptionInfo structure and sub-pointers to hold the given
* number of subsamples. This will allocate pointers for the key ID, IV,
* and subsample entries, set the size members, and zero-initialize the rest.
*
* @param subsample_count The number of subsamples.
* @param key_id_size The number of bytes in the key ID, should be 16.
* @param iv_size The number of bytes in the IV, should be 16.
*
* @return The new AVEncryptionInfo structure, or NULL on error.
*/
AVEncryptionInfo *av_encryption_info_alloc(uint32_t subsample_count, uint32_t key_id_size, uint32_t iv_size);
/**
* Allocates an AVEncryptionInfo structure with a copy of the given data.
* @return The new AVEncryptionInfo structure, or NULL on error.
*/
AVEncryptionInfo *av_encryption_info_clone(const AVEncryptionInfo *info);
/**
* Frees the given encryption info object. This MUST NOT be used to free the
* side-data data pointer, that should use normal side-data methods.
*/
void av_encryption_info_free(AVEncryptionInfo *info);
/**
* Creates a copy of the AVEncryptionInfo that is contained in the given side
* data. The resulting object should be passed to av_encryption_info_free()
* when done.
*
* @return The new AVEncryptionInfo structure, or NULL on error.
*/
AVEncryptionInfo *av_encryption_info_get_side_data(const uint8_t *side_data, size_t side_data_size);
/**
* Allocates and initializes side data that holds a copy of the given encryption
* info. The resulting pointer should be either freed using av_free or given
* to av_packet_add_side_data().
*
* @return The new side-data pointer, or NULL.
*/
uint8_t *av_encryption_info_add_side_data(
const AVEncryptionInfo *info, size_t *side_data_size);
/**
* Allocates an AVEncryptionInitInfo structure and sub-pointers to hold the
* given sizes. This will allocate pointers and set all the fields.
*
* @return The new AVEncryptionInitInfo structure, or NULL on error.
*/
AVEncryptionInitInfo *av_encryption_init_info_alloc(
uint32_t system_id_size, uint32_t num_key_ids, uint32_t key_id_size, uint32_t data_size);
/**
* Frees the given encryption init info object. This MUST NOT be used to free
* the side-data data pointer, that should use normal side-data methods.
*/
void av_encryption_init_info_free(AVEncryptionInitInfo* info);
/**
* Creates a copy of the AVEncryptionInitInfo that is contained in the given
* side data. The resulting object should be passed to
* av_encryption_init_info_free() when done.
*
* @return The new AVEncryptionInitInfo structure, or NULL on error.
*/
AVEncryptionInitInfo *av_encryption_init_info_get_side_data(
const uint8_t* side_data, size_t side_data_size);
/**
* Allocates and initializes side data that holds a copy of the given encryption
* init info. The resulting pointer should be either freed using av_free or
* given to av_packet_add_side_data().
*
* @return The new side-data pointer, or NULL.
*/
uint8_t *av_encryption_init_info_add_side_data(
const AVEncryptionInitInfo *info, size_t *side_data_size);
#endif /* AVUTIL_ENCRYPTION_INFO_H */

View File

@ -1,5 +1,5 @@
/* Automatically generated by version.sh, do not manually edit! */ /* Automatically generated by version.sh, do not manually edit! */
#ifndef AVUTIL_FFVERSION_H #ifndef AVUTIL_FFVERSION_H
#define AVUTIL_FFVERSION_H #define AVUTIL_FFVERSION_H
#define FFMPEG_VERSION "3.4.git" #define FFMPEG_VERSION "N-93231-g96d79ff5b5"
#endif /* AVUTIL_FFVERSION_H */ #endif /* AVUTIL_FFVERSION_H */

View File

@ -33,6 +33,8 @@
* allocated buffer or map it with mmap() when available. * allocated buffer or map it with mmap() when available.
* In case of success set *bufptr to the read or mmapped buffer, and * In case of success set *bufptr to the read or mmapped buffer, and
* *size to the size in bytes of the buffer in *bufptr. * *size to the size in bytes of the buffer in *bufptr.
* Unlike mmap this function succeeds with zero sized files, in this
* case *bufptr will be set to NULL and *size will be set to 0.
* The returned buffer must be released with av_file_unmap(). * The returned buffer must be released with av_file_unmap().
* *
* @param log_offset loglevel offset used for logging * @param log_offset loglevel offset used for logging

View File

@ -141,6 +141,44 @@ enum AVFrameSideDataType {
* metadata key entry "name". * metadata key entry "name".
*/ */
AV_FRAME_DATA_ICC_PROFILE, AV_FRAME_DATA_ICC_PROFILE,
#if FF_API_FRAME_QP
/**
* Implementation-specific description of the format of AV_FRAME_QP_TABLE_DATA.
* The contents of this side data are undocumented and internal; use
* av_frame_set_qp_table() and av_frame_get_qp_table() to access this in a
* meaningful way instead.
*/
AV_FRAME_DATA_QP_TABLE_PROPERTIES,
/**
* Raw QP table data. Its format is described by
* AV_FRAME_DATA_QP_TABLE_PROPERTIES. Use av_frame_set_qp_table() and
* av_frame_get_qp_table() to access this instead.
*/
AV_FRAME_DATA_QP_TABLE_DATA,
#endif
/**
* Timecode which conforms to SMPTE ST 12-1. The data is an array of 4 uint32_t
* where the first uint32_t describes how many (1-3) of the other timecodes are used.
* The timecode format is described in the av_timecode_get_smpte_from_framenum()
* function in libavutil/timecode.c.
*/
AV_FRAME_DATA_S12M_TIMECODE,
/**
* HDR dynamic metadata associated with a video frame. The payload is
* an AVDynamicHDRPlus type and contains information for color
* volume transform - application 4 of SMPTE 2094-40:2016 standard.
*/
AV_FRAME_DATA_DYNAMIC_HDR_PLUS,
/**
* Regions Of Interest, the data is an array of AVRegionOfInterest type, the number of
* array element is implied by AVFrameSideData.size / AVRegionOfInterest.self_size.
*/
AV_FRAME_DATA_REGIONS_OF_INTEREST,
}; };
enum AVActiveFormatDescription { enum AVActiveFormatDescription {
@ -168,6 +206,35 @@ typedef struct AVFrameSideData {
AVBufferRef *buf; AVBufferRef *buf;
} AVFrameSideData; } AVFrameSideData;
/**
* Structure to hold Region Of Interest.
*
* self_size specifies the size of this data structure. This value
* should be set to sizeof(AVRegionOfInterest). EINVAL is returned if self_size is zero.
*
* Number of pixels to discard from the top/bottom/left/right border of
* the frame to obtain the region of interest of the frame.
* They are encoder dependent and will be extended internally
* if the codec requires an alignment.
* If the regions overlap, the last value in the list will be used.
*
* qoffset is quant offset, and base rule here:
* returns EINVAL if AVRational.den is zero.
* the value (num/den) range is [-1.0, 1.0], clamp to +-1.0 if out of range.
* 0 means no picture quality change,
* negative offset asks for better quality (and the best with value -1.0),
* positive offset asks for worse quality (and the worst with value 1.0).
* How to explain/implement the different quilaity requirement is encoder dependent.
*/
typedef struct AVRegionOfInterest {
uint32_t self_size;
int top;
int bottom;
int left;
int right;
AVRational qoffset;
} AVRegionOfInterest;
/** /**
* This structure describes decoded (raw) audio or video data. * This structure describes decoded (raw) audio or video data.
* *
@ -364,7 +431,6 @@ typedef struct AVFrame {
* that time, * that time,
* the decoder reorders values as needed and sets AVFrame.reordered_opaque * the decoder reorders values as needed and sets AVFrame.reordered_opaque
* to exactly one of the values provided by the user through AVCodecContext.reordered_opaque * to exactly one of the values provided by the user through AVCodecContext.reordered_opaque
* @deprecated in favor of pkt_pts
*/ */
int64_t reordered_opaque; int64_t reordered_opaque;
@ -529,6 +595,7 @@ typedef struct AVFrame {
attribute_deprecated attribute_deprecated
int qscale_type; int qscale_type;
attribute_deprecated
AVBufferRef *qp_table_buf; AVBufferRef *qp_table_buf;
#endif #endif
/** /**
@ -800,6 +867,22 @@ AVFrameSideData *av_frame_new_side_data(AVFrame *frame,
enum AVFrameSideDataType type, enum AVFrameSideDataType type,
int size); int size);
/**
* Add a new side data to a frame from an existing AVBufferRef
*
* @param frame a frame to which the side data should be added
* @param type the type of the added side data
* @param buf an AVBufferRef to add as side data. The ownership of
* the reference is transferred to the frame.
*
* @return newly added side data on success, NULL on error. On failure
* the frame is unchanged and the AVBufferRef remains owned by
* the caller.
*/
AVFrameSideData *av_frame_new_side_data_from_buf(AVFrame *frame,
enum AVFrameSideDataType type,
AVBufferRef *buf);
/** /**
* @return a pointer to the side data of a given type on success, NULL if there * @return a pointer to the side data of a given type on success, NULL if there
* is no side data with such type in this frame. * is no side data with such type in this frame.

View File

@ -29,6 +29,8 @@
#include <stdint.h> #include <stdint.h>
#include "version.h"
/** /**
* @defgroup lavu_hash Hash Functions * @defgroup lavu_hash Hash Functions
* @ingroup lavu_crypto * @ingroup lavu_crypto
@ -179,7 +181,11 @@ void av_hash_init(struct AVHashContext *ctx);
* @param[in] src Data to be added to the hash context * @param[in] src Data to be added to the hash context
* @param[in] len Size of the additional data * @param[in] len Size of the additional data
*/ */
#if FF_API_CRYPTO_SIZE_T
void av_hash_update(struct AVHashContext *ctx, const uint8_t *src, int len); void av_hash_update(struct AVHashContext *ctx, const uint8_t *src, int len);
#else
void av_hash_update(struct AVHashContext *ctx, const uint8_t *src, size_t len);
#endif
/** /**
* Finalize a hash context and compute the actual hash value. * Finalize a hash context and compute the actual hash value.

View File

@ -0,0 +1,343 @@
/*
* Copyright (c) 2018 Mohammad Izadi <moh.izadi at gmail.com>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef AVUTIL_HDR_DYNAMIC_METADATA_H
#define AVUTIL_HDR_DYNAMIC_METADATA_H
#include "frame.h"
#include "rational.h"
/**
* Option for overlapping elliptical pixel selectors in an image.
*/
enum AVHDRPlusOverlapProcessOption {
AV_HDR_PLUS_OVERLAP_PROCESS_WEIGHTED_AVERAGING = 0,
AV_HDR_PLUS_OVERLAP_PROCESS_LAYERING = 1,
};
/**
* Represents the percentile at a specific percentage in
* a distribution.
*/
typedef struct AVHDRPlusPercentile {
/**
* The percentage value corresponding to a specific percentile linearized
* RGB value in the processing window in the scene. The value shall be in
* the range of 0 to100, inclusive.
*/
uint8_t percentage;
/**
* The linearized maxRGB value at a specific percentile in the processing
* window in the scene. The value shall be in the range of 0 to 1, inclusive
* and in multiples of 0.00001.
*/
AVRational percentile;
} AVHDRPlusPercentile;
/**
* Color transform parameters at a processing window in a dynamic metadata for
* SMPTE 2094-40.
*/
typedef struct AVHDRPlusColorTransformParams {
/**
* The relative x coordinate of the top left pixel of the processing
* window. The value shall be in the range of 0 and 1, inclusive and
* in multiples of 1/(width of Picture - 1). The value 1 corresponds
* to the absolute coordinate of width of Picture - 1. The value for
* first processing window shall be 0.
*/
AVRational window_upper_left_corner_x;
/**
* The relative y coordinate of the top left pixel of the processing
* window. The value shall be in the range of 0 and 1, inclusive and
* in multiples of 1/(height of Picture - 1). The value 1 corresponds
* to the absolute coordinate of height of Picture - 1. The value for
* first processing window shall be 0.
*/
AVRational window_upper_left_corner_y;
/**
* The relative x coordinate of the bottom right pixel of the processing
* window. The value shall be in the range of 0 and 1, inclusive and
* in multiples of 1/(width of Picture - 1). The value 1 corresponds
* to the absolute coordinate of width of Picture - 1. The value for
* first processing window shall be 1.
*/
AVRational window_lower_right_corner_x;
/**
* The relative y coordinate of the bottom right pixel of the processing
* window. The value shall be in the range of 0 and 1, inclusive and
* in multiples of 1/(height of Picture - 1). The value 1 corresponds
* to the absolute coordinate of height of Picture - 1. The value for
* first processing window shall be 1.
*/
AVRational window_lower_right_corner_y;
/**
* The x coordinate of the center position of the concentric internal and
* external ellipses of the elliptical pixel selector in the processing
* window. The value shall be in the range of 0 to (width of Picture - 1),
* inclusive and in multiples of 1 pixel.
*/
uint16_t center_of_ellipse_x;
/**
* The y coordinate of the center position of the concentric internal and
* external ellipses of the elliptical pixel selector in the processing
* window. The value shall be in the range of 0 to (height of Picture - 1),
* inclusive and in multiples of 1 pixel.
*/
uint16_t center_of_ellipse_y;
/**
* The clockwise rotation angle in degree of arc with respect to the
* positive direction of the x-axis of the concentric internal and external
* ellipses of the elliptical pixel selector in the processing window. The
* value shall be in the range of 0 to 180, inclusive and in multiples of 1.
*/
uint8_t rotation_angle;
/**
* The semi-major axis value of the internal ellipse of the elliptical pixel
* selector in amount of pixels in the processing window. The value shall be
* in the range of 1 to 65535, inclusive and in multiples of 1 pixel.
*/
uint16_t semimajor_axis_internal_ellipse;
/**
* The semi-major axis value of the external ellipse of the elliptical pixel
* selector in amount of pixels in the processing window. The value
* shall not be less than semimajor_axis_internal_ellipse of the current
* processing window. The value shall be in the range of 1 to 65535,
* inclusive and in multiples of 1 pixel.
*/
uint16_t semimajor_axis_external_ellipse;
/**
* The semi-minor axis value of the external ellipse of the elliptical pixel
* selector in amount of pixels in the processing window. The value shall be
* in the range of 1 to 65535, inclusive and in multiples of 1 pixel.
*/
uint16_t semiminor_axis_external_ellipse;
/**
* Overlap process option indicates one of the two methods of combining
* rendered pixels in the processing window in an image with at least one
* elliptical pixel selector. For overlapping elliptical pixel selectors
* in an image, overlap_process_option shall have the same value.
*/
enum AVHDRPlusOverlapProcessOption overlap_process_option;
/**
* The maximum of the color components of linearized RGB values in the
* processing window in the scene. The values should be in the range of 0 to
* 1, inclusive and in multiples of 0.00001. maxscl[ 0 ], maxscl[ 1 ], and
* maxscl[ 2 ] are corresponding to R, G, B color components respectively.
*/
AVRational maxscl[3];
/**
* The average of linearized maxRGB values in the processing window in the
* scene. The value should be in the range of 0 to 1, inclusive and in
* multiples of 0.00001.
*/
AVRational average_maxrgb;
/**
* The number of linearized maxRGB values at given percentiles in the
* processing window in the scene. The maximum value shall be 15.
*/
uint8_t num_distribution_maxrgb_percentiles;
/**
* The linearized maxRGB values at given percentiles in the
* processing window in the scene.
*/
AVHDRPlusPercentile distribution_maxrgb[15];
/**
* The fraction of selected pixels in the image that contains the brightest
* pixel in the scene. The value shall be in the range of 0 to 1, inclusive
* and in multiples of 0.001.
*/
AVRational fraction_bright_pixels;
/**
* This flag indicates that the metadata for the tone mapping function in
* the processing window is present (for value of 1).
*/
uint8_t tone_mapping_flag;
/**
* The x coordinate of the separation point between the linear part and the
* curved part of the tone mapping function. The value shall be in the range
* of 0 to 1, excluding 0 and in multiples of 1/4095.
*/
AVRational knee_point_x;
/**
* The y coordinate of the separation point between the linear part and the
* curved part of the tone mapping function. The value shall be in the range
* of 0 to 1, excluding 0 and in multiples of 1/4095.
*/
AVRational knee_point_y;
/**
* The number of the intermediate anchor parameters of the tone mapping
* function in the processing window. The maximum value shall be 15.
*/
uint8_t num_bezier_curve_anchors;
/**
* The intermediate anchor parameters of the tone mapping function in the
* processing window in the scene. The values should be in the range of 0
* to 1, inclusive and in multiples of 1/1023.
*/
AVRational bezier_curve_anchors[15];
/**
* This flag shall be equal to 0 in bitstreams conforming to this version of
* this Specification. Other values are reserved for future use.
*/
uint8_t color_saturation_mapping_flag;
/**
* The color saturation gain in the processing window in the scene. The
* value shall be in the range of 0 to 63/8, inclusive and in multiples of
* 1/8. The default value shall be 1.
*/
AVRational color_saturation_weight;
} AVHDRPlusColorTransformParams;
/**
* This struct represents dynamic metadata for color volume transform -
* application 4 of SMPTE 2094-40:2016 standard.
*
* To be used as payload of a AVFrameSideData or AVPacketSideData with the
* appropriate type.
*
* @note The struct should be allocated with
* av_dynamic_hdr_plus_alloc() and its size is not a part of
* the public ABI.
*/
typedef struct AVDynamicHDRPlus {
/**
* Country code by Rec. ITU-T T.35 Annex A. The value shall be 0xB5.
*/
uint8_t itu_t_t35_country_code;
/**
* Application version in the application defining document in ST-2094
* suite. The value shall be set to 0.
*/
uint8_t application_version;
/**
* The number of processing windows. The value shall be in the range
* of 1 to 3, inclusive.
*/
uint8_t num_windows;
/**
* The color transform parameters for every processing window.
*/
AVHDRPlusColorTransformParams params[3];
/**
* The nominal maximum display luminance of the targeted system display,
* in units of 0.0001 candelas per square metre. The value shall be in
* the range of 0 to 10000, inclusive.
*/
AVRational targeted_system_display_maximum_luminance;
/**
* This flag shall be equal to 0 in bit streams conforming to this version
* of this Specification. The value 1 is reserved for future use.
*/
uint8_t targeted_system_display_actual_peak_luminance_flag;
/**
* The number of rows in the targeted system_display_actual_peak_luminance
* array. The value shall be in the range of 2 to 25, inclusive.
*/
uint8_t num_rows_targeted_system_display_actual_peak_luminance;
/**
* The number of columns in the
* targeted_system_display_actual_peak_luminance array. The value shall be
* in the range of 2 to 25, inclusive.
*/
uint8_t num_cols_targeted_system_display_actual_peak_luminance;
/**
* The normalized actual peak luminance of the targeted system display. The
* values should be in the range of 0 to 1, inclusive and in multiples of
* 1/15.
*/
AVRational targeted_system_display_actual_peak_luminance[25][25];
/**
* This flag shall be equal to 0 in bitstreams conforming to this version of
* this Specification. The value 1 is reserved for future use.
*/
uint8_t mastering_display_actual_peak_luminance_flag;
/**
* The number of rows in the mastering_display_actual_peak_luminance array.
* The value shall be in the range of 2 to 25, inclusive.
*/
uint8_t num_rows_mastering_display_actual_peak_luminance;
/**
* The number of columns in the mastering_display_actual_peak_luminance
* array. The value shall be in the range of 2 to 25, inclusive.
*/
uint8_t num_cols_mastering_display_actual_peak_luminance;
/**
* The normalized actual peak luminance of the mastering display used for
* mastering the image essence. The values should be in the range of 0 to 1,
* inclusive and in multiples of 1/15.
*/
AVRational mastering_display_actual_peak_luminance[25][25];
} AVDynamicHDRPlus;
/**
* Allocate an AVDynamicHDRPlus structure and set its fields to
* default values. The resulting struct can be freed using av_freep().
*
* @return An AVDynamicHDRPlus filled with default values or NULL
* on failure.
*/
AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size);
/**
* Allocate a complete AVDynamicHDRPlus and add it to the frame.
* @param frame The frame which side data is added to.
*
* @return The AVDynamicHDRPlus structure to be filled by caller or NULL
* on failure.
*/
AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame);
#endif /* AVUTIL_HDR_DYNAMIC_METADATA_H */

View File

@ -41,6 +41,7 @@ typedef struct AVCUDADeviceContextInternal AVCUDADeviceContextInternal;
*/ */
typedef struct AVCUDADeviceContext { typedef struct AVCUDADeviceContext {
CUcontext cuda_ctx; CUcontext cuda_ctx;
CUstream stream;
AVCUDADeviceContextInternal *internal; AVCUDADeviceContextInternal *internal;
} AVCUDADeviceContext; } AVCUDADeviceContext;

View File

@ -58,6 +58,9 @@ typedef struct AVDRMObjectDescriptor {
size_t size; size_t size;
/** /**
* Format modifier applied to the object (DRM_FORMAT_MOD_*). * Format modifier applied to the object (DRM_FORMAT_MOD_*).
*
* If the format modifier is unknown then this should be set to
* DRM_FORMAT_MOD_INVALID.
*/ */
uint64_t format_modifier; uint64_t format_modifier;
} AVDRMObjectDescriptor; } AVDRMObjectDescriptor;

View File

@ -542,6 +542,21 @@ union unaligned_16 { uint16_t l; } __attribute__((packed)) av_alias;
# define AV_WN64A(p, v) AV_WNA(64, p, v) # define AV_WN64A(p, v) AV_WNA(64, p, v)
#endif #endif
#if AV_HAVE_BIGENDIAN
# define AV_RLA(s, p) av_bswap##s(AV_RN##s##A(p))
# define AV_WLA(s, p, v) AV_WN##s##A(p, av_bswap##s(v))
#else
# define AV_RLA(s, p) AV_RN##s##A(p)
# define AV_WLA(s, p, v) AV_WN##s##A(p, v)
#endif
#ifndef AV_RL64A
# define AV_RL64A(p) AV_RLA(64, p)
#endif
#ifndef AV_WL64A
# define AV_WL64A(p, v) AV_WLA(64, p, v)
#endif
/* /*
* The AV_COPYxxU macros are suitable for copying data to/from unaligned * The AV_COPYxxU macros are suitable for copying data to/from unaligned
* memory locations. * memory locations.

View File

@ -339,7 +339,7 @@ av_alloc_size(2, 3) void *av_realloc_array(void *ptr, size_t nmemb, size_t size)
* @warning Unlike av_malloc(), the allocated memory is not guaranteed to be * @warning Unlike av_malloc(), the allocated memory is not guaranteed to be
* correctly aligned. * correctly aligned.
*/ */
av_alloc_size(2, 3) int av_reallocp_array(void *ptr, size_t nmemb, size_t size); int av_reallocp_array(void *ptr, size_t nmemb, size_t size);
/** /**
* Reallocate the given buffer if it is not large enough, otherwise do nothing. * Reallocate the given buffer if it is not large enough, otherwise do nothing.
@ -363,10 +363,10 @@ av_alloc_size(2, 3) int av_reallocp_array(void *ptr, size_t nmemb, size_t size);
* @endcode * @endcode
* *
* @param[in,out] ptr Already allocated buffer, or `NULL` * @param[in,out] ptr Already allocated buffer, or `NULL`
* @param[in,out] size Pointer to current size of buffer `ptr`. `*size` is * @param[in,out] size Pointer to the size of buffer `ptr`. `*size` is
* changed to `min_size` in case of success or 0 in * updated to the new allocated size, in particular 0
* case of failure * in case of failure.
* @param[in] min_size New size of buffer `ptr` * @param[in] min_size Desired minimal size of buffer `ptr`
* @return `ptr` if the buffer is large enough, a pointer to newly reallocated * @return `ptr` if the buffer is large enough, a pointer to newly reallocated
* buffer if the buffer was not large enough, or `NULL` in case of * buffer if the buffer was not large enough, or `NULL` in case of
* error * error
@ -397,10 +397,10 @@ void *av_fast_realloc(void *ptr, unsigned int *size, size_t min_size);
* @param[in,out] ptr Pointer to pointer to an already allocated buffer. * @param[in,out] ptr Pointer to pointer to an already allocated buffer.
* `*ptr` will be overwritten with pointer to new * `*ptr` will be overwritten with pointer to new
* buffer on success or `NULL` on failure * buffer on success or `NULL` on failure
* @param[in,out] size Pointer to current size of buffer `*ptr`. `*size` is * @param[in,out] size Pointer to the size of buffer `*ptr`. `*size` is
* changed to `min_size` in case of success or 0 in * updated to the new allocated size, in particular 0
* case of failure * in case of failure.
* @param[in] min_size New size of buffer `*ptr` * @param[in] min_size Desired minimal size of buffer `*ptr`
* @see av_realloc() * @see av_realloc()
* @see av_fast_mallocz() * @see av_fast_mallocz()
*/ */
@ -418,10 +418,10 @@ void av_fast_malloc(void *ptr, unsigned int *size, size_t min_size);
* @param[in,out] ptr Pointer to pointer to an already allocated buffer. * @param[in,out] ptr Pointer to pointer to an already allocated buffer.
* `*ptr` will be overwritten with pointer to new * `*ptr` will be overwritten with pointer to new
* buffer on success or `NULL` on failure * buffer on success or `NULL` on failure
* @param[in,out] size Pointer to current size of buffer `*ptr`. `*size` is * @param[in,out] size Pointer to the size of buffer `*ptr`. `*size` is
* changed to `min_size` in case of success or 0 in * updated to the new allocated size, in particular 0
* case of failure * in case of failure.
* @param[in] min_size New size of buffer `*ptr` * @param[in] min_size Desired minimal size of buffer `*ptr`
* @see av_fast_malloc() * @see av_fast_malloc()
*/ */
void av_fast_mallocz(void *ptr, unsigned int *size, size_t min_size); void av_fast_mallocz(void *ptr, unsigned int *size, size_t min_size);

View File

@ -29,6 +29,8 @@
#include <stdint.h> #include <stdint.h>
#include "version.h"
/** /**
* @defgroup lavu_murmur3 Murmur3 * @defgroup lavu_murmur3 Murmur3
* @ingroup lavu_hash * @ingroup lavu_hash
@ -97,7 +99,11 @@ void av_murmur3_init(struct AVMurMur3 *c);
* @param[in] src Input data to update hash with * @param[in] src Input data to update hash with
* @param[in] len Number of bytes to read from `src` * @param[in] len Number of bytes to read from `src`
*/ */
#if FF_API_CRYPTO_SIZE_T
void av_murmur3_update(struct AVMurMur3 *c, const uint8_t *src, int len); void av_murmur3_update(struct AVMurMur3 *c, const uint8_t *src, int len);
#else
void av_murmur3_update(struct AVMurMur3 *c, const uint8_t *src, size_t len);
#endif
/** /**
* Finish hashing and output digest value. * Finish hashing and output digest value.

View File

@ -229,15 +229,15 @@ enum AVOptionType{
AV_OPT_TYPE_BINARY, ///< offset must point to a pointer immediately followed by an int for the length AV_OPT_TYPE_BINARY, ///< offset must point to a pointer immediately followed by an int for the length
AV_OPT_TYPE_DICT, AV_OPT_TYPE_DICT,
AV_OPT_TYPE_UINT64, AV_OPT_TYPE_UINT64,
AV_OPT_TYPE_CONST = 128, AV_OPT_TYPE_CONST,
AV_OPT_TYPE_IMAGE_SIZE = MKBETAG('S','I','Z','E'), ///< offset must point to two consecutive integers AV_OPT_TYPE_IMAGE_SIZE, ///< offset must point to two consecutive integers
AV_OPT_TYPE_PIXEL_FMT = MKBETAG('P','F','M','T'), AV_OPT_TYPE_PIXEL_FMT,
AV_OPT_TYPE_SAMPLE_FMT = MKBETAG('S','F','M','T'), AV_OPT_TYPE_SAMPLE_FMT,
AV_OPT_TYPE_VIDEO_RATE = MKBETAG('V','R','A','T'), ///< offset must point to AVRational AV_OPT_TYPE_VIDEO_RATE, ///< offset must point to AVRational
AV_OPT_TYPE_DURATION = MKBETAG('D','U','R',' '), AV_OPT_TYPE_DURATION,
AV_OPT_TYPE_COLOR = MKBETAG('C','O','L','R'), AV_OPT_TYPE_COLOR,
AV_OPT_TYPE_CHANNEL_LAYOUT = MKBETAG('C','H','L','A'), AV_OPT_TYPE_CHANNEL_LAYOUT,
AV_OPT_TYPE_BOOL = MKBETAG('B','O','O','L'), AV_OPT_TYPE_BOOL,
}; };
/** /**
@ -287,7 +287,9 @@ typedef struct AVOption {
* This flag only makes sense when AV_OPT_FLAG_EXPORT is also set. * This flag only makes sense when AV_OPT_FLAG_EXPORT is also set.
*/ */
#define AV_OPT_FLAG_READONLY 128 #define AV_OPT_FLAG_READONLY 128
#define AV_OPT_FLAG_BSF_PARAM (1<<8) ///< a generic parameter which can be set by the user for bit stream filtering
#define AV_OPT_FLAG_FILTERING_PARAM (1<<16) ///< a generic parameter which can be set by the user for filtering #define AV_OPT_FLAG_FILTERING_PARAM (1<<16) ///< a generic parameter which can be set by the user for filtering
#define AV_OPT_FLAG_DEPRECATED (1<<17) ///< set if option is deprecated, users should refer to AVOption.help text for more information
//FIXME think about enc-audio, ... style flags //FIXME think about enc-audio, ... style flags
/** /**

View File

@ -154,17 +154,21 @@ typedef struct AVPixFmtDescriptor {
* in some cases be simpler. Or the data can be interpreted purely based on * in some cases be simpler. Or the data can be interpreted purely based on
* the pixel format without using the palette. * the pixel format without using the palette.
* An example of a pseudo-paletted format is AV_PIX_FMT_GRAY8 * An example of a pseudo-paletted format is AV_PIX_FMT_GRAY8
*
* @deprecated This flag is deprecated, and will be removed. When it is removed,
* the extra palette allocation in AVFrame.data[1] is removed as well. Only
* actual paletted formats (as indicated by AV_PIX_FMT_FLAG_PAL) will have a
* palette. Starting with FFmpeg versions which have this flag deprecated, the
* extra "pseudo" palette is already ignored, and API users are not required to
* allocate a palette for AV_PIX_FMT_FLAG_PSEUDOPAL formats (it was required
* before the deprecation, though).
*/ */
#define AV_PIX_FMT_FLAG_PSEUDOPAL (1 << 6) #define AV_PIX_FMT_FLAG_PSEUDOPAL (1 << 6)
/** /**
* The pixel format has an alpha channel. This is set on all formats that * The pixel format has an alpha channel. This is set on all formats that
* support alpha in some way. The exception is AV_PIX_FMT_PAL8, which can * support alpha in some way, including AV_PIX_FMT_PAL8. The alpha is always
* carry alpha as part of the palette. Details are explained in the * straight, never pre-multiplied.
* AVPixelFormat enum, and are also encoded in the corresponding
* AVPixFmtDescriptor.
*
* The alpha is always straight, never pre-multiplied.
* *
* If a codec or a filter does not support alpha, it should set all alpha to * If a codec or a filter does not support alpha, it should set all alpha to
* opaque, or use the equivalent pixel formats without alpha component, e.g. * opaque, or use the equivalent pixel formats without alpha component, e.g.
@ -339,7 +343,13 @@ char *av_get_pix_fmt_string(char *buf, int buf_size,
* format writes the values corresponding to the palette * format writes the values corresponding to the palette
* component c in data[1] to dst, rather than the palette indexes in * component c in data[1] to dst, rather than the palette indexes in
* data[0]. The behavior is undefined if the format is not paletted. * data[0]. The behavior is undefined if the format is not paletted.
* @param dst_element_size size of elements in dst array (2 or 4 byte)
*/ */
void av_read_image_line2(void *dst, const uint8_t *data[4],
const int linesize[4], const AVPixFmtDescriptor *desc,
int x, int y, int c, int w, int read_pal_component,
int dst_element_size);
void av_read_image_line(uint16_t *dst, const uint8_t *data[4], void av_read_image_line(uint16_t *dst, const uint8_t *data[4],
const int linesize[4], const AVPixFmtDescriptor *desc, const int linesize[4], const AVPixFmtDescriptor *desc,
int x, int y, int c, int w, int read_pal_component); int x, int y, int c, int w, int read_pal_component);
@ -357,7 +367,12 @@ void av_read_image_line(uint16_t *dst, const uint8_t *data[4],
* @param y the vertical coordinate of the first pixel to write * @param y the vertical coordinate of the first pixel to write
* @param w the width of the line to write, that is the number of * @param w the width of the line to write, that is the number of
* values to write to the image line * values to write to the image line
* @param src_element_size size of elements in src array (2 or 4 byte)
*/ */
void av_write_image_line2(const void *src, uint8_t *data[4],
const int linesize[4], const AVPixFmtDescriptor *desc,
int x, int y, int c, int w, int src_element_size);
void av_write_image_line(const uint16_t *src, uint8_t *data[4], void av_write_image_line(const uint16_t *src, uint8_t *data[4],
const int linesize[4], const AVPixFmtDescriptor *desc, const int linesize[4], const AVPixFmtDescriptor *desc,
int x, int y, int c, int w); int x, int y, int c, int w);

View File

@ -42,6 +42,10 @@
* This is stored as BGRA on little-endian CPU architectures and ARGB on * This is stored as BGRA on little-endian CPU architectures and ARGB on
* big-endian CPUs. * big-endian CPUs.
* *
* @note
* If the resolution is not a multiple of the chroma subsampling factor
* then the chroma plane resolution must be rounded up.
*
* @par * @par
* When the pixel format is palettized RGB32 (AV_PIX_FMT_PAL8), the palettized * When the pixel format is palettized RGB32 (AV_PIX_FMT_PAL8), the palettized
* image data is stored in AVFrame.data[0]. The palette is transported in * image data is stored in AVFrame.data[0]. The palette is transported in
@ -330,6 +334,17 @@ enum AVPixelFormat {
*/ */
AV_PIX_FMT_OPENCL, AV_PIX_FMT_OPENCL,
AV_PIX_FMT_GRAY14BE, ///< Y , 14bpp, big-endian
AV_PIX_FMT_GRAY14LE, ///< Y , 14bpp, little-endian
AV_PIX_FMT_GRAYF32BE, ///< IEEE-754 single precision Y, 32bpp, big-endian
AV_PIX_FMT_GRAYF32LE, ///< IEEE-754 single precision Y, 32bpp, little-endian
AV_PIX_FMT_YUVA422P12BE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), 12b alpha, big-endian
AV_PIX_FMT_YUVA422P12LE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), 12b alpha, little-endian
AV_PIX_FMT_YUVA444P12BE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), 12b alpha, big-endian
AV_PIX_FMT_YUVA444P12LE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), 12b alpha, little-endian
AV_PIX_FMT_NB ///< number of pixel formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions AV_PIX_FMT_NB ///< number of pixel formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions
}; };
@ -349,6 +364,7 @@ enum AVPixelFormat {
#define AV_PIX_FMT_GRAY9 AV_PIX_FMT_NE(GRAY9BE, GRAY9LE) #define AV_PIX_FMT_GRAY9 AV_PIX_FMT_NE(GRAY9BE, GRAY9LE)
#define AV_PIX_FMT_GRAY10 AV_PIX_FMT_NE(GRAY10BE, GRAY10LE) #define AV_PIX_FMT_GRAY10 AV_PIX_FMT_NE(GRAY10BE, GRAY10LE)
#define AV_PIX_FMT_GRAY12 AV_PIX_FMT_NE(GRAY12BE, GRAY12LE) #define AV_PIX_FMT_GRAY12 AV_PIX_FMT_NE(GRAY12BE, GRAY12LE)
#define AV_PIX_FMT_GRAY14 AV_PIX_FMT_NE(GRAY14BE, GRAY14LE)
#define AV_PIX_FMT_GRAY16 AV_PIX_FMT_NE(GRAY16BE, GRAY16LE) #define AV_PIX_FMT_GRAY16 AV_PIX_FMT_NE(GRAY16BE, GRAY16LE)
#define AV_PIX_FMT_YA16 AV_PIX_FMT_NE(YA16BE, YA16LE) #define AV_PIX_FMT_YA16 AV_PIX_FMT_NE(YA16BE, YA16LE)
#define AV_PIX_FMT_RGB48 AV_PIX_FMT_NE(RGB48BE, RGB48LE) #define AV_PIX_FMT_RGB48 AV_PIX_FMT_NE(RGB48BE, RGB48LE)
@ -397,12 +413,16 @@ enum AVPixelFormat {
#define AV_PIX_FMT_GBRPF32 AV_PIX_FMT_NE(GBRPF32BE, GBRPF32LE) #define AV_PIX_FMT_GBRPF32 AV_PIX_FMT_NE(GBRPF32BE, GBRPF32LE)
#define AV_PIX_FMT_GBRAPF32 AV_PIX_FMT_NE(GBRAPF32BE, GBRAPF32LE) #define AV_PIX_FMT_GBRAPF32 AV_PIX_FMT_NE(GBRAPF32BE, GBRAPF32LE)
#define AV_PIX_FMT_GRAYF32 AV_PIX_FMT_NE(GRAYF32BE, GRAYF32LE)
#define AV_PIX_FMT_YUVA420P9 AV_PIX_FMT_NE(YUVA420P9BE , YUVA420P9LE) #define AV_PIX_FMT_YUVA420P9 AV_PIX_FMT_NE(YUVA420P9BE , YUVA420P9LE)
#define AV_PIX_FMT_YUVA422P9 AV_PIX_FMT_NE(YUVA422P9BE , YUVA422P9LE) #define AV_PIX_FMT_YUVA422P9 AV_PIX_FMT_NE(YUVA422P9BE , YUVA422P9LE)
#define AV_PIX_FMT_YUVA444P9 AV_PIX_FMT_NE(YUVA444P9BE , YUVA444P9LE) #define AV_PIX_FMT_YUVA444P9 AV_PIX_FMT_NE(YUVA444P9BE , YUVA444P9LE)
#define AV_PIX_FMT_YUVA420P10 AV_PIX_FMT_NE(YUVA420P10BE, YUVA420P10LE) #define AV_PIX_FMT_YUVA420P10 AV_PIX_FMT_NE(YUVA420P10BE, YUVA420P10LE)
#define AV_PIX_FMT_YUVA422P10 AV_PIX_FMT_NE(YUVA422P10BE, YUVA422P10LE) #define AV_PIX_FMT_YUVA422P10 AV_PIX_FMT_NE(YUVA422P10BE, YUVA422P10LE)
#define AV_PIX_FMT_YUVA444P10 AV_PIX_FMT_NE(YUVA444P10BE, YUVA444P10LE) #define AV_PIX_FMT_YUVA444P10 AV_PIX_FMT_NE(YUVA444P10BE, YUVA444P10LE)
#define AV_PIX_FMT_YUVA422P12 AV_PIX_FMT_NE(YUVA422P12BE, YUVA422P12LE)
#define AV_PIX_FMT_YUVA444P12 AV_PIX_FMT_NE(YUVA444P12BE, YUVA444P12LE)
#define AV_PIX_FMT_YUVA420P16 AV_PIX_FMT_NE(YUVA420P16BE, YUVA420P16LE) #define AV_PIX_FMT_YUVA420P16 AV_PIX_FMT_NE(YUVA420P16BE, YUVA420P16LE)
#define AV_PIX_FMT_YUVA422P16 AV_PIX_FMT_NE(YUVA422P16BE, YUVA422P16LE) #define AV_PIX_FMT_YUVA422P16 AV_PIX_FMT_NE(YUVA422P16BE, YUVA422P16LE)
#define AV_PIX_FMT_YUVA444P16 AV_PIX_FMT_NE(YUVA444P16BE, YUVA444P16LE) #define AV_PIX_FMT_YUVA444P16 AV_PIX_FMT_NE(YUVA444P16BE, YUVA444P16LE)

View File

@ -66,7 +66,11 @@ int av_ripemd_init(struct AVRIPEMD* context, int bits);
* @param data input data to update hash with * @param data input data to update hash with
* @param len input data length * @param len input data length
*/ */
#if FF_API_CRYPTO_SIZE_T
void av_ripemd_update(struct AVRIPEMD* context, const uint8_t* data, unsigned int len); void av_ripemd_update(struct AVRIPEMD* context, const uint8_t* data, unsigned int len);
#else
void av_ripemd_update(struct AVRIPEMD* context, const uint8_t* data, size_t len);
#endif
/** /**
* Finish hashing and output digest value. * Finish hashing and output digest value.

View File

@ -95,6 +95,14 @@ void av_thread_message_queue_set_err_recv(AVThreadMessageQueue *mq,
void av_thread_message_queue_set_free_func(AVThreadMessageQueue *mq, void av_thread_message_queue_set_free_func(AVThreadMessageQueue *mq,
void (*free_func)(void *msg)); void (*free_func)(void *msg));
/**
* Return the current number of messages in the queue.
*
* @return the current number of messages or AVERROR(ENOSYS) if lavu was built
* without thread support
*/
int av_thread_message_queue_nb_elems(AVThreadMessageQueue *mq);
/** /**
* Flush the message queue * Flush the message queue
* *

View File

@ -79,7 +79,7 @@
*/ */
#define LIBAVUTIL_VERSION_MAJOR 56 #define LIBAVUTIL_VERSION_MAJOR 56
#define LIBAVUTIL_VERSION_MINOR 7 #define LIBAVUTIL_VERSION_MINOR 26
#define LIBAVUTIL_VERSION_MICRO 100 #define LIBAVUTIL_VERSION_MICRO 100
#define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
@ -126,6 +126,9 @@
#ifndef FF_API_FRAME_GET_SET #ifndef FF_API_FRAME_GET_SET
#define FF_API_FRAME_GET_SET (LIBAVUTIL_VERSION_MAJOR < 57) #define FF_API_FRAME_GET_SET (LIBAVUTIL_VERSION_MAJOR < 57)
#endif #endif
#ifndef FF_API_PSEUDOPAL
#define FF_API_PSEUDOPAL (LIBAVUTIL_VERSION_MAJOR < 57)
#endif
/** /**

View File

@ -0,0 +1,336 @@
/*
* Copyright (C) 2001-2011 Michael Niedermayer <michaelni@gmx.at>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SWSCALE_SWSCALE_H
#define SWSCALE_SWSCALE_H
/**
* @file
* @ingroup libsws
* external API header
*/
#include <stdint.h>
#include "libavutil/avutil.h"
#include "libavutil/log.h"
#include "libavutil/pixfmt.h"
#include "version.h"
/**
* @defgroup libsws libswscale
* Color conversion and scaling library.
*
* @{
*
* Return the LIBSWSCALE_VERSION_INT constant.
*/
unsigned swscale_version(void);
/**
* Return the libswscale build-time configuration.
*/
const char *swscale_configuration(void);
/**
* Return the libswscale license.
*/
const char *swscale_license(void);
/* values for the flags, the stuff on the command line is different */
#define SWS_FAST_BILINEAR 1
#define SWS_BILINEAR 2
#define SWS_BICUBIC 4
#define SWS_X 8
#define SWS_POINT 0x10
#define SWS_AREA 0x20
#define SWS_BICUBLIN 0x40
#define SWS_GAUSS 0x80
#define SWS_SINC 0x100
#define SWS_LANCZOS 0x200
#define SWS_SPLINE 0x400
#define SWS_SRC_V_CHR_DROP_MASK 0x30000
#define SWS_SRC_V_CHR_DROP_SHIFT 16
#define SWS_PARAM_DEFAULT 123456
#define SWS_PRINT_INFO 0x1000
//the following 3 flags are not completely implemented
//internal chrominance subsampling info
#define SWS_FULL_CHR_H_INT 0x2000
//input subsampling info
#define SWS_FULL_CHR_H_INP 0x4000
#define SWS_DIRECT_BGR 0x8000
#define SWS_ACCURATE_RND 0x40000
#define SWS_BITEXACT 0x80000
#define SWS_ERROR_DIFFUSION 0x800000
#define SWS_MAX_REDUCE_CUTOFF 0.002
#define SWS_CS_ITU709 1
#define SWS_CS_FCC 4
#define SWS_CS_ITU601 5
#define SWS_CS_ITU624 5
#define SWS_CS_SMPTE170M 5
#define SWS_CS_SMPTE240M 7
#define SWS_CS_DEFAULT 5
#define SWS_CS_BT2020 9
/**
* Return a pointer to yuv<->rgb coefficients for the given colorspace
* suitable for sws_setColorspaceDetails().
*
* @param colorspace One of the SWS_CS_* macros. If invalid,
* SWS_CS_DEFAULT is used.
*/
const int *sws_getCoefficients(int colorspace);
// when used for filters they must have an odd number of elements
// coeffs cannot be shared between vectors
typedef struct SwsVector {
double *coeff; ///< pointer to the list of coefficients
int length; ///< number of coefficients in the vector
} SwsVector;
// vectors can be shared
typedef struct SwsFilter {
SwsVector *lumH;
SwsVector *lumV;
SwsVector *chrH;
SwsVector *chrV;
} SwsFilter;
struct SwsContext;
/**
* Return a positive value if pix_fmt is a supported input format, 0
* otherwise.
*/
int sws_isSupportedInput(enum AVPixelFormat pix_fmt);
/**
* Return a positive value if pix_fmt is a supported output format, 0
* otherwise.
*/
int sws_isSupportedOutput(enum AVPixelFormat pix_fmt);
/**
* @param[in] pix_fmt the pixel format
* @return a positive value if an endianness conversion for pix_fmt is
* supported, 0 otherwise.
*/
int sws_isSupportedEndiannessConversion(enum AVPixelFormat pix_fmt);
/**
* Allocate an empty SwsContext. This must be filled and passed to
* sws_init_context(). For filling see AVOptions, options.c and
* sws_setColorspaceDetails().
*/
struct SwsContext *sws_alloc_context(void);
/**
* Initialize the swscaler context sws_context.
*
* @return zero or positive value on success, a negative value on
* error
*/
av_warn_unused_result
int sws_init_context(struct SwsContext *sws_context, SwsFilter *srcFilter, SwsFilter *dstFilter);
/**
* Free the swscaler context swsContext.
* If swsContext is NULL, then does nothing.
*/
void sws_freeContext(struct SwsContext *swsContext);
/**
* Allocate and return an SwsContext. You need it to perform
* scaling/conversion operations using sws_scale().
*
* @param srcW the width of the source image
* @param srcH the height of the source image
* @param srcFormat the source image format
* @param dstW the width of the destination image
* @param dstH the height of the destination image
* @param dstFormat the destination image format
* @param flags specify which algorithm and options to use for rescaling
* @param param extra parameters to tune the used scaler
* For SWS_BICUBIC param[0] and [1] tune the shape of the basis
* function, param[0] tunes f(1) and param[1] f´(1)
* For SWS_GAUSS param[0] tunes the exponent and thus cutoff
* frequency
* For SWS_LANCZOS param[0] tunes the width of the window function
* @return a pointer to an allocated context, or NULL in case of error
* @note this function is to be removed after a saner alternative is
* written
*/
struct SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat,
int dstW, int dstH, enum AVPixelFormat dstFormat,
int flags, SwsFilter *srcFilter,
SwsFilter *dstFilter, const double *param);
/**
* Scale the image slice in srcSlice and put the resulting scaled
* slice in the image in dst. A slice is a sequence of consecutive
* rows in an image.
*
* Slices have to be provided in sequential order, either in
* top-bottom or bottom-top order. If slices are provided in
* non-sequential order the behavior of the function is undefined.
*
* @param c the scaling context previously created with
* sws_getContext()
* @param srcSlice the array containing the pointers to the planes of
* the source slice
* @param srcStride the array containing the strides for each plane of
* the source image
* @param srcSliceY the position in the source image of the slice to
* process, that is the number (counted starting from
* zero) in the image of the first row of the slice
* @param srcSliceH the height of the source slice, that is the number
* of rows in the slice
* @param dst the array containing the pointers to the planes of
* the destination image
* @param dstStride the array containing the strides for each plane of
* the destination image
* @return the height of the output slice
*/
int sws_scale(struct SwsContext *c, const uint8_t *const srcSlice[],
const int srcStride[], int srcSliceY, int srcSliceH,
uint8_t *const dst[], const int dstStride[]);
/**
* @param dstRange flag indicating the while-black range of the output (1=jpeg / 0=mpeg)
* @param srcRange flag indicating the while-black range of the input (1=jpeg / 0=mpeg)
* @param table the yuv2rgb coefficients describing the output yuv space, normally ff_yuv2rgb_coeffs[x]
* @param inv_table the yuv2rgb coefficients describing the input yuv space, normally ff_yuv2rgb_coeffs[x]
* @param brightness 16.16 fixed point brightness correction
* @param contrast 16.16 fixed point contrast correction
* @param saturation 16.16 fixed point saturation correction
* @return -1 if not supported
*/
int sws_setColorspaceDetails(struct SwsContext *c, const int inv_table[4],
int srcRange, const int table[4], int dstRange,
int brightness, int contrast, int saturation);
/**
* @return -1 if not supported
*/
int sws_getColorspaceDetails(struct SwsContext *c, int **inv_table,
int *srcRange, int **table, int *dstRange,
int *brightness, int *contrast, int *saturation);
/**
* Allocate and return an uninitialized vector with length coefficients.
*/
SwsVector *sws_allocVec(int length);
/**
* Return a normalized Gaussian curve used to filter stuff
* quality = 3 is high quality, lower is lower quality.
*/
SwsVector *sws_getGaussianVec(double variance, double quality);
/**
* Scale all the coefficients of a by the scalar value.
*/
void sws_scaleVec(SwsVector *a, double scalar);
/**
* Scale all the coefficients of a so that their sum equals height.
*/
void sws_normalizeVec(SwsVector *a, double height);
#if FF_API_SWS_VECTOR
attribute_deprecated SwsVector *sws_getConstVec(double c, int length);
attribute_deprecated SwsVector *sws_getIdentityVec(void);
attribute_deprecated void sws_convVec(SwsVector *a, SwsVector *b);
attribute_deprecated void sws_addVec(SwsVector *a, SwsVector *b);
attribute_deprecated void sws_subVec(SwsVector *a, SwsVector *b);
attribute_deprecated void sws_shiftVec(SwsVector *a, int shift);
attribute_deprecated SwsVector *sws_cloneVec(SwsVector *a);
attribute_deprecated void sws_printVec2(SwsVector *a, AVClass *log_ctx, int log_level);
#endif
void sws_freeVec(SwsVector *a);
SwsFilter *sws_getDefaultFilter(float lumaGBlur, float chromaGBlur,
float lumaSharpen, float chromaSharpen,
float chromaHShift, float chromaVShift,
int verbose);
void sws_freeFilter(SwsFilter *filter);
/**
* Check if context can be reused, otherwise reallocate a new one.
*
* If context is NULL, just calls sws_getContext() to get a new
* context. Otherwise, checks if the parameters are the ones already
* saved in context. If that is the case, returns the current
* context. Otherwise, frees context and gets a new context with
* the new parameters.
*
* Be warned that srcFilter and dstFilter are not checked, they
* are assumed to remain the same.
*/
struct SwsContext *sws_getCachedContext(struct SwsContext *context,
int srcW, int srcH, enum AVPixelFormat srcFormat,
int dstW, int dstH, enum AVPixelFormat dstFormat,
int flags, SwsFilter *srcFilter,
SwsFilter *dstFilter, const double *param);
/**
* Convert an 8-bit paletted frame into a frame with a color depth of 32 bits.
*
* The output frame will have the same packed format as the palette.
*
* @param src source frame buffer
* @param dst destination frame buffer
* @param num_pixels number of pixels to convert
* @param palette array with [256] entries, which must match color arrangement (RGB or BGR) of src
*/
void sws_convertPalette8ToPacked32(const uint8_t *src, uint8_t *dst, int num_pixels, const uint8_t *palette);
/**
* Convert an 8-bit paletted frame into a frame with a color depth of 24 bits.
*
* With the palette format "ABCD", the destination frame ends up with the format "ABC".
*
* @param src source frame buffer
* @param dst destination frame buffer
* @param num_pixels number of pixels to convert
* @param palette array with [256] entries, which must match color arrangement (RGB or BGR) of src
*/
void sws_convertPalette8ToPacked24(const uint8_t *src, uint8_t *dst, int num_pixels, const uint8_t *palette);
/**
* Get the AVClass for swsContext. It can be used in combination with
* AV_OPT_SEARCH_FAKE_OBJ for examining options.
*
* @see av_opt_find().
*/
const AVClass *sws_get_class(void);
/**
* @}
*/
#endif /* SWSCALE_SWSCALE_H */

View File

@ -0,0 +1,53 @@
/*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SWSCALE_VERSION_H
#define SWSCALE_VERSION_H
/**
* @file
* swscale version macros
*/
#include "libavutil/version.h"
#define LIBSWSCALE_VERSION_MAJOR 5
#define LIBSWSCALE_VERSION_MINOR 4
#define LIBSWSCALE_VERSION_MICRO 100
#define LIBSWSCALE_VERSION_INT AV_VERSION_INT(LIBSWSCALE_VERSION_MAJOR, \
LIBSWSCALE_VERSION_MINOR, \
LIBSWSCALE_VERSION_MICRO)
#define LIBSWSCALE_VERSION AV_VERSION(LIBSWSCALE_VERSION_MAJOR, \
LIBSWSCALE_VERSION_MINOR, \
LIBSWSCALE_VERSION_MICRO)
#define LIBSWSCALE_BUILD LIBSWSCALE_VERSION_INT
#define LIBSWSCALE_IDENT "SwS" AV_STRINGIFY(LIBSWSCALE_VERSION)
/**
* FF_API_* defines may be placed below to indicate public API that will be
* dropped at a future version bump. The defines themselves are not part of
* the public API and may change, break or disappear at any time.
*/
#ifndef FF_API_SWS_VECTOR
#define FF_API_SWS_VECTOR (LIBSWSCALE_VERSION_MAJOR < 6)
#endif
#endif /* SWSCALE_VERSION_H */

Binary file not shown.

View File

@ -1,14 +1,20 @@
#include <jni.h> #include <jni.h>
#include <utils.h>
#include <libyuv.h>
#include <android/bitmap.h> #include <android/bitmap.h>
#include <cstdint> #include <cstdint>
#include <limits> #include <limits>
#include <string> #include <string>
#include <unistd.h>
#include <linux/stat.h>
#include <asm/fcntl.h>
#include <fcntl.h>
#include <libyuv.h>
#include "tgnet/ConnectionsManager.h"
#include "c_utils.h"
extern "C" { extern "C" {
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <libavutil/eval.h> #include <libavutil/eval.h>
#include <libswscale/swscale.h>
static const std::string av_make_error_str(int errnum) { static const std::string av_make_error_str(int errnum) {
char errbuf[AV_ERROR_MAX_STRING_SIZE]; char errbuf[AV_ERROR_MAX_STRING_SIZE];
@ -18,6 +24,11 @@ static const std::string av_make_error_str(int errnum) {
#undef av_err2str #undef av_err2str
#define av_err2str(errnum) av_make_error_str(errnum).c_str() #define av_err2str(errnum) av_make_error_str(errnum).c_str()
#define FFMPEG_AVSEEK_SIZE 0x10000
jclass jclass_AnimatedFileDrawableStream;
jmethodID jclass_AnimatedFileDrawableStream_read;
jmethodID jclass_AnimatedFileDrawableStream_cancel;
typedef struct VideoInfo { typedef struct VideoInfo {
@ -38,12 +49,47 @@ typedef struct VideoInfo {
delete [] src; delete [] src;
src = nullptr; src = nullptr;
} }
if (stream != nullptr) {
JNIEnv *jniEnv = nullptr;
JavaVMAttachArgs jvmArgs;
jvmArgs.version = JNI_VERSION_1_6;
bool attached;
if (JNI_EDETACHED == javaVm->GetEnv((void **) &jniEnv, JNI_VERSION_1_6)) {
javaVm->AttachCurrentThread(&jniEnv, &jvmArgs);
attached = true;
} else {
attached = false;
}
jniEnv->DeleteGlobalRef(stream);
if (attached) {
javaVm->DetachCurrentThread();
}
stream = nullptr;
}
/*if (ioBuffer) { TODO memleak?
av_free(ioBuffer);
ioBuffer = nullptr;
}*/
if (ioContext != nullptr) {
avio_context_free(&ioContext);
ioContext = nullptr;
}
if (sws_ctx != nullptr) {
sws_freeContext(sws_ctx);
sws_ctx = nullptr;
}
if (fd >= 0) {
close(fd);
fd = -1;
}
av_packet_unref(&orig_pkt); av_packet_unref(&orig_pkt);
video_stream_idx = -1; video_stream_idx = -1;
video_stream = nullptr; video_stream = nullptr;
} }
AVFormatContext *fmt_ctx = nullptr; AVFormatContext *fmt_ctx = nullptr;
char *src = nullptr; char *src = nullptr;
int video_stream_idx = -1; int video_stream_idx = -1;
@ -53,14 +99,22 @@ typedef struct VideoInfo {
bool has_decoded_frames = false; bool has_decoded_frames = false;
AVPacket pkt; AVPacket pkt;
AVPacket orig_pkt; AVPacket orig_pkt;
}; bool stopped = false;
bool seeking = false;
jobject makeGlobarRef(JNIEnv *env, jobject object) { uint8_t __attribute__ ((aligned (16))) *dst_data[1];
if (object) { int32_t dst_linesize[1];
return env->NewGlobalRef(object);
} struct SwsContext *sws_ctx = nullptr;
return 0;
} AVIOContext *ioContext = nullptr;
unsigned char *ioBuffer = nullptr;
jobject stream = nullptr;
int32_t account = 0;
int fd = -1;
int64_t file_size = 0;
int64_t last_seek_p = 0;
};
int open_codec_context(int *stream_idx, AVCodecContext **dec_ctx, AVFormatContext *fmt_ctx, enum AVMediaType type) { int open_codec_context(int *stream_idx, AVCodecContext **dec_ctx, AVFormatContext *fmt_ctx, enum AVMediaType type) {
int ret, stream_index; int ret, stream_index;
@ -119,7 +173,82 @@ int decode_packet(VideoInfo *info, int *got_frame) {
return decoded; return decoded;
} }
jlong Java_org_telegram_ui_Components_AnimatedFileDrawable_createDecoder(JNIEnv *env, jclass clazz, jstring src, jintArray data) { void requestFd(VideoInfo *info) {
JNIEnv *jniEnv = nullptr;
JavaVMAttachArgs jvmArgs;
jvmArgs.version = JNI_VERSION_1_6;
bool attached;
if (JNI_EDETACHED == javaVm->GetEnv((void **) &jniEnv, JNI_VERSION_1_6)) {
javaVm->AttachCurrentThread(&jniEnv, &jvmArgs);
attached = true;
} else {
attached = false;
}
jniEnv->CallIntMethod(info->stream, jclass_AnimatedFileDrawableStream_read, (jint) 0, (jint) 1);
if (attached) {
javaVm->DetachCurrentThread();
}
info->fd = open(info->src, O_RDONLY, S_IRUSR);
}
int readCallback(void *opaque, uint8_t *buf, int buf_size) {
VideoInfo *info = (VideoInfo *) opaque;
if (!info->stopped) {
if (info->fd < 0) {
requestFd(info);
}
if (info->fd >= 0) {
if (info->last_seek_p + buf_size > info->file_size) {
buf_size = (int) (info->file_size - info->last_seek_p);
}
if (buf_size > 0) {
JNIEnv *jniEnv = nullptr;
JavaVMAttachArgs jvmArgs;
jvmArgs.version = JNI_VERSION_1_6;
bool attached;
if (JNI_EDETACHED == javaVm->GetEnv((void **) &jniEnv, JNI_VERSION_1_6)) {
javaVm->AttachCurrentThread(&jniEnv, &jvmArgs);
attached = true;
} else {
attached = false;
}
buf_size = jniEnv->CallIntMethod(info->stream, jclass_AnimatedFileDrawableStream_read, (jint) info->last_seek_p, (jint) buf_size);
info->last_seek_p += buf_size;
if (attached) {
javaVm->DetachCurrentThread();
}
return (int) read(info->fd, buf, (size_t) buf_size);
}
}
}
return 0;
}
int64_t seekCallback(void *opaque, int64_t offset, int whence) {
VideoInfo *info = (VideoInfo *) opaque;
if (!info->stopped) {
if (info->fd < 0) {
requestFd(info);
}
if (info->fd >= 0) {
if (whence & FFMPEG_AVSEEK_SIZE) {
return info->file_size;
} else {
info->last_seek_p = offset;
lseek(info->fd, off_t(offset), SEEK_SET);
return offset;
}
}
}
return 0;
}
jlong Java_org_telegram_ui_Components_AnimatedFileDrawable_createDecoder(JNIEnv *env, jclass clazz, jstring src, jintArray data, jint account, jlong streamFileSize, jobject stream) {
VideoInfo *info = new VideoInfo(); VideoInfo *info = new VideoInfo();
char const *srcString = env->GetStringUTFChars(src, 0); char const *srcString = env->GetStringUTFChars(src, 0);
@ -130,12 +259,40 @@ jlong Java_org_telegram_ui_Components_AnimatedFileDrawable_createDecoder(JNIEnv
if (srcString != 0) { if (srcString != 0) {
env->ReleaseStringUTFChars(src, srcString); env->ReleaseStringUTFChars(src, srcString);
} }
int ret; int ret;
if ((ret = avformat_open_input(&info->fmt_ctx, info->src, NULL, NULL)) < 0) { if (streamFileSize != 0) {
LOGE("can't open source file %s, %s", info->src, av_err2str(ret)); info->file_size = streamFileSize;
delete info; info->stream = env->NewGlobalRef(stream);
return 0; info->account = account;
info->fd = open(info->src, O_RDONLY, S_IRUSR);
info->ioBuffer = (unsigned char *) av_malloc(64 * 1024);
info->ioContext = avio_alloc_context(info->ioBuffer, 64 * 1024, 0, info, readCallback, nullptr, seekCallback);
if (info->ioContext == nullptr) {
delete info;
return 0;
}
info->fmt_ctx = avformat_alloc_context();
info->fmt_ctx->pb = info->ioContext;
AVDictionary *options = NULL;
av_dict_set(&options, "usetoc", "1", 0);
ret = avformat_open_input(&info->fmt_ctx, "http://localhost/file", NULL, &options);
av_dict_free(&options);
if (ret < 0) {
LOGE("can't open source file %s, %s", info->src, av_err2str(ret));
delete info;
return 0;
}
info->fmt_ctx->flags |= AVFMT_FLAG_FAST_SEEK;
} else {
if ((ret = avformat_open_input(&info->fmt_ctx, info->src, NULL, NULL)) < 0) {
LOGE("can't open source file %s, %s", info->src, av_err2str(ret));
delete info;
return 0;
}
} }
if ((ret = avformat_find_stream_info(info->fmt_ctx, NULL)) < 0) { if ((ret = avformat_find_stream_info(info->fmt_ctx, NULL)) < 0) {
@ -164,11 +321,12 @@ jlong Java_org_telegram_ui_Components_AnimatedFileDrawable_createDecoder(JNIEnv
av_init_packet(&info->pkt); av_init_packet(&info->pkt);
info->pkt.data = NULL; info->pkt.data = NULL;
info->pkt.size = 0; info->pkt.size = 0;
jint *dataArr = env->GetIntArrayElements(data, 0); jint *dataArr = env->GetIntArrayElements(data, 0);
if (dataArr != nullptr) { if (dataArr != nullptr) {
dataArr[0] = info->video_dec_ctx->width; dataArr[0] = info->video_dec_ctx->width;
dataArr[1] = info->video_dec_ctx->height; dataArr[1] = info->video_dec_ctx->height;
//float pixelWidthHeightRatio = info->video_dec_ctx->sample_aspect_ratio.num / info->video_dec_ctx->sample_aspect_ratio.den; TODO support
AVDictionaryEntry *rotate_tag = av_dict_get(info->video_stream->metadata, "rotate", NULL, 0); AVDictionaryEntry *rotate_tag = av_dict_get(info->video_stream->metadata, "rotate", NULL, 0);
if (rotate_tag && *rotate_tag->value && strcmp(rotate_tag->value, "0")) { if (rotate_tag && *rotate_tag->value && strcmp(rotate_tag->value, "0")) {
char *tail; char *tail;
@ -179,6 +337,8 @@ jlong Java_org_telegram_ui_Components_AnimatedFileDrawable_createDecoder(JNIEnv
} else { } else {
dataArr[2] = 0; dataArr[2] = 0;
} }
dataArr[4] = (int32_t) (info->fmt_ctx->duration * 1000 / AV_TIME_BASE);
//(int32_t) (1000 * info->video_stream->duration * av_q2d(info->video_stream->time_base));
env->ReleaseIntArrayElements(data, dataArr, 0); env->ReleaseIntArrayElements(data, dataArr, 0);
} }
@ -192,19 +352,123 @@ void Java_org_telegram_ui_Components_AnimatedFileDrawable_destroyDecoder(JNIEnv
return; return;
} }
VideoInfo *info = (VideoInfo *) (intptr_t) ptr; VideoInfo *info = (VideoInfo *) (intptr_t) ptr;
if (info->stream != nullptr) {
JNIEnv *jniEnv = nullptr;
JavaVMAttachArgs jvmArgs;
jvmArgs.version = JNI_VERSION_1_6;
bool attached;
if (JNI_EDETACHED == javaVm->GetEnv((void **) &jniEnv, JNI_VERSION_1_6)) {
javaVm->AttachCurrentThread(&jniEnv, &jvmArgs);
attached = true;
} else {
attached = false;
}
jniEnv->CallVoidMethod(info->stream, jclass_AnimatedFileDrawableStream_cancel);
if (attached) {
javaVm->DetachCurrentThread();
}
}
delete info; delete info;
} }
void Java_org_telegram_ui_Components_AnimatedFileDrawable_stopDecoder(JNIEnv *env, jclass clazz, jlong ptr) {
if (ptr == NULL) {
return;
}
VideoInfo *info = (VideoInfo *) (intptr_t) ptr;
info->stopped = true;
}
void Java_org_telegram_ui_Components_AnimatedFileDrawable_prepareToSeek(JNIEnv *env, jclass clazz, jlong ptr) {
if (ptr == NULL) {
return;
}
VideoInfo *info = (VideoInfo *) (intptr_t) ptr;
info->seeking = true;
}
void Java_org_telegram_ui_Components_AnimatedFileDrawable_seekToMs(JNIEnv *env, jclass clazz, jlong ptr, jlong ms) {
if (ptr == NULL) {
return;
}
VideoInfo *info = (VideoInfo *) (intptr_t) ptr;
info->seeking = false;
int64_t pts = (int64_t) (ms / av_q2d(info->video_stream->time_base) / 1000);
int ret = 0;
if ((ret = av_seek_frame(info->fmt_ctx, info->video_stream_idx, pts, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME)) < 0) {
LOGE("can't seek file %s, %s", info->src, av_err2str(ret));
return;
} else {
avcodec_flush_buffers(info->video_dec_ctx);
int got_frame = 0;
int32_t tries = 1000;
while (tries > 0) {
if (info->pkt.size == 0) {
ret = av_read_frame(info->fmt_ctx, &info->pkt);
if (ret >= 0) {
info->orig_pkt = info->pkt;
}
}
if (info->pkt.size > 0) {
ret = decode_packet(info, &got_frame);
if (ret < 0) {
if (info->has_decoded_frames) {
ret = 0;
}
info->pkt.size = 0;
} else {
info->pkt.data += ret;
info->pkt.size -= ret;
}
if (info->pkt.size == 0) {
av_packet_unref(&info->orig_pkt);
}
} else {
info->pkt.data = NULL;
info->pkt.size = 0;
ret = decode_packet(info, &got_frame);
if (ret < 0) {
return;
}
if (got_frame == 0) {
av_seek_frame(info->fmt_ctx, info->video_stream_idx, 0, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);
return;
}
}
if (ret < 0) {
return;
}
if (got_frame) {
if (info->frame->format == AV_PIX_FMT_YUV420P || info->frame->format == AV_PIX_FMT_BGRA || info->frame->format == AV_PIX_FMT_YUVJ420P) {
int64_t pkt_pts;
if (info->frame->pts != AV_NOPTS_VALUE) {
pkt_pts = info->frame->pts;
} else {
pkt_pts = info->frame->pkt_dts;
}
if (pkt_pts >= pts) {
return;
}
}
av_frame_unref(info->frame);
}
tries--;
}
}
}
jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv *env, jclass clazz, jlong ptr, jobject bitmap, jintArray data) { jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv *env, jclass clazz, jlong ptr, jobject bitmap, jintArray data, jint stride) {
if (ptr == NULL || bitmap == nullptr) { if (ptr == NULL || bitmap == nullptr) {
return 0; return 0;
} }
//int64_t time = ConnectionsManager::getInstance(0).getCurrentTimeMonotonicMillis();
VideoInfo *info = (VideoInfo *) (intptr_t) ptr; VideoInfo *info = (VideoInfo *) (intptr_t) ptr;
int ret = 0; int ret = 0;
int got_frame = 0; int got_frame = 0;
int32_t triesCount = 6; int32_t triesCount = 6;
while (triesCount != 0) { while (!info->stopped && triesCount != 0) {
if (info->pkt.size == 0) { if (info->pkt.size == 0) {
ret = av_read_frame(info->fmt_ctx, &info->pkt); ret = av_read_frame(info->fmt_ctx, &info->pkt);
//LOGD("got packet with size %d", info->pkt.size); //LOGD("got packet with size %d", info->pkt.size);
@ -212,7 +476,7 @@ jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv *
info->orig_pkt = info->pkt; info->orig_pkt = info->pkt;
} }
} }
if (info->pkt.size > 0) { if (info->pkt.size > 0) {
ret = decode_packet(info, &got_frame); ret = decode_packet(info, &got_frame);
if (ret < 0) { if (ret < 0) {
@ -225,7 +489,7 @@ jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv *
info->pkt.data += ret; info->pkt.data += ret;
info->pkt.size -= ret; info->pkt.size -= ret;
} }
if (info->pkt.size == 0) { if (info->pkt.size == 0) {
av_packet_unref(&info->orig_pkt); av_packet_unref(&info->orig_pkt);
} }
@ -239,8 +503,7 @@ jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv *
} }
if (got_frame == 0) { if (got_frame == 0) {
if (info->has_decoded_frames) { if (info->has_decoded_frames) {
//LOGD("file end reached %s", info->src); if ((ret = av_seek_frame(info->fmt_ctx, info->video_stream_idx, 0, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME)) < 0) {
if ((ret = avformat_seek_file(info->fmt_ctx, -1, std::numeric_limits<int64_t>::min(), 0, std::numeric_limits<int64_t>::max(), 0)) < 0) {
LOGE("can't seek to begin of file %s, %s", info->src, av_err2str(ret)); LOGE("can't seek to begin of file %s, %s", info->src, av_err2str(ret));
return 0; return 0;
} else { } else {
@ -249,7 +512,7 @@ jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv *
} }
} }
} }
if (ret < 0) { if (ret < 0 || info->seeking) {
return 0; return 0;
} }
if (got_frame) { if (got_frame) {
@ -261,7 +524,11 @@ jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv *
if (dataArr != nullptr) { if (dataArr != nullptr) {
wantedWidth = dataArr[0]; wantedWidth = dataArr[0];
wantedHeight = dataArr[1]; wantedHeight = dataArr[1];
dataArr[3] = (jint) (1000 * info->frame->pts * av_q2d(info->video_stream->time_base)); if (info->frame->pts != AV_NOPTS_VALUE) {
dataArr[3] = (jint) (1000 * info->frame->pts * av_q2d(info->video_stream->time_base));
} else {
dataArr[3] = (jint) (1000 * info->frame->pkt_dts * av_q2d(info->video_stream->time_base));
}
env->ReleaseIntArrayElements(data, dataArr, 0); env->ReleaseIntArrayElements(data, dataArr, 0);
} else { } else {
AndroidBitmapInfo bitmapInfo; AndroidBitmapInfo bitmapInfo;
@ -269,26 +536,64 @@ jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv *
wantedWidth = bitmapInfo.width; wantedWidth = bitmapInfo.width;
wantedHeight = bitmapInfo.height; wantedHeight = bitmapInfo.height;
} }
void *pixels; void *pixels;
if (AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0) { if (AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0) {
if (info->frame->format == AV_PIX_FMT_YUV420P || info->frame->format == AV_PIX_FMT_YUVJ420P) { if (wantedWidth == info->frame->width && wantedHeight == info->frame->height || wantedWidth == info->frame->height && wantedHeight == info->frame->width) {
//LOGD("y %d, u %d, v %d, width %d, height %d", info->frame->linesize[0], info->frame->linesize[2], info->frame->linesize[1], info->frame->width, info->frame->height); if (info->sws_ctx == nullptr) {
if (wantedWidth == info->frame->width && wantedHeight == info->frame->height || wantedWidth == info->frame->height && wantedHeight == info->frame->width) { if (info->frame->format > AV_PIX_FMT_NONE && info->frame->format < AV_PIX_FMT_NB) {
libyuv::I420ToARGB(info->frame->data[0], info->frame->linesize[0], info->frame->data[2], info->frame->linesize[2], info->frame->data[1], info->frame->linesize[1], (uint8_t *) pixels, info->frame->width * 4, info->frame->width, info->frame->height); info->sws_ctx = sws_getContext(info->frame->width, info->frame->height, (AVPixelFormat) info->frame->format, info->frame->width, info->frame->height, AV_PIX_FMT_RGBA, SWS_BILINEAR, NULL, NULL, NULL);
} else if (info->video_dec_ctx->pix_fmt > AV_PIX_FMT_NONE && info->video_dec_ctx->pix_fmt < AV_PIX_FMT_NB) {
info->sws_ctx = sws_getContext(info->video_dec_ctx->width, info->video_dec_ctx->height, info->video_dec_ctx->pix_fmt, info->video_dec_ctx->width, info->video_dec_ctx->height, AV_PIX_FMT_RGBA, SWS_BILINEAR, NULL, NULL, NULL);
}
}
if (info->sws_ctx == nullptr || ((intptr_t) pixels) % 16 != 0) {
if (info->frame->format == AV_PIX_FMT_YUV420P || info->frame->format == AV_PIX_FMT_YUVJ420P) {
if (info->frame->colorspace == AVColorSpace::AVCOL_SPC_BT709) {
libyuv::H420ToARGB(info->frame->data[0], info->frame->linesize[0], info->frame->data[2], info->frame->linesize[2], info->frame->data[1], info->frame->linesize[1], (uint8_t *) pixels, info->frame->width * 4, info->frame->width, info->frame->height);
} else {
libyuv::I420ToARGB(info->frame->data[0], info->frame->linesize[0], info->frame->data[2], info->frame->linesize[2], info->frame->data[1], info->frame->linesize[1], (uint8_t *) pixels, info->frame->width * 4, info->frame->width, info->frame->height);
}
} else if (info->frame->format == AV_PIX_FMT_BGRA) {
libyuv::ABGRToARGB(info->frame->data[0], info->frame->linesize[0], (uint8_t *) pixels, info->frame->width * 4, info->frame->width, info->frame->height);
}
} else {
info->dst_data[0] = (uint8_t *) pixels;
info->dst_linesize[0] = stride;
sws_scale(info->sws_ctx, info->frame->data, info->frame->linesize, 0, info->frame->height, info->dst_data, info->dst_linesize);
} }
} else if (info->frame->format == AV_PIX_FMT_BGRA) {
libyuv::ABGRToARGB(info->frame->data[0], info->frame->linesize[0], (uint8_t *) pixels, info->frame->width * 4, info->frame->width, info->frame->height);
} }
AndroidBitmap_unlockPixels(env, bitmap); AndroidBitmap_unlockPixels(env, bitmap);
} }
} }
info->has_decoded_frames = true; info->has_decoded_frames = true;
av_frame_unref(info->frame); av_frame_unref(info->frame);
//LOGD("frame time %lld ms", ConnectionsManager::getInstance(0).getCurrentTimeMonotonicMillis() - time);
return 1; return 1;
} }
triesCount--; if (!info->has_decoded_frames) {
triesCount--;
}
} }
return 0; return 0;
} }
jint videoOnJNILoad(JavaVM *vm, JNIEnv *env) {
jclass_AnimatedFileDrawableStream = (jclass) env->NewGlobalRef(env->FindClass("org/telegram/messenger/AnimatedFileDrawableStream"));
if (jclass_AnimatedFileDrawableStream == 0) {
return JNI_FALSE;
}
jclass_AnimatedFileDrawableStream_read = env->GetMethodID(jclass_AnimatedFileDrawableStream, "read", "(II)I");
if (jclass_AnimatedFileDrawableStream_read == 0) {
return JNI_FALSE;
}
jclass_AnimatedFileDrawableStream_cancel = env->GetMethodID(jclass_AnimatedFileDrawableStream, "cancel", "()V");
if (jclass_AnimatedFileDrawableStream_cancel == 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}
} }

View File

@ -7,7 +7,7 @@
#include <android/bitmap.h> #include <android/bitmap.h>
#include <libwebp/webp/decode.h> #include <libwebp/webp/decode.h>
#include <libwebp/webp/encode.h> #include <libwebp/webp/encode.h>
#include "utils.h" #include "c_utils.h"
#include "image.h" #include "image.h"
jclass jclass_NullPointerException; jclass jclass_NullPointerException;
@ -28,34 +28,34 @@ jclass createGlobarRef(JNIEnv *env, jclass class) {
return 0; return 0;
} }
jint imageOnJNILoad(JavaVM *vm, void *reserved, JNIEnv *env) { jint imageOnJNILoad(JavaVM *vm, JNIEnv *env) {
jclass_NullPointerException = createGlobarRef(env, (*env)->FindClass(env, "java/lang/NullPointerException")); jclass_NullPointerException = createGlobarRef(env, (*env)->FindClass(env, "java/lang/NullPointerException"));
if (jclass_NullPointerException == 0) { if (jclass_NullPointerException == 0) {
return -1; return JNI_FALSE;
} }
jclass_RuntimeException = createGlobarRef(env, (*env)->FindClass(env, "java/lang/RuntimeException")); jclass_RuntimeException = createGlobarRef(env, (*env)->FindClass(env, "java/lang/RuntimeException"));
if (jclass_RuntimeException == 0) { if (jclass_RuntimeException == 0) {
return -1; return JNI_FALSE;
} }
jclass_Options = createGlobarRef(env, (*env)->FindClass(env, "android/graphics/BitmapFactory$Options")); jclass_Options = createGlobarRef(env, (*env)->FindClass(env, "android/graphics/BitmapFactory$Options"));
if (jclass_Options == 0) { if (jclass_Options == 0) {
return -1; return JNI_FALSE;
} }
jclass_Options_inJustDecodeBounds = (*env)->GetFieldID(env, jclass_Options, "inJustDecodeBounds", "Z"); jclass_Options_inJustDecodeBounds = (*env)->GetFieldID(env, jclass_Options, "inJustDecodeBounds", "Z");
if (jclass_Options_inJustDecodeBounds == 0) { if (jclass_Options_inJustDecodeBounds == 0) {
return -1; return JNI_FALSE;
} }
jclass_Options_outHeight = (*env)->GetFieldID(env, jclass_Options, "outHeight", "I"); jclass_Options_outHeight = (*env)->GetFieldID(env, jclass_Options, "outHeight", "I");
if (jclass_Options_outHeight == 0) { if (jclass_Options_outHeight == 0) {
return -1; return JNI_FALSE;
} }
jclass_Options_outWidth = (*env)->GetFieldID(env, jclass_Options, "outWidth", "I"); jclass_Options_outWidth = (*env)->GetFieldID(env, jclass_Options, "outWidth", "I");
if (jclass_Options_outWidth == 0) { if (jclass_Options_outWidth == 0) {
return -1; return JNI_FALSE;
} }
return JNI_VERSION_1_6; return JNI_TRUE;
} }
static inline uint64_t getColors(const uint8_t *p) { static inline uint64_t getColors(const uint8_t *p) {
@ -545,7 +545,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_calcCDT(JNIEnv *env, jclass
uint32_t totalSegments = PGPhotoEnhanceSegments * PGPhotoEnhanceSegments; uint32_t totalSegments = PGPhotoEnhanceSegments * PGPhotoEnhanceSegments;
uint32_t tileArea = (uint32_t) (floorf(imageWidth / PGPhotoEnhanceSegments) * floorf(imageHeight / PGPhotoEnhanceSegments)); uint32_t tileArea = (uint32_t) (floorf(imageWidth / PGPhotoEnhanceSegments) * floorf(imageHeight / PGPhotoEnhanceSegments));
uint32_t clipLimit = (uint32_t) max(1, _clipLimit * tileArea / (float) PGPhotoEnhanceHistogramBins); uint32_t clipLimit = (uint32_t) MAX(1, _clipLimit * tileArea / (float) PGPhotoEnhanceHistogramBins);
float scale = 255.0f / (float) tileArea; float scale = 255.0f / (float) tileArea;
unsigned char *bytes = (*env)->GetDirectBufferAddress(env, hsvBuffer); unsigned char *bytes = (*env)->GetDirectBufferAddress(env, hsvBuffer);
@ -609,7 +609,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_calcCDT(JNIEnv *env, jclass
uint32_t cdf = 0; uint32_t cdf = 0;
for (uint32_t j = hMin; j < PGPhotoEnhanceHistogramBins; ++j) { for (uint32_t j = hMin; j < PGPhotoEnhanceHistogramBins; ++j) {
cdf += cdfs[i][j]; cdf += cdfs[i][j];
cdfs[i][j] = (uint8_t) min(255, cdf * scale); cdfs[i][j] = (uint8_t) MIN(255, cdf * scale);
} }
cdfsMin[i] = cdfs[i][hMin]; cdfsMin[i] = cdfs[i][hMin];
@ -709,8 +709,6 @@ JNIEXPORT jboolean Java_org_telegram_messenger_Utilities_loadWebpImage(JNIEnv *e
} }
#define SQUARE(i) ((i)*(i)) #define SQUARE(i) ((i)*(i))
#define MAX(a, b) (a>b ? a : b)
#define MIN(a, b) (a>b ? b : a)
inline static void zeroClearInt(int* p, size_t count) { memset(p, 0, sizeof(int) * count); } inline static void zeroClearInt(int* p, size_t count) { memset(p, 0, sizeof(int) * count); }
JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env, jclass class, jobject bitmap, jint radius){ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env, jclass class, jobject bitmap, jint radius){
@ -724,6 +722,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env
int w=info.width; int w=info.width;
int h=info.height; int h=info.height;
int stride=info.stride;
unsigned char* pixels=0; unsigned char* pixels=0;
AndroidBitmap_lockPixels(env, bitmap, (void **) &pixels); AndroidBitmap_lockPixels(env, bitmap, (void **) &pixels);
@ -777,7 +776,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env
for(i = -radius;i <= radius;i++){ for(i = -radius;i <= radius;i++){
sir = &stack[(i + radius)*3]; sir = &stack[(i + radius)*3];
int offset = (yi + MIN(wm, MAX(i, 0)))*4; int offset = (y*stride + (MIN(wm, MAX(i, 0)))*4);
sir[0] = pixels[offset]; sir[0] = pixels[offset];
sir[1] = pixels[offset + 1]; sir[1] = pixels[offset + 1];
sir[2] = pixels[offset + 2]; sir[2] = pixels[offset + 2];
@ -818,7 +817,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env
vmin[x] = MIN(x + radius + 1, wm); vmin[x] = MIN(x + radius + 1, wm);
} }
int offset = (yw + vmin[x])*4; int offset = (y*stride + vmin[x]*4);
sir[0] = pixels[offset]; sir[0] = pixels[offset];
sir[1] = pixels[offset + 1]; sir[1] = pixels[offset + 1];
sir[2] = pixels[offset + 2]; sir[2] = pixels[offset + 2];
@ -878,10 +877,9 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env
yp += w; yp += w;
} }
} }
yi = x;
stackpointer = radius; stackpointer = radius;
for (y = 0;y < h;y++) { for (y = 0;y < h;y++) {
int offset = yi*4; int offset = stride*y+x*4;
pixels[offset] = dv[rsum]; pixels[offset] = dv[rsum];
pixels[offset + 1] = dv[gsum]; pixels[offset + 1] = dv[gsum];
pixels[offset + 2] = dv[bsum]; pixels[offset + 2] = dv[bsum];
@ -897,7 +895,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env
boutsum -= sir[2]; boutsum -= sir[2];
if (x == 0){ if (x == 0){
vmin[y] = MIN(y + r1, hm)*w; vmin[y] = (MIN(y + r1, hm))*w;
} }
p = x + vmin[y]; p = x + vmin[y];

View File

@ -3,6 +3,6 @@
#include <jni.h> #include <jni.h>
jint imageOnJNILoad(JavaVM *vm, void *reserved, JNIEnv *env); jint imageOnJNILoad(JavaVM *vm, JNIEnv *env);
#endif #endif

View File

@ -13,6 +13,7 @@
#include "libtgvoip/client/android/tg_voip_jni.h" #include "libtgvoip/client/android/tg_voip_jni.h"
int registerNativeTgNetFunctions(JavaVM *vm, JNIEnv *env); int registerNativeTgNetFunctions(JavaVM *vm, JNIEnv *env);
int videoOnJNILoad(JavaVM *vm, JNIEnv *env);
jint JNI_OnLoad(JavaVM *vm, void *reserved) { jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = 0; JNIEnv *env = 0;
@ -22,7 +23,11 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
return -1; return -1;
} }
if (imageOnJNILoad(vm, reserved, env) == -1) { if (imageOnJNILoad(vm, env) != JNI_TRUE) {
return -1;
}
if (videoOnJNILoad(vm, env) != JNI_TRUE) {
return -1; return -1;
} }
@ -163,19 +168,6 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_aesCbcEncryption(JNIEnv *en
(*env)->ReleaseByteArrayElements(env, iv, ivBuff, JNI_ABORT); (*env)->ReleaseByteArrayElements(env, iv, ivBuff, JNI_ABORT);
} }
JNIEXPORT jstring Java_org_telegram_messenger_Utilities_readlink(JNIEnv *env, jclass class, jstring path) {
static char buf[PATH_MAX + 1];
const char *fileName = (*env)->GetStringUTFChars(env, path, NULL);
ssize_t result = readlink(fileName, buf, PATH_MAX);
jstring value = 0;
if (result != -1) {
buf[result] = '\0';
value = (*env)->NewStringUTF(env, buf);
}
(*env)->ReleaseStringUTFChars(env, path, fileName);
return value;
}
int64_t listdir(const char *fileName, int32_t mode, int32_t docType, int64_t time) { int64_t listdir(const char *fileName, int32_t mode, int32_t docType, int64_t time) {
int64_t value = 0; int64_t value = 0;
DIR *dir; DIR *dir;
@ -209,7 +201,7 @@ int64_t listdir(const char *fileName, int32_t mode, int32_t docType, int64_t tim
} else { } else {
stat(buff, &attrib); stat(buff, &attrib);
if (mode == 0) { if (mode == 0) {
value += attrib.st_size; value += 512 * attrib.st_blocks;
} else if (mode == 1) { } else if (mode == 1) {
if (attrib.st_atim.tv_sec != 0) { if (attrib.st_atim.tv_sec != 0) {
if (attrib.st_atim.tv_sec < time) { if (attrib.st_atim.tv_sec < time) {

@ -1 +1 @@
Subproject commit fc13464b35aeb373844bafc7a9a33b18ca7afa3b Subproject commit ce74c9216f599874571061f39c2dc31632b3004b

File diff suppressed because it is too large Load Diff

View File

@ -123,9 +123,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()]. ** [sqlite_version()] and [sqlite_source_id()].
*/ */
#define SQLITE_VERSION "3.26.0" #define SQLITE_VERSION "3.27.2"
#define SQLITE_VERSION_NUMBER 3026000 #define SQLITE_VERSION_NUMBER 3027002
#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9" #define SQLITE_SOURCE_ID "2019-02-25 16:06:06 bd49a8271d650fa89e446b42e513b595a717b9212c91dd384aab871fc1d0f6d7"
/* /*
** CAPI3REF: Run-Time Library Version Numbers ** CAPI3REF: Run-Time Library Version Numbers
@ -823,6 +823,15 @@ struct sqlite3_io_methods {
** file space based on this hint in order to help writes to the database ** file space based on this hint in order to help writes to the database
** file run faster. ** file run faster.
** **
** <li>[[SQLITE_FCNTL_SIZE_LIMIT]]
** The [SQLITE_FCNTL_SIZE_LIMIT] opcode is used by in-memory VFS that
** implements [sqlite3_deserialize()] to set an upper bound on the size
** of the in-memory database. The argument is a pointer to a [sqlite3_int64].
** If the integer pointed to is negative, then it is filled in with the
** current limit. Otherwise the limit is set to the larger of the value
** of the integer pointed to and the current database size. The integer
** pointed to is set to the new limit.
**
** <li>[[SQLITE_FCNTL_CHUNK_SIZE]] ** <li>[[SQLITE_FCNTL_CHUNK_SIZE]]
** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS ** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS
** extends and truncates the database file in chunks of a size specified ** extends and truncates the database file in chunks of a size specified
@ -1131,6 +1140,7 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33
#define SQLITE_FCNTL_LOCK_TIMEOUT 34 #define SQLITE_FCNTL_LOCK_TIMEOUT 34
#define SQLITE_FCNTL_DATA_VERSION 35 #define SQLITE_FCNTL_DATA_VERSION 35
#define SQLITE_FCNTL_SIZE_LIMIT 36
/* deprecated names */ /* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
@ -1972,6 +1982,17 @@ struct sqlite3_mem_methods {
** negative value for this option restores the default behaviour. ** negative value for this option restores the default behaviour.
** This option is only available if SQLite is compiled with the ** This option is only available if SQLite is compiled with the
** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option. ** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option.
**
** [[SQLITE_CONFIG_MEMDB_MAXSIZE]]
** <dt>SQLITE_CONFIG_MEMDB_MAXSIZE
** <dd>The SQLITE_CONFIG_MEMDB_MAXSIZE option accepts a single parameter
** [sqlite3_int64] parameter which is the default maximum size for an in-memory
** database created using [sqlite3_deserialize()]. This default maximum
** size can be adjusted up or down for individual databases using the
** [SQLITE_FCNTL_SIZE_LIMIT] [sqlite3_file_control|file-control]. If this
** configuration setting is never used, then the default maximum is determined
** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that
** compile-time option is not set, then the default maximum is 1073741824.
** </dl> ** </dl>
*/ */
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
@ -2002,6 +2023,7 @@ struct sqlite3_mem_methods {
#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */
/* /*
** CAPI3REF: Database Connection Configuration Options ** CAPI3REF: Database Connection Configuration Options
@ -2347,7 +2369,7 @@ SQLITE_API int sqlite3_changes(sqlite3*);
** not. ^Changes to a view that are intercepted by INSTEAD OF triggers ** not. ^Changes to a view that are intercepted by INSTEAD OF triggers
** are not counted. ** are not counted.
** **
** This the [sqlite3_total_changes(D)] interface only reports the number ** The [sqlite3_total_changes(D)] interface only reports the number
** of rows that changed due to SQL statement run against database ** of rows that changed due to SQL statement run against database
** connection D. Any changes by other database connections are ignored. ** connection D. Any changes by other database connections are ignored.
** To detect changes against a database file from other database ** To detect changes against a database file from other database
@ -2991,9 +3013,9 @@ SQLITE_API int sqlite3_set_authorizer(
** time is in units of nanoseconds, however the current implementation ** time is in units of nanoseconds, however the current implementation
** is only capable of millisecond resolution so the six least significant ** is only capable of millisecond resolution so the six least significant
** digits in the time are meaningless. Future versions of SQLite ** digits in the time are meaningless. Future versions of SQLite
** might provide greater resolution on the profiler callback. The ** might provide greater resolution on the profiler callback. Invoking
** sqlite3_profile() function is considered experimental and is ** either [sqlite3_trace()] or [sqlite3_trace_v2()] will cancel the
** subject to change in future versions of SQLite. ** profile callback.
*/ */
SQLITE_API SQLITE_DEPRECATED void *sqlite3_trace(sqlite3*, SQLITE_API SQLITE_DEPRECATED void *sqlite3_trace(sqlite3*,
void(*xTrace)(void*,const char*), void*); void(*xTrace)(void*,const char*), void*);
@ -3407,6 +3429,8 @@ SQLITE_API int sqlite3_open_v2(
** is not a database file pathname pointer that SQLite passed into the xOpen ** is not a database file pathname pointer that SQLite passed into the xOpen
** VFS method, then the behavior of this routine is undefined and probably ** VFS method, then the behavior of this routine is undefined and probably
** undesirable. ** undesirable.
**
** See the [URI filename] documentation for additional information.
*/ */
SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam);
SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault);
@ -3629,18 +3653,23 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** deplete the limited store of lookaside memory. Future versions of ** deplete the limited store of lookaside memory. Future versions of
** SQLite may act on this hint differently. ** SQLite may act on this hint differently.
** **
** [[SQLITE_PREPARE_NORMALIZE]] ^(<dt>SQLITE_PREPARE_NORMALIZE</dt> ** [[SQLITE_PREPARE_NORMALIZE]] <dt>SQLITE_PREPARE_NORMALIZE</dt>
** <dd>The SQLITE_PREPARE_NORMALIZE flag indicates that a normalized ** <dd>The SQLITE_PREPARE_NORMALIZE flag is a no-op. This flag used
** representation of the SQL statement should be calculated and then ** to be required for any prepared statement that wanted to use the
** associated with the prepared statement, which can be obtained via ** [sqlite3_normalized_sql()] interface. However, the
** the [sqlite3_normalized_sql()] interface.)^ The semantics used to ** [sqlite3_normalized_sql()] interface is now available to all
** normalize a SQL statement are unspecified and subject to change. ** prepared statements, regardless of whether or not they use this
** At a minimum, literal values will be replaced with suitable ** flag.
** placeholders. **
** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt>
** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
** to return an error (error code SQLITE_ERROR) if the statement uses
** any virtual tables.
** </dl> ** </dl>
*/ */
#define SQLITE_PREPARE_PERSISTENT 0x01 #define SQLITE_PREPARE_PERSISTENT 0x01
#define SQLITE_PREPARE_NORMALIZE 0x02 #define SQLITE_PREPARE_NORMALIZE 0x02
#define SQLITE_PREPARE_NO_VTAB 0x04
/* /*
** CAPI3REF: Compiling An SQL Statement ** CAPI3REF: Compiling An SQL Statement
@ -9996,7 +10025,7 @@ SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *pIter);
** sqlite3changeset_next() is called on the iterator or until the ** sqlite3changeset_next() is called on the iterator or until the
** conflict-handler function returns. If pnCol is not NULL, then *pnCol is ** conflict-handler function returns. If pnCol is not NULL, then *pnCol is
** set to the number of columns in the table affected by the change. If ** set to the number of columns in the table affected by the change. If
** pbIncorrect is not NULL, then *pbIndirect is set to true (1) if the change ** pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change
** is an indirect change, or false (0) otherwise. See the documentation for ** is an indirect change, or false (0) otherwise. See the documentation for
** [sqlite3session_indirect()] for a description of direct and indirect ** [sqlite3session_indirect()] for a description of direct and indirect
** changes. Finally, if pOp is not NULL, then *pOp is set to one of ** changes. Finally, if pOp is not NULL, then *pOp is set to one of
@ -11230,12 +11259,8 @@ struct Fts5PhraseIter {
** **
** Usually, output parameter *piPhrase is set to the phrase number, *piCol ** Usually, output parameter *piPhrase is set to the phrase number, *piCol
** to the column in which it occurs and *piOff the token offset of the ** to the column in which it occurs and *piOff the token offset of the
** first token of the phrase. The exception is if the table was created ** first token of the phrase. Returns SQLITE_OK if successful, or an error
** with the offsets=0 option specified. In this case *piOff is always ** code (i.e. SQLITE_NOMEM) if an error occurs.
** set to -1.
**
** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM)
** if an error occurs.
** **
** This API can be quite slow if used with an FTS5 table created with the ** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" or "detail=column" option. ** "detail=none" or "detail=column" option.
@ -11524,11 +11549,11 @@ struct Fts5ExtensionApi {
** the tokenizer substitutes "first" for "1st" and the query works ** the tokenizer substitutes "first" for "1st" and the query works
** as expected. ** as expected.
** **
** <li> By adding multiple synonyms for a single term to the FTS index. ** <li> By querying the index for all synonyms of each query term
** In this case, when tokenizing query text, the tokenizer may ** separately. In this case, when tokenizing query text, the
** provide multiple synonyms for a single term within the document. ** tokenizer may provide multiple synonyms for a single term
** FTS5 then queries the index for each synonym individually. For ** within the document. FTS5 then queries the index for each
** example, faced with the query: ** synonym individually. For example, faced with the query:
** **
** <codeblock> ** <codeblock>
** ... MATCH 'first place'</codeblock> ** ... MATCH 'first place'</codeblock>
@ -11552,7 +11577,7 @@ struct Fts5ExtensionApi {
** "place". ** "place".
** **
** This way, even if the tokenizer does not provide synonyms ** This way, even if the tokenizer does not provide synonyms
** when tokenizing query text (it should not - to do would be ** when tokenizing query text (it should not - to do so would be
** inefficient), it doesn't matter if the user queries for ** inefficient), it doesn't matter if the user queries for
** 'first + place' or '1st + place', as there are entries in the ** 'first + place' or '1st + place', as there are entries in the
** FTS index corresponding to both forms of the first token. ** FTS index corresponding to both forms of the first token.

View File

@ -0,0 +1,18 @@
#include <jni.h>
#include <sys/stat.h>
#include <climits>
#include <unistd.h>
thread_local static char buf[PATH_MAX + 1];
extern "C" JNIEXPORT jstring Java_org_telegram_messenger_Utilities_readlink(JNIEnv *env, jclass clazz, jstring path) {
const char *fileName = env->GetStringUTFChars(path, NULL);
ssize_t result = readlink(fileName, buf, PATH_MAX);
jstring value = 0;
if (result != -1) {
buf[result] = '\0';
value = env->NewStringUTF(buf);
}
env->ReleaseStringUTFChars(path, fileName);
return value;
}

View File

@ -1,14 +0,0 @@
#include "utils.h"
void throwException(JNIEnv *env, char *format, ...) {
jclass exClass = (*env)->FindClass(env, "java/lang/UnsupportedOperationException");
if (!exClass) {
return;
}
char dest[256];
va_list argptr;
va_start(argptr, format);
vsprintf(dest, format, argptr);
va_end(argptr);
(*env)->ThrowNew(env, exClass, dest);
}

View File

@ -67,7 +67,7 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="replace" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="replace" />
<application <application
android:name=".ApplicationLoader" android:name="org.telegram.messenger.${applicationClassName}"
android:allowBackup="false" android:allowBackup="false"
android:hardwareAccelerated="@bool/useHardwareAcceleration" android:hardwareAccelerated="@bool/useHardwareAcceleration"
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_launcher"
@ -364,6 +364,11 @@
android:exported="false" android:exported="false"
android:grantUriPermissions="true"/> android:grantUriPermissions="true"/>
<provider
android:authorities="${applicationId}.call_sound_provider"
android:name=".voip.CallNotificationSoundProvider"
android:exported="true"/>
<uses-library android:name="com.sec.android.app.multiwindow" android:required="false" /> <uses-library android:name="com.sec.android.app.multiwindow" android:required="false" />
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" /> <meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
<meta-data android:name="com.sec.android.multiwindow.DEFAULT_SIZE_W" android:value="632dp" /> <meta-data android:name="com.sec.android.multiwindow.DEFAULT_SIZE_W" android:value="632dp" />

View File

@ -1,5 +1,6 @@
chat_unreadMessagesStartText=-620756993 chat_unreadMessagesStartText=-620756993
chat_inFileBackgroundSelected=-13347218 chat_inFileBackgroundSelected=-13347218
windowBackgroundChecked=-11107667
chat_editDoneIcon=-9456666 chat_editDoneIcon=-9456666
radioBackgroundChecked=-10636048 radioBackgroundChecked=-10636048
dialogTextBlue=-9456666 dialogTextBlue=-9456666
@ -23,6 +24,7 @@ switchTrack=-11314335
chat_inPreviewInstantSelectedText=-11099429 chat_inPreviewInstantSelectedText=-11099429
chat_attachAudioBackground=-1012649 chat_attachAudioBackground=-1012649
actionBarDefaultSubmenuBackground=-14144465 actionBarDefaultSubmenuBackground=-14144465
switchTrackBlueThumb=-14473945
avatar_nameInMessageViolet=-5925398 avatar_nameInMessageViolet=-5925398
emptyListPlaceholder=-11247768 emptyListPlaceholder=-11247768
chat_inAudioSelectedProgress=-14925469 chat_inAudioSelectedProgress=-14925469
@ -78,6 +80,7 @@ windowBackgroundWhiteBlueHeader=-9456666
files_folderIconBackground=-13947340 files_folderIconBackground=-13947340
chat_messagePanelVoiceBackground=-11495209 chat_messagePanelVoiceBackground=-11495209
passport_authorizeBackgroundSelected=-9590295 passport_authorizeBackgroundSelected=-9590295
switchTrackBlueChecked=-5908226
sharedMedia_linkPlaceholderText=-11117472 sharedMedia_linkPlaceholderText=-11117472
player_seekBarBackground=1196577362 player_seekBarBackground=1196577362
divider=384431615 divider=384431615
@ -153,6 +156,7 @@ chat_inVenueInfoSelectedText=-8085320
dialogTextBlue2=-9456666 dialogTextBlue2=-9456666
avatar_backgroundGroupCreateSpanBlue=-12751207 avatar_backgroundGroupCreateSpanBlue=-12751207
dialogTextBlue3=-9456666 dialogTextBlue3=-9456666
switchTrackBlueThumbChecked=-11107667
dialogTextBlue4=-9456666 dialogTextBlue4=-9456666
windowBackgroundWhiteGreenText=-10101914 windowBackgroundWhiteGreenText=-10101914
chat_emojiPanelIcon=-10458511 chat_emojiPanelIcon=-10458511
@ -270,6 +274,7 @@ avatar_nameInMessageBlue=-9456666
dialogTextBlack=-1 dialogTextBlack=-1
actionBarDefault=-14276309 actionBarDefault=-14276309
profile_actionIcon=-1 profile_actionIcon=-1
windowBackgroundUnchecked=-14473945
actionBarDefaultSelector=-11972268 actionBarDefaultSelector=-11972268
chats_menuTopShadow=-15724528 chats_menuTopShadow=-15724528
chat_outAudioPerfomerText=-6965025 chat_outAudioPerfomerText=-6965025
@ -279,10 +284,12 @@ chat_inVenueInfoText=-8351328
chat_replyPanelIcons=-9456666 chat_replyPanelIcons=-9456666
fastScrollInactive=-12103850 fastScrollInactive=-12103850
chat_outSentClockSelected=-6764038 chat_outSentClockSelected=-6764038
switchTrackBlueSelectorChecked=848091135
musicPicker_checkbox=-11621658 musicPicker_checkbox=-11621658
chat_outFileBackground=-11829594 chat_outFileBackground=-11829594
chats_name=-1 chats_name=-1
chat_attachSendBackground=-10242065 chat_attachSendBackground=-10242065
switchTrackBlueSelector=431611386
dialogBadgeBackground=-10371847 dialogBadgeBackground=-10371847
chat_outBubbleSelected=-12487769 chat_outBubbleSelected=-12487769
avatar_backgroundInProfileBlue=-11232035 avatar_backgroundInProfileBlue=-11232035

View File

@ -1,5 +1,6 @@
chat_unreadMessagesStartText=-620756993 chat_unreadMessagesStartText=-620756993
chat_inFileBackgroundSelected=-13218977 chat_inFileBackgroundSelected=-13218977
windowBackgroundChecked=-11632213
chat_editDoneIcon=-10177041 chat_editDoneIcon=-10177041
radioBackgroundChecked=-10177041 radioBackgroundChecked=-10177041
dialogTextBlue=-10177041 dialogTextBlue=-10177041
@ -22,6 +23,7 @@ chat_inPreviewInstantSelectedText=-5648402
chat_attachAudioBackground=-619421 chat_attachAudioBackground=-619421
location_sendLocationBackground=-9919529 location_sendLocationBackground=-9919529
actionBarDefaultSubmenuBackground=-13878451 actionBarDefaultSubmenuBackground=-13878451
switchTrackBlueThumb=-14866637
avatar_nameInMessageViolet=-6643205 avatar_nameInMessageViolet=-6643205
emptyListPlaceholder=-8549479 emptyListPlaceholder=-8549479
chat_inAudioSelectedProgress=-1 chat_inAudioSelectedProgress=-1
@ -74,6 +76,7 @@ chat_recordTime=-1
windowBackgroundWhiteBlueHeader=-8796932 windowBackgroundWhiteBlueHeader=-8796932
files_folderIconBackground=-13286315 files_folderIconBackground=-13286315
passport_authorizeBackgroundSelected=-11627561 passport_authorizeBackgroundSelected=-11627561
switchTrackBlueChecked=-8333825
player_seekBarBackground=1196577362 player_seekBarBackground=1196577362
groupcreate_onlineText=-10177041 groupcreate_onlineText=-10177041
divider=-1795162112 divider=-1795162112
@ -148,6 +151,7 @@ chat_inVenueInfoSelectedText=-7490861
dialogTextBlue2=-10177041 dialogTextBlue2=-10177041
avatar_backgroundGroupCreateSpanBlue=-13803892 avatar_backgroundGroupCreateSpanBlue=-13803892
dialogTextBlue3=-10177041 dialogTextBlue3=-10177041
switchTrackBlueThumbChecked=-11632213
dialogTextBlue4=-10177041 dialogTextBlue4=-10177041
windowBackgroundWhiteGreenText=-10371737 windowBackgroundWhiteGreenText=-10371737
chat_emojiPanelIcon=-9996665 chat_emojiPanelIcon=-9996665
@ -183,6 +187,7 @@ chat_outContactPhoneText=-7357217
chat_inAudioTitleText=-8796932 chat_inAudioTitleText=-8796932
chat_messageLinkIn=-8796932 chat_messageLinkIn=-8796932
chats_menuBackground=-14866637 chats_menuBackground=-14866637
windowBackgroundCheckText=-1
chat_serviceBackground=-2110438831 chat_serviceBackground=-2110438831
chats_secretIcon=-9316522 chats_secretIcon=-9316522
chat_inFileBackground=-13417903 chat_inFileBackground=-13417903
@ -236,6 +241,7 @@ checkboxSquareBackground=-10177051
chat_mediaSentClock=-1291845633 chat_mediaSentClock=-1291845633
files_folderIcon=-8220513 files_folderIcon=-8220513
chats_menuCloudBackgroundCats=-11232035 chats_menuCloudBackgroundCats=-11232035
switchTrackBlue=-10984850
chat_topPanelClose=-10590606 chat_topPanelClose=-10590606
profile_adminIcon=-8549479 profile_adminIcon=-8549479
chats_verifiedBackground=-10177041 chats_verifiedBackground=-10177041
@ -257,6 +263,7 @@ dialogTextBlack=-1
actionBarDefault=-14602949 actionBarDefault=-14602949
location_placeLocationBackground=-9919529 location_placeLocationBackground=-9919529
profile_actionIcon=-1 profile_actionIcon=-1
windowBackgroundUnchecked=-14866637
actionBarDefaultSelector=-13483693 actionBarDefaultSelector=-13483693
chats_menuTopShadow=-1542185690 chats_menuTopShadow=-1542185690
chat_outAudioPerfomerText=-7357217 chat_outAudioPerfomerText=-7357217
@ -270,8 +277,10 @@ fastScrollInactive=-12760487
chat_outSentClockSelected=-4268038 chat_outSentClockSelected=-4268038
featuredStickers_addedIcon=-10177041 featuredStickers_addedIcon=-10177041
chat_goDownButtonCounter=-14866637 chat_goDownButtonCounter=-14866637
switchTrackBlueSelectorChecked=848091135
chat_outFileBackground=-11175517 chat_outFileBackground=-11175517
chats_name=-1446156 chats_name=-1446156
switchTrackBlueSelector=431611386
dialogBadgeBackground=-10177041 dialogBadgeBackground=-10177041
chat_outBubbleSelected=-11829841 chat_outBubbleSelected=-11829841
avatar_backgroundInProfileBlue=-11232035 avatar_backgroundInProfileBlue=-11232035

View File

@ -2,3 +2,4 @@ change SimpleExoPlayer.java.java
change VideoListener.java change VideoListener.java
change AspectRatioFrameLayout.java change AspectRatioFrameLayout.java
change DefaultExtractorsFactory.java change DefaultExtractorsFactory.java
change MP4Extractor.java - MAXIMUM_READ_AHEAD_BYTES_STREAM to 1MB

View File

@ -394,7 +394,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
} }
@Override @Override
public void release() { public void release(boolean async) {
Log.i(TAG, "Release " + Integer.toHexString(System.identityHashCode(this)) + " [" Log.i(TAG, "Release " + Integer.toHexString(System.identityHashCode(this)) + " ["
+ ExoPlayerLibraryInfo.VERSION_SLASHY + "] [" + Util.DEVICE_DEBUG_INFO + "] [" + ExoPlayerLibraryInfo.VERSION_SLASHY + "] [" + Util.DEVICE_DEBUG_INFO + "] ["
+ ExoPlayerLibraryInfo.registeredModules() + "]"); + ExoPlayerLibraryInfo.registeredModules() + "]");

View File

@ -921,8 +921,8 @@ public final class Format implements Parcelable {
this.height = height; this.height = height;
this.frameRate = frameRate; this.frameRate = frameRate;
this.rotationDegrees = rotationDegrees == Format.NO_VALUE ? 0 : rotationDegrees; this.rotationDegrees = rotationDegrees == Format.NO_VALUE ? 0 : rotationDegrees;
this.pixelWidthHeightRatio = this.pixelWidthHeightRatio = 1;
pixelWidthHeightRatio == Format.NO_VALUE ? 1 : pixelWidthHeightRatio; //pixelWidthHeightRatio == Format.NO_VALUE ? 1 : pixelWidthHeightRatio; TODO remove after ffmpeg support
this.projectionData = projectionData; this.projectionData = projectionData;
this.stereoMode = stereoMode; this.stereoMode = stereoMode;
this.colorInfo = colorInfo; this.colorInfo = colorInfo;

View File

@ -760,7 +760,7 @@ public interface Player {
* Releases the player. This method must be called when the player is no longer required. The * Releases the player. This method must be called when the player is no longer required. The
* player must not be used after calling this method. * player must not be used after calling this method.
*/ */
void release(); void release(boolean async);
/** /**
* Returns the number of renderers. * Returns the number of renderers.

View File

@ -54,6 +54,9 @@ import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.VideoFrameMetadataListener; import com.google.android.exoplayer2.video.VideoFrameMetadataListener;
import com.google.android.exoplayer2.video.VideoRendererEventListener; import com.google.android.exoplayer2.video.VideoRendererEventListener;
import com.google.android.exoplayer2.video.spherical.CameraMotionListener; import com.google.android.exoplayer2.video.spherical.CameraMotionListener;
import org.telegram.messenger.Utilities;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -986,9 +989,13 @@ public class SimpleExoPlayer extends BasePlayer
} }
@Override @Override
public void release() { public void release(boolean async) {
audioFocusManager.handleStop(); audioFocusManager.handleStop();
player.release(); if (async) {
Utilities.globalQueue.postRunnable(() -> player.release(async));
} else {
player.release(async);
}
removeSurfaceCallbacks(); removeSurfaceCallbacks();
if (surface != null) { if (surface != null) {
if (ownsSurface) { if (ownsSurface) {

View File

@ -88,7 +88,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
* For poorly interleaved streams, the maximum byte difference one track is allowed to be read * For poorly interleaved streams, the maximum byte difference one track is allowed to be read
* ahead before the source will be reloaded at a new position to read another track. * ahead before the source will be reloaded at a new position to read another track.
*/ */
private static final long MAXIMUM_READ_AHEAD_BYTES_STREAM = 10 * 1024 * 1024; private static final long MAXIMUM_READ_AHEAD_BYTES_STREAM = 1024 * 1024;
private final @Flags int flags; private final @Flags int flags;

View File

@ -356,7 +356,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
} else if (isPendingReset()) { } else if (isPendingReset()) {
return pendingResetPositionUs; return pendingResetPositionUs;
} }
long largestQueuedTimestampUs = C.TIME_UNSET; long largestQueuedTimestampUs = Long.MAX_VALUE;
if (haveAudioVideoTracks) { if (haveAudioVideoTracks) {
// Ignore non-AV tracks, which may be sparse or poorly interleaved. // Ignore non-AV tracks, which may be sparse or poorly interleaved.
largestQueuedTimestampUs = Long.MAX_VALUE; largestQueuedTimestampUs = Long.MAX_VALUE;
@ -368,7 +368,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
} }
} }
} }
if (largestQueuedTimestampUs == C.TIME_UNSET) { if (largestQueuedTimestampUs == Long.MAX_VALUE) {
largestQueuedTimestampUs = getLargestQueuedTimestampUs(); largestQueuedTimestampUs = getLargestQueuedTimestampUs();
} }
return largestQueuedTimestampUs == Long.MIN_VALUE ? lastSeekPositionUs return largestQueuedTimestampUs == Long.MIN_VALUE ? lastSeekPositionUs

View File

@ -210,13 +210,17 @@ public class AspectRatioFrameLayout extends FrameLayout {
} }
break; break;
case RESIZE_MODE_FILL: case RESIZE_MODE_FILL:
if (aspectDeformation <= 0) {
height = (int) (width / videoAspectRatio);
} else {
width = (int) (height * videoAspectRatio);
}
default: default:
// Ignore target aspect ratio // Ignore target aspect ratio
break; break;
} }
aspectRatioUpdateDispatcher.scheduleUpdate(videoAspectRatio, viewAspectRatio, true); aspectRatioUpdateDispatcher.scheduleUpdate(videoAspectRatio, viewAspectRatio, true);
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
int count = getChildCount(); int count = getChildCount();
for (int a = 0; a < count; a++) { for (int a = 0; a < count; a++) {

View File

@ -1853,14 +1853,33 @@ public class AndroidUtilities {
} }
public static String formatFileSize(long size) { public static String formatFileSize(long size) {
return formatFileSize(size, false);
}
public static String formatFileSize(long size, boolean removeZero) {
if (size < 1024) { if (size < 1024) {
return String.format("%d B", size); return String.format("%d B", size);
} else if (size < 1024 * 1024) { } else if (size < 1024 * 1024) {
return String.format("%.1f KB", size / 1024.0f); float value = size / 1024.0f;
if (removeZero && (value - (int) value) * 10 == 0) {
return String.format("%d KB", (int) value);
} else {
return String.format("%.1f KB", value);
}
} else if (size < 1024 * 1024 * 1024) { } else if (size < 1024 * 1024 * 1024) {
return String.format("%.1f MB", size / 1024.0f / 1024.0f); float value = size / 1024.0f / 1024.0f;
if (removeZero && (value - (int) value) * 10 == 0) {
return String.format("%d MB", (int) value);
} else {
return String.format("%.1f MB", value);
}
} else { } else {
return String.format("%.1f GB", size / 1024.0f / 1024.0f / 1024.0f); float value = size / 1024.0f / 1024.0f / 1024.0f;
if (removeZero && (value - (int) value) * 10 == 0) {
return String.format("%d GB", (int) value);
} else {
return String.format("%.1f GB", value);
}
} }
} }
@ -2132,9 +2151,10 @@ public class AndroidUtilities {
return rights == null || Math.abs(rights.until_date - System.currentTimeMillis() / 1000) > 5 * 365 * 24 * 60 * 60; return rights == null || Math.abs(rights.until_date - System.currentTimeMillis() / 1000) > 5 * 365 * 24 * 60 * 60;
} }
public static void setRectToRect(Matrix matrix, RectF src, RectF dst, int rotation, Matrix.ScaleToFit align) { public static void setRectToRect(Matrix matrix, RectF src, RectF dst, int rotation, boolean translate) {
float tx, sx; float tx, sx;
float ty, sy; float ty, sy;
boolean xLarger = false;
if (rotation == 90 || rotation == 270) { if (rotation == 90 || rotation == 270) {
sx = dst.height() / src.width(); sx = dst.height() / src.width();
sy = dst.width() / src.height(); sy = dst.width() / src.height();
@ -2142,17 +2162,16 @@ public class AndroidUtilities {
sx = dst.width() / src.width(); sx = dst.width() / src.width();
sy = dst.height() / src.height(); sy = dst.height() / src.height();
} }
if (align != Matrix.ScaleToFit.FILL) { if (sx < sy) {
if (sx > sy) { sx = sy;
sx = sy; xLarger = true;
} else { } else {
sy = sx; sy = sx;
}
} }
tx = -src.left * sx;
ty = -src.top * sy;
matrix.setTranslate(dst.left, dst.top); if (translate) {
matrix.setTranslate(dst.left, dst.top);
}
if (rotation == 90) { if (rotation == 90) {
matrix.preRotate(90); matrix.preRotate(90);
matrix.preTranslate(0, -dst.width()); matrix.preTranslate(0, -dst.width());
@ -2164,8 +2183,31 @@ public class AndroidUtilities {
matrix.preTranslate(-dst.height(), 0); matrix.preTranslate(-dst.height(), 0);
} }
if (translate) {
tx = -src.left * sx;
ty = -src.top * sy;
} else {
tx = dst.left - src.left * sx;
ty = dst.top - src.top * sy;
}
float diff;
if (xLarger) {
diff = dst.width() - src.width() * sy;
} else {
diff = dst.height() - src.height() * sy;
}
diff = diff / 2.0f;
if (xLarger) {
tx += diff;
} else {
ty += diff;
}
matrix.preScale(sx, sy); matrix.preScale(sx, sy);
matrix.preTranslate(tx, ty); if (translate) {
matrix.preTranslate(tx, ty);
}
} }
public static boolean handleProxyIntent(Activity activity, Intent intent) { public static boolean handleProxyIntent(Activity activity, Intent intent) {
@ -2538,4 +2580,10 @@ public class AndroidUtilities {
} }
return link; return link;
} }
public static float distanceInfluenceForSnapDuration(float f) {
f -= 0.5F;
f *= 0.47123894F;
return (float) Math.sin((double) f);
}
} }

View File

@ -0,0 +1,100 @@
package org.telegram.messenger;
import org.telegram.tgnet.TLRPC;
import java.util.concurrent.CountDownLatch;
public class AnimatedFileDrawableStream implements FileLoadOperationStream {
private FileLoadOperation loadOperation;
private CountDownLatch countDownLatch;
private TLRPC.Document document;
private Object parentObject;
private int currentAccount;
private volatile boolean canceled;
private final Object sync = new Object();
private int lastOffset;
public AnimatedFileDrawableStream(TLRPC.Document d, Object p, int a) {
document = d;
parentObject = p;
currentAccount = a;
loadOperation = FileLoader.getInstance(currentAccount).loadStreamFile(this, document, parentObject, 0);
}
public int read(int offset, int readLength) {
synchronized (sync) {
if (canceled) {
return 0;
}
}
if (readLength == 0) {
return 0;
} else {
int availableLength = 0;
try {
while (availableLength == 0) {
availableLength = loadOperation.getDownloadedLengthFromOffset(offset, readLength);
if (availableLength == 0) {
if (loadOperation.isPaused() || lastOffset != offset) {
FileLoader.getInstance(currentAccount).loadStreamFile(this, document, parentObject, offset);
}
synchronized (sync) {
if (canceled) {
return 0;
}
countDownLatch = new CountDownLatch(1);
}
FileLoader.getInstance(currentAccount).setLoadingVideo(document, false, true);
countDownLatch.await();
}
}
lastOffset = offset + availableLength;
} catch (Exception e) {
FileLog.e(e);
}
return availableLength;
}
}
public void cancel() {
cancel(true);
}
public void cancel(boolean removeLoading) {
synchronized (sync) {
if (countDownLatch != null) {
countDownLatch.countDown();
if (removeLoading && !canceled) {
FileLoader.getInstance(currentAccount).removeLoadingVideo(document, false, true);
}
}
canceled = true;
}
}
public void reset() {
synchronized (sync) {
canceled = false;
}
}
public TLRPC.Document getDocument() {
return document;
}
public Object getParentObject() {
return document;
}
public int getCurrentAccount() {
return currentAccount;
}
@Override
public void newDataAvailable() {
if (countDownLatch != null) {
countDownLatch.countDown();
}
}
}

View File

@ -89,7 +89,12 @@ public class ApplicationLoader extends Application {
BroadcastReceiver networkStateReceiver = new BroadcastReceiver() { BroadcastReceiver networkStateReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
currentNetworkInfo = connectivityManager.getActiveNetworkInfo(); try {
currentNetworkInfo = connectivityManager.getActiveNetworkInfo();
} catch (Throwable ignore) {
}
boolean isSlow = isConnectionSlow(); boolean isSlow = isConnectionSlow();
for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) { for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) {
ConnectionsManager.getInstance(a).checkConnection(); ConnectionsManager.getInstance(a).checkConnection();
@ -151,11 +156,24 @@ public class ApplicationLoader extends Application {
WearDataLayerListenerService.updateWatchConnectionState(); WearDataLayerListenerService.updateWatchConnectionState();
} }
public ApplicationLoader() {
super();
}
@Override @Override
public void onCreate() { public void onCreate() {
try {
applicationContext = getApplicationContext();
} catch (Throwable ignore) {
}
super.onCreate(); super.onCreate();
applicationContext = getApplicationContext(); if (applicationContext == null) {
applicationContext = getApplicationContext();
}
NativeLoader.initNativeLibs(ApplicationLoader.applicationContext); NativeLoader.initNativeLibs(ApplicationLoader.applicationContext);
ConnectionsManager.native_setJava(false); ConnectionsManager.native_setJava(false);
new ForegroundDetector(this); new ForegroundDetector(this);

View File

@ -18,8 +18,8 @@ public class BuildVars {
public static boolean LOGS_ENABLED = false; public static boolean LOGS_ENABLED = false;
public static boolean USE_CLOUD_STRINGS = true; public static boolean USE_CLOUD_STRINGS = true;
public static boolean CHECK_UPDATES = false; public static boolean CHECK_UPDATES = false;
public static int BUILD_VERSION = 1497; public static int BUILD_VERSION = 1517;
public static String BUILD_VERSION_STRING = "5.3.0"; public static String BUILD_VERSION_STRING = "5.4.0";
public static int APP_ID = 0; //obtain your own APP_ID at https://core.telegram.org/api/obtaining_api_id public static int APP_ID = 0; //obtain your own APP_ID at https://core.telegram.org/api/obtaining_api_id
public static String APP_HASH = ""; //obtain your own APP_HASH at https://core.telegram.org/api/obtaining_api_id public static String APP_HASH = ""; //obtain your own APP_HASH at https://core.telegram.org/api/obtaining_api_id
public static String HOCKEY_APP_HASH = "your-hockeyapp-api-key-here"; public static String HOCKEY_APP_HASH = "your-hockeyapp-api-key-here";

View File

@ -2008,8 +2008,8 @@ public class ContactsController {
try { try {
Uri rawContactUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, systemAccount.name).appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, systemAccount.type).build(); Uri rawContactUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, systemAccount.name).appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, systemAccount.type).build();
int value = contentResolver.delete(rawContactUri, ContactsContract.RawContacts.SYNC2 + " = " + user.id, null); int value = contentResolver.delete(rawContactUri, ContactsContract.RawContacts.SYNC2 + " = " + user.id, null);
} catch (Exception e) { } catch (Exception ignore) {
FileLog.e(e);
} }
} }
@ -2049,8 +2049,8 @@ public class ContactsController {
if (result != null && result.length > 0 && result[0].uri != null) { if (result != null && result.length > 0 && result[0].uri != null) {
res = Long.parseLong(result[0].uri.getLastPathSegment()); res = Long.parseLong(result[0].uri.getLastPathSegment());
} }
} catch (Exception e) { } catch (Exception ignore) {
FileLog.e(e);
} }
synchronized (observerLock) { synchronized (observerLock) {
ignoreChanges = false; ignoreChanges = false;

View File

@ -53,14 +53,14 @@ public class DispatchQueue extends Thread {
public void postRunnable(Runnable runnable, long delay) { public void postRunnable(Runnable runnable, long delay) {
try { try {
syncLatch.await(); syncLatch.await();
if (delay <= 0) {
handler.post(runnable);
} else {
handler.postDelayed(runnable, delay);
}
} catch (Exception e) { } catch (Exception e) {
FileLog.e(e); FileLog.e(e);
} }
if (delay <= 0) {
handler.post(runnable);
} else {
handler.postDelayed(runnable, delay);
}
} }
public void cleanupQueue() { public void cleanupQueue() {
@ -76,6 +76,10 @@ public class DispatchQueue extends Thread {
} }
public void recycle() {
handler.getLooper().quit();
}
@Override @Override
public void run() { public void run() {
Looper.prepare(); Looper.prepare();

View File

@ -17,6 +17,7 @@ import android.net.ConnectivityManager;
import android.util.LongSparseArray; import android.util.LongSparseArray;
import android.util.SparseArray; import android.util.SparseArray;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.TLRPC;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -33,27 +34,25 @@ public class DownloadController implements NotificationCenter.NotificationCenter
int getObserverTag(); int getObserverTag();
} }
public static final int AUTODOWNLOAD_MASK_PHOTO = 1; public static final int AUTODOWNLOAD_TYPE_PHOTO = 1;
public static final int AUTODOWNLOAD_MASK_AUDIO = 2; public static final int AUTODOWNLOAD_TYPE_AUDIO = 2;
public static final int AUTODOWNLOAD_MASK_VIDEO = 4; public static final int AUTODOWNLOAD_TYPE_VIDEO = 4;
public static final int AUTODOWNLOAD_MASK_DOCUMENT = 8; public static final int AUTODOWNLOAD_TYPE_DOCUMENT = 8;
public static final int AUTODOWNLOAD_MASK_MUSIC = 16;
public static final int AUTODOWNLOAD_MASK_GIF = 32; public static final int PRESET_NUM_CONTACT = 0;
public static final int AUTODOWNLOAD_MASK_VIDEOMESSAGE = 64; public static final int PRESET_NUM_PM = 1;
public boolean globalAutodownloadEnabled; public static final int PRESET_NUM_GROUP = 2;
public int mobileDataDownloadMask[] = new int[4]; public static final int PRESET_NUM_CHANNEL = 3;
public int wifiDownloadMask[] = new int[4];
public int roamingDownloadMask[] = new int[4]; public static final int PRESET_SIZE_NUM_PHOTO = 0;
public int mobileMaxFileSize[] = new int[7]; public static final int PRESET_SIZE_NUM_VIDEO = 1;
public int wifiMaxFileSize[] = new int[7]; public static final int PRESET_SIZE_NUM_DOCUMENT = 2;
public int roamingMaxFileSize[] = new int[7]; public static final int PRESET_SIZE_NUM_AUDIO = 3;
private int lastCheckMask = 0; private int lastCheckMask = 0;
private ArrayList<DownloadObject> photoDownloadQueue = new ArrayList<>(); private ArrayList<DownloadObject> photoDownloadQueue = new ArrayList<>();
private ArrayList<DownloadObject> audioDownloadQueue = new ArrayList<>(); private ArrayList<DownloadObject> audioDownloadQueue = new ArrayList<>();
private ArrayList<DownloadObject> videoMessageDownloadQueue = new ArrayList<>();
private ArrayList<DownloadObject> documentDownloadQueue = new ArrayList<>(); private ArrayList<DownloadObject> documentDownloadQueue = new ArrayList<>();
private ArrayList<DownloadObject> musicDownloadQueue = new ArrayList<>();
private ArrayList<DownloadObject> gifDownloadQueue = new ArrayList<>();
private ArrayList<DownloadObject> videoDownloadQueue = new ArrayList<>(); private ArrayList<DownloadObject> videoDownloadQueue = new ArrayList<>();
private HashMap<String, DownloadObject> downloadQueueKeys = new HashMap<>(); private HashMap<String, DownloadObject> downloadQueueKeys = new HashMap<>();
@ -65,8 +64,137 @@ public class DownloadController implements NotificationCenter.NotificationCenter
private ArrayList<FileDownloadProgressListener> deleteLaterArray = new ArrayList<>(); private ArrayList<FileDownloadProgressListener> deleteLaterArray = new ArrayList<>();
private int lastTag = 0; private int lastTag = 0;
private boolean loadingAutoDownloadConfig;
private LongSparseArray<Long> typingTimes = new LongSparseArray<>(); private LongSparseArray<Long> typingTimes = new LongSparseArray<>();
public static class Preset {
public int[] mask = new int[4];
public int[] sizes = new int[4];
public boolean preloadVideo;
public boolean preloadMusic;
public boolean lessCallData;
public boolean enabled;
public Preset(int m, int p, int v, int f, boolean pv, boolean pm, boolean e, boolean l) {
for (int a = 0; a < mask.length; a++) {
mask[a] = m;
}
sizes[PRESET_SIZE_NUM_PHOTO] = p;
sizes[PRESET_SIZE_NUM_VIDEO] = v;
sizes[PRESET_SIZE_NUM_DOCUMENT] = f;
sizes[PRESET_SIZE_NUM_AUDIO] = 512 * 1024;
preloadVideo = pv;
preloadMusic = pm;
lessCallData = l;
enabled = e;
}
public Preset(int[] m, int p, int v, int f, boolean pv, boolean pm, boolean e, boolean l) {
System.arraycopy(m, 0, mask, 0, mask.length);
sizes[PRESET_SIZE_NUM_PHOTO] = p;
sizes[PRESET_SIZE_NUM_VIDEO] = v;
sizes[PRESET_SIZE_NUM_DOCUMENT] = f;
sizes[PRESET_SIZE_NUM_AUDIO] = 512 * 1024;
preloadVideo = pv;
preloadMusic = pm;
lessCallData = l;
enabled = e;
}
public Preset(String str) {
String[] args = str.split("_");
if (args.length >= 11) {
mask[0] = Utilities.parseInt(args[0]);
mask[1] = Utilities.parseInt(args[1]);
mask[2] = Utilities.parseInt(args[2]);
mask[3] = Utilities.parseInt(args[3]);
sizes[PRESET_SIZE_NUM_PHOTO] = Utilities.parseInt(args[4]);
sizes[PRESET_SIZE_NUM_VIDEO] = Utilities.parseInt(args[5]);
sizes[PRESET_SIZE_NUM_DOCUMENT] = Utilities.parseInt(args[6]);
sizes[PRESET_SIZE_NUM_AUDIO] = Utilities.parseInt(args[7]);
preloadVideo = Utilities.parseInt(args[8]) == 1;
preloadMusic = Utilities.parseInt(args[9]) == 1;
enabled = Utilities.parseInt(args[10]) == 1;
if (args.length >= 12) {
lessCallData = Utilities.parseInt(args[11]) == 1;
}
}
}
public void set(Preset preset) {
System.arraycopy(preset.mask, 0, mask, 0, mask.length);
System.arraycopy(preset.sizes, 0, sizes, 0, sizes.length);
preloadVideo = preset.preloadVideo;
preloadMusic = preset.preloadMusic;
lessCallData = preset.lessCallData;
//enabled = preset.enabled;
}
public void set(TLRPC.TL_autoDownloadSettings settings) {
preloadMusic = settings.audio_preload_next;
preloadVideo = settings.video_preload_large;
lessCallData = settings.phonecalls_less_data;
//enabled = !settings.disabled;
sizes[PRESET_SIZE_NUM_PHOTO] = Math.max(500 * 1024, settings.photo_size_max);
sizes[PRESET_SIZE_NUM_VIDEO] = Math.max(500 * 1024, settings.video_size_max);
sizes[PRESET_SIZE_NUM_DOCUMENT] = Math.max(500 * 1024, settings.file_size_max);
for (int a = 0; a < mask.length; a++) {
if (settings.photo_size_max != 0 && !settings.disabled) {
mask[a] |= AUTODOWNLOAD_TYPE_PHOTO;
} else {
mask[a] &=~ AUTODOWNLOAD_TYPE_PHOTO;
}
if (settings.video_size_max != 0 && !settings.disabled) {
mask[a] |= AUTODOWNLOAD_TYPE_VIDEO;
} else {
mask[a] &=~ AUTODOWNLOAD_TYPE_VIDEO;
}
if (settings.file_size_max != 0 && !settings.disabled) {
mask[a] |= AUTODOWNLOAD_TYPE_DOCUMENT;
} else {
mask[a] &=~ AUTODOWNLOAD_TYPE_DOCUMENT;
}
}
}
@Override
public String toString() {
return mask[0] + "_" + mask[1] + "_" + mask[2] + "_" + mask[3] +
"_" + sizes[PRESET_SIZE_NUM_PHOTO] +
"_" + sizes[PRESET_SIZE_NUM_VIDEO] +
"_" + sizes[PRESET_SIZE_NUM_DOCUMENT] +
"_" + sizes[PRESET_SIZE_NUM_AUDIO] +
"_" + (preloadVideo ? 1 : 0) +
"_" + (preloadMusic ? 1 : 0) +
"_" + (enabled ? 1 : 0) +
"_" + (lessCallData ? 1 : 0);
}
public boolean equals(Preset obj) {
return mask[0] == obj.mask[0] &&
mask[1] == obj.mask[1] &&
mask[2] == obj.mask[2] &&
mask[3] == obj.mask[3] &&
sizes[0] == obj.sizes[0] &&
sizes[1] == obj.sizes[1] &&
sizes[2] == obj.sizes[2] &&
sizes[3] == obj.sizes[3] &&
preloadVideo == obj.preloadVideo &&
preloadMusic == obj.preloadMusic;
}
}
public Preset lowPreset;
public Preset mediumPreset;
public Preset highPreset;
public Preset mobilePreset;
public Preset wifiPreset;
public Preset roamingPreset;
public int currentMobilePreset;
public int currentWifiPreset;
public int currentRoamingPreset;
private int currentAccount; private int currentAccount;
private static volatile DownloadController Instance[] = new DownloadController[UserConfig.MAX_ACCOUNT_COUNT]; private static volatile DownloadController Instance[] = new DownloadController[UserConfig.MAX_ACCOUNT_COUNT];
@ -86,32 +214,63 @@ public class DownloadController implements NotificationCenter.NotificationCenter
public DownloadController(int instance) { public DownloadController(int instance) {
currentAccount = instance; currentAccount = instance;
SharedPreferences preferences = MessagesController.getMainSettings(currentAccount); SharedPreferences preferences = MessagesController.getMainSettings(currentAccount);
for (int a = 0; a < 4; a++) { lowPreset = new Preset(preferences.getString("preset0", "1_1_1_1_1048576_512000_512000_524288_0_0_1_1"));
String key = "mobileDataDownloadMask" + (a == 0 ? "" : a); mediumPreset = new Preset(preferences.getString("preset1", "13_13_13_13_1048576_10485760_1048576_524288_1_1_1_0"));
if (a == 0 || preferences.contains(key)) { highPreset = new Preset(preferences.getString("preset2", "13_13_13_13_1048576_15728640_3145728_524288_1_1_1_0"));
mobileDataDownloadMask[a] = preferences.getInt(key, AUTODOWNLOAD_MASK_PHOTO | AUTODOWNLOAD_MASK_AUDIO | AUTODOWNLOAD_MASK_MUSIC | AUTODOWNLOAD_MASK_GIF | AUTODOWNLOAD_MASK_VIDEOMESSAGE); boolean newConfig;
wifiDownloadMask[a] = preferences.getInt("wifiDownloadMask" + (a == 0 ? "" : a), AUTODOWNLOAD_MASK_PHOTO | AUTODOWNLOAD_MASK_AUDIO | AUTODOWNLOAD_MASK_MUSIC | AUTODOWNLOAD_MASK_GIF | AUTODOWNLOAD_MASK_VIDEOMESSAGE); if (newConfig = preferences.contains("newConfig") || !UserConfig.getInstance(currentAccount).isClientActivated()) {
roamingDownloadMask[a] = preferences.getInt("roamingDownloadMask" + (a == 0 ? "" : a), 0); mobilePreset = new Preset(preferences.getString("mobilePreset", mediumPreset.toString()));
} else { wifiPreset = new Preset(preferences.getString("wifiPreset", highPreset.toString()));
mobileDataDownloadMask[a] = mobileDataDownloadMask[0]; roamingPreset = new Preset(preferences.getString("roamingPreset", lowPreset.toString()));
wifiDownloadMask[a] = wifiDownloadMask[0]; currentMobilePreset = preferences.getInt("currentMobilePreset", 3);
roamingDownloadMask[a] = roamingDownloadMask[0]; currentWifiPreset = preferences.getInt("currentWifiPreset", 3);
currentRoamingPreset = preferences.getInt("currentRoamingPreset", 3);
if (!newConfig) {
preferences.edit().putBoolean("newConfig", true).commit();
} }
} } else {
for (int a = 0; a < 7; a++) { int mobileDataDownloadMask[] = new int[4];
int sdefault; int wifiDownloadMask[] = new int[4];
if (a == 1) { int roamingDownloadMask[] = new int[4];
sdefault = 2 * 1024 * 1024; int mobileMaxFileSize[] = new int[7];
} else if (a == 6) { int wifiMaxFileSize[] = new int[7];
sdefault = 5 * 1024 * 1024; int roamingMaxFileSize[] = new int[7];
} else {
sdefault = 10 * 1024 * 1024; for (int a = 0; a < 4; a++) {
String key = "mobileDataDownloadMask" + (a == 0 ? "" : a);
if (a == 0 || preferences.contains(key)) {
mobileDataDownloadMask[a] = preferences.getInt(key, AUTODOWNLOAD_TYPE_PHOTO | AUTODOWNLOAD_TYPE_VIDEO | AUTODOWNLOAD_TYPE_DOCUMENT);
wifiDownloadMask[a] = preferences.getInt("wifiDownloadMask" + (a == 0 ? "" : a), AUTODOWNLOAD_TYPE_PHOTO | AUTODOWNLOAD_TYPE_VIDEO | AUTODOWNLOAD_TYPE_DOCUMENT);
roamingDownloadMask[a] = preferences.getInt("roamingDownloadMask" + (a == 0 ? "" : a), AUTODOWNLOAD_TYPE_PHOTO);
} else {
mobileDataDownloadMask[a] = mobileDataDownloadMask[0];
wifiDownloadMask[a] = wifiDownloadMask[0];
roamingDownloadMask[a] = roamingDownloadMask[0];
}
} }
mobileMaxFileSize[a] = preferences.getInt("mobileMaxDownloadSize" + a, sdefault);
wifiMaxFileSize[a] = preferences.getInt("wifiMaxDownloadSize" + a, sdefault); mobileMaxFileSize[2] = preferences.getInt("mobileMaxDownloadSize" + 2, mediumPreset.sizes[PRESET_SIZE_NUM_VIDEO]);
roamingMaxFileSize[a] = preferences.getInt("roamingMaxDownloadSize" + a, sdefault); mobileMaxFileSize[3] = preferences.getInt("mobileMaxDownloadSize" + 3, mediumPreset.sizes[PRESET_SIZE_NUM_DOCUMENT]);
wifiMaxFileSize[2] = preferences.getInt("wifiMaxDownloadSize" + 2, highPreset.sizes[PRESET_SIZE_NUM_VIDEO]);
wifiMaxFileSize[3] = preferences.getInt("wifiMaxDownloadSize" + 3, highPreset.sizes[PRESET_SIZE_NUM_DOCUMENT]);
roamingMaxFileSize[2] = preferences.getInt("roamingMaxDownloadSize" + 2, lowPreset.sizes[PRESET_SIZE_NUM_VIDEO]);
roamingMaxFileSize[3] = preferences.getInt("roamingMaxDownloadSize" + 3, lowPreset.sizes[PRESET_SIZE_NUM_DOCUMENT]);
boolean globalAutodownloadEnabled = preferences.getBoolean("globalAutodownloadEnabled", true);
mobilePreset = new Preset(mobileDataDownloadMask, mediumPreset.sizes[PRESET_SIZE_NUM_PHOTO], mobileMaxFileSize[2], mobileMaxFileSize[3], true, true, globalAutodownloadEnabled, false);
wifiPreset = new Preset(wifiDownloadMask, highPreset.sizes[PRESET_SIZE_NUM_PHOTO], wifiMaxFileSize[2], wifiMaxFileSize[3], true, true, globalAutodownloadEnabled, false);
roamingPreset = new Preset(roamingDownloadMask, lowPreset.sizes[PRESET_SIZE_NUM_PHOTO], roamingMaxFileSize[2], roamingMaxFileSize[3], false, false, globalAutodownloadEnabled, true);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("newConfig", true);
editor.putString("mobilePreset", mobilePreset.toString());
editor.putString("wifiPreset", wifiPreset.toString());
editor.putString("roamingPreset", roamingPreset.toString());
editor.putInt("currentMobilePreset", currentMobilePreset = 3);
editor.putInt("currentWifiPreset", currentWifiPreset = 3);
editor.putInt("currentRoamingPreset", currentRoamingPreset = 3);
editor.commit();
} }
globalAutodownloadEnabled = preferences.getBoolean("globalAutodownloadEnabled", true);
AndroidUtilities.runOnUIThread(() -> { AndroidUtilities.runOnUIThread(() -> {
NotificationCenter.getInstance(currentAccount).addObserver(DownloadController.this, NotificationCenter.fileDidFailedLoad); NotificationCenter.getInstance(currentAccount).addObserver(DownloadController.this, NotificationCenter.fileDidFailedLoad);
@ -120,6 +279,7 @@ public class DownloadController implements NotificationCenter.NotificationCenter
NotificationCenter.getInstance(currentAccount).addObserver(DownloadController.this, NotificationCenter.FileUploadProgressChanged); NotificationCenter.getInstance(currentAccount).addObserver(DownloadController.this, NotificationCenter.FileUploadProgressChanged);
NotificationCenter.getInstance(currentAccount).addObserver(DownloadController.this, NotificationCenter.httpFileDidLoad); NotificationCenter.getInstance(currentAccount).addObserver(DownloadController.this, NotificationCenter.httpFileDidLoad);
NotificationCenter.getInstance(currentAccount).addObserver(DownloadController.this, NotificationCenter.httpFileDidFailedLoad); NotificationCenter.getInstance(currentAccount).addObserver(DownloadController.this, NotificationCenter.httpFileDidFailedLoad);
loadAutoDownloadConfig(false);
}); });
BroadcastReceiver networkStateReceiver = new BroadcastReceiver() { BroadcastReceiver networkStateReceiver = new BroadcastReceiver() {
@ -136,72 +296,145 @@ public class DownloadController implements NotificationCenter.NotificationCenter
} }
} }
public static int maskToIndex(int mask) { public void loadAutoDownloadConfig(boolean force) {
if (mask == AUTODOWNLOAD_MASK_PHOTO) { if (loadingAutoDownloadConfig || !force && Math.abs(System.currentTimeMillis() - UserConfig.getInstance(currentAccount).autoDownloadConfigLoadTime) < 24 * 60 * 60 * 1000) {
return 0; return;
} else if (mask == AUTODOWNLOAD_MASK_AUDIO) {
return 1;
} else if (mask == AUTODOWNLOAD_MASK_VIDEO) {
return 2;
} else if (mask == AUTODOWNLOAD_MASK_DOCUMENT) {
return 3;
} else if (mask == AUTODOWNLOAD_MASK_MUSIC) {
return 4;
} else if (mask == AUTODOWNLOAD_MASK_GIF) {
return 5;
} else if (mask == AUTODOWNLOAD_MASK_VIDEOMESSAGE) {
return 6;
} }
return 0; loadingAutoDownloadConfig = true;
TLRPC.TL_account_getAutoDownloadSettings req = new TLRPC.TL_account_getAutoDownloadSettings();
ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> {
loadingAutoDownloadConfig = false;
UserConfig.getInstance(currentAccount).autoDownloadConfigLoadTime = System.currentTimeMillis();
UserConfig.getInstance(currentAccount).saveConfig(false);
if (response != null) {
TLRPC.TL_account_autoDownloadSettings res = (TLRPC.TL_account_autoDownloadSettings) response;
lowPreset.set(res.low);
mediumPreset.set(res.medium);
highPreset.set(res.high);
for (int a = 0; a < 3; a++) {
Preset preset;
if (a == 0) {
preset = mobilePreset;
} else if (a == 1) {
preset = wifiPreset;
} else {
preset = roamingPreset;
}
if (preset.equals(lowPreset)) {
preset.set(res.low);
} else if (preset.equals(mediumPreset)) {
preset.set(res.medium);
} else if (preset.equals(highPreset)) {
preset.set(res.high);
}
}
SharedPreferences.Editor editor = MessagesController.getMainSettings(currentAccount).edit();
editor.putString("mobilePreset", mobilePreset.toString());
editor.putString("wifiPreset", wifiPreset.toString());
editor.putString("roamingPreset", roamingPreset.toString());
editor.putString("preset0", lowPreset.toString());
editor.putString("preset1", mediumPreset.toString());
editor.putString("preset2", highPreset.toString());
editor.commit();
String str1 = lowPreset.toString();
String str2 = mediumPreset.toString();
String str3 = highPreset.toString();
checkAutodownloadSettings();
}
}));
}
public Preset getCurrentMobilePreset() {
if (currentMobilePreset == 0) {
return lowPreset;
} else if (currentMobilePreset == 1) {
return mediumPreset;
} else if (currentMobilePreset == 2) {
return highPreset;
} else {
return mobilePreset;
}
}
public Preset getCurrentWiFiPreset() {
if (currentWifiPreset == 0) {
return lowPreset;
} else if (currentWifiPreset == 1) {
return mediumPreset;
} else if (currentWifiPreset == 2) {
return highPreset;
} else {
return wifiPreset;
}
}
public Preset getCurrentRoamingPreset() {
if (currentRoamingPreset == 0) {
return lowPreset;
} else if (currentRoamingPreset == 1) {
return mediumPreset;
} else if (currentRoamingPreset == 2) {
return highPreset;
} else {
return roamingPreset;
}
}
public static int typeToIndex(int type) {
if (type == AUTODOWNLOAD_TYPE_PHOTO) {
return PRESET_SIZE_NUM_PHOTO;
} else if (type == AUTODOWNLOAD_TYPE_AUDIO) {
return PRESET_SIZE_NUM_AUDIO;
} else if (type == AUTODOWNLOAD_TYPE_VIDEO) {
return PRESET_SIZE_NUM_VIDEO;
} else if (type == AUTODOWNLOAD_TYPE_DOCUMENT) {
return PRESET_SIZE_NUM_DOCUMENT;
}
return PRESET_SIZE_NUM_PHOTO;
} }
public void cleanup() { public void cleanup() {
photoDownloadQueue.clear(); photoDownloadQueue.clear();
audioDownloadQueue.clear(); audioDownloadQueue.clear();
videoMessageDownloadQueue.clear();
documentDownloadQueue.clear(); documentDownloadQueue.clear();
videoDownloadQueue.clear(); videoDownloadQueue.clear();
musicDownloadQueue.clear();
gifDownloadQueue.clear();
downloadQueueKeys.clear(); downloadQueueKeys.clear();
typingTimes.clear(); typingTimes.clear();
} }
public int getAutodownloadMask() { public int getAutodownloadMask() {
if (!globalAutodownloadEnabled) {
return 0;
}
int result = 0; int result = 0;
int masksArray[]; int masksArray[];
if (ApplicationLoader.isConnectedToWiFi()) { if (ApplicationLoader.isConnectedToWiFi()) {
masksArray = wifiDownloadMask; if (!wifiPreset.enabled) {
return 0;
}
masksArray = getCurrentWiFiPreset().mask;
} else if (ApplicationLoader.isRoaming()) { } else if (ApplicationLoader.isRoaming()) {
masksArray = roamingDownloadMask; if (!roamingPreset.enabled) {
return 0;
}
masksArray = getCurrentRoamingPreset().mask;
} else { } else {
masksArray = mobileDataDownloadMask; if (!mobilePreset.enabled) {
return 0;
}
masksArray = getCurrentMobilePreset().mask;
} }
for (int a = 0; a < 4; a++) { for (int a = 0; a < masksArray.length; a++) {
int mask = 0; int mask = 0;
if ((masksArray[a] & AUTODOWNLOAD_MASK_PHOTO) != 0) { if ((masksArray[a] & AUTODOWNLOAD_TYPE_PHOTO) != 0) {
mask |= AUTODOWNLOAD_MASK_PHOTO; mask |= AUTODOWNLOAD_TYPE_PHOTO;
} }
if ((masksArray[a] & AUTODOWNLOAD_MASK_AUDIO) != 0) { if ((masksArray[a] & AUTODOWNLOAD_TYPE_AUDIO) != 0) {
mask |= AUTODOWNLOAD_MASK_AUDIO; mask |= AUTODOWNLOAD_TYPE_AUDIO;
} }
if ((masksArray[a] & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0) { if ((masksArray[a] & AUTODOWNLOAD_TYPE_VIDEO) != 0) {
mask |= AUTODOWNLOAD_MASK_VIDEOMESSAGE; mask |= AUTODOWNLOAD_TYPE_VIDEO;
} }
if ((masksArray[a] & AUTODOWNLOAD_MASK_VIDEO) != 0) { if ((masksArray[a] & AUTODOWNLOAD_TYPE_DOCUMENT) != 0) {
mask |= AUTODOWNLOAD_MASK_VIDEO; mask |= AUTODOWNLOAD_TYPE_DOCUMENT;
}
if ((masksArray[a] & AUTODOWNLOAD_MASK_DOCUMENT) != 0) {
mask |= AUTODOWNLOAD_MASK_DOCUMENT;
}
if ((masksArray[a] & AUTODOWNLOAD_MASK_MUSIC) != 0) {
mask |= AUTODOWNLOAD_MASK_MUSIC;
}
if ((masksArray[a] & AUTODOWNLOAD_MASK_GIF) != 0) {
mask |= AUTODOWNLOAD_MASK_GIF;
} }
result |= mask << (a * 8); result |= mask << (a * 8);
} }
@ -209,31 +442,22 @@ public class DownloadController implements NotificationCenter.NotificationCenter
} }
protected int getAutodownloadMaskAll() { protected int getAutodownloadMaskAll() {
if (!globalAutodownloadEnabled) { if (!mobilePreset.enabled && !roamingPreset.enabled && !wifiPreset.enabled) {
return 0; return 0;
} }
int mask = 0; int mask = 0;
for (int a = 0; a < 4; a++) { for (int a = 0; a < 4; a++) {
if ((mobileDataDownloadMask[a] & AUTODOWNLOAD_MASK_PHOTO) != 0 || (wifiDownloadMask[a] & AUTODOWNLOAD_MASK_PHOTO) != 0 || (roamingDownloadMask[a] & AUTODOWNLOAD_MASK_PHOTO) != 0) { if ((getCurrentMobilePreset().mask[a] & AUTODOWNLOAD_TYPE_PHOTO) != 0 || (getCurrentWiFiPreset().mask[a] & AUTODOWNLOAD_TYPE_PHOTO) != 0 || (getCurrentRoamingPreset().mask[a] & AUTODOWNLOAD_TYPE_PHOTO) != 0) {
mask |= AUTODOWNLOAD_MASK_PHOTO; mask |= AUTODOWNLOAD_TYPE_PHOTO;
} }
if ((mobileDataDownloadMask[a] & AUTODOWNLOAD_MASK_AUDIO) != 0 || (wifiDownloadMask[a] & AUTODOWNLOAD_MASK_AUDIO) != 0 || (roamingDownloadMask[a] & AUTODOWNLOAD_MASK_AUDIO) != 0) { if ((getCurrentMobilePreset().mask[a] & AUTODOWNLOAD_TYPE_AUDIO) != 0 || (getCurrentWiFiPreset().mask[a] & AUTODOWNLOAD_TYPE_AUDIO) != 0 || (getCurrentRoamingPreset().mask[a] & AUTODOWNLOAD_TYPE_AUDIO) != 0) {
mask |= AUTODOWNLOAD_MASK_AUDIO; mask |= AUTODOWNLOAD_TYPE_AUDIO;
} }
if ((mobileDataDownloadMask[a] & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0 || (wifiDownloadMask[a] & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0 || (roamingDownloadMask[a] & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0) { if ((getCurrentMobilePreset().mask[a] & AUTODOWNLOAD_TYPE_VIDEO) != 0 || (getCurrentWiFiPreset().mask[a] & AUTODOWNLOAD_TYPE_VIDEO) != 0 || (getCurrentRoamingPreset().mask[a] & AUTODOWNLOAD_TYPE_VIDEO) != 0) {
mask |= AUTODOWNLOAD_MASK_VIDEOMESSAGE; mask |= AUTODOWNLOAD_TYPE_VIDEO;
} }
if ((mobileDataDownloadMask[a] & AUTODOWNLOAD_MASK_VIDEO) != 0 || (wifiDownloadMask[a] & AUTODOWNLOAD_MASK_VIDEO) != 0 || (roamingDownloadMask[a] & AUTODOWNLOAD_MASK_VIDEO) != 0) { if ((getCurrentMobilePreset().mask[a] & AUTODOWNLOAD_TYPE_DOCUMENT) != 0 || (getCurrentWiFiPreset().mask[a] & AUTODOWNLOAD_TYPE_DOCUMENT) != 0 || (getCurrentRoamingPreset().mask[a] & AUTODOWNLOAD_TYPE_DOCUMENT) != 0) {
mask |= AUTODOWNLOAD_MASK_VIDEO; mask |= AUTODOWNLOAD_TYPE_DOCUMENT;
}
if ((mobileDataDownloadMask[a] & AUTODOWNLOAD_MASK_DOCUMENT) != 0 || (wifiDownloadMask[a] & AUTODOWNLOAD_MASK_DOCUMENT) != 0 || (roamingDownloadMask[a] & AUTODOWNLOAD_MASK_DOCUMENT) != 0) {
mask |= AUTODOWNLOAD_MASK_DOCUMENT;
}
if ((mobileDataDownloadMask[a] & AUTODOWNLOAD_MASK_MUSIC) != 0 || (wifiDownloadMask[a] & AUTODOWNLOAD_MASK_MUSIC) != 0 || (roamingDownloadMask[a] & AUTODOWNLOAD_MASK_MUSIC) != 0) {
mask |= AUTODOWNLOAD_MASK_MUSIC;
}
if ((mobileDataDownloadMask[a] & AUTODOWNLOAD_MASK_GIF) != 0 || (wifiDownloadMask[a] & AUTODOWNLOAD_MASK_GIF) != 0 || (roamingDownloadMask[a] & AUTODOWNLOAD_MASK_GIF) != 0) {
mask |= AUTODOWNLOAD_MASK_GIF;
} }
} }
return mask; return mask;
@ -245,9 +469,9 @@ public class DownloadController implements NotificationCenter.NotificationCenter
return; return;
} }
lastCheckMask = currentMask; lastCheckMask = currentMask;
if ((currentMask & AUTODOWNLOAD_MASK_PHOTO) != 0) { if ((currentMask & AUTODOWNLOAD_TYPE_PHOTO) != 0) {
if (photoDownloadQueue.isEmpty()) { if (photoDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_PHOTO); newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_PHOTO);
} }
} else { } else {
for (int a = 0; a < photoDownloadQueue.size(); a++) { for (int a = 0; a < photoDownloadQueue.size(); a++) {
@ -260,9 +484,9 @@ public class DownloadController implements NotificationCenter.NotificationCenter
} }
photoDownloadQueue.clear(); photoDownloadQueue.clear();
} }
if ((currentMask & AUTODOWNLOAD_MASK_AUDIO) != 0) { if ((currentMask & AUTODOWNLOAD_TYPE_AUDIO) != 0) {
if (audioDownloadQueue.isEmpty()) { if (audioDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_AUDIO); newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_AUDIO);
} }
} else { } else {
for (int a = 0; a < audioDownloadQueue.size(); a++) { for (int a = 0; a < audioDownloadQueue.size(); a++) {
@ -271,20 +495,9 @@ public class DownloadController implements NotificationCenter.NotificationCenter
} }
audioDownloadQueue.clear(); audioDownloadQueue.clear();
} }
if ((currentMask & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0) { if ((currentMask & AUTODOWNLOAD_TYPE_DOCUMENT) != 0) {
if (videoMessageDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_VIDEOMESSAGE);
}
} else {
for (int a = 0; a < videoMessageDownloadQueue.size(); a++) {
DownloadObject downloadObject = videoMessageDownloadQueue.get(a);
FileLoader.getInstance(currentAccount).cancelLoadFile((TLRPC.Document) downloadObject.object);
}
videoMessageDownloadQueue.clear();
}
if ((currentMask & AUTODOWNLOAD_MASK_DOCUMENT) != 0) {
if (documentDownloadQueue.isEmpty()) { if (documentDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_DOCUMENT); newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_DOCUMENT);
} }
} else { } else {
for (int a = 0; a < documentDownloadQueue.size(); a++) { for (int a = 0; a < documentDownloadQueue.size(); a++) {
@ -294,9 +507,9 @@ public class DownloadController implements NotificationCenter.NotificationCenter
} }
documentDownloadQueue.clear(); documentDownloadQueue.clear();
} }
if ((currentMask & AUTODOWNLOAD_MASK_VIDEO) != 0) { if ((currentMask & AUTODOWNLOAD_TYPE_VIDEO) != 0) {
if (videoDownloadQueue.isEmpty()) { if (videoDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_VIDEO); newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_VIDEO);
} }
} else { } else {
for (int a = 0; a < videoDownloadQueue.size(); a++) { for (int a = 0; a < videoDownloadQueue.size(); a++) {
@ -305,82 +518,40 @@ public class DownloadController implements NotificationCenter.NotificationCenter
} }
videoDownloadQueue.clear(); videoDownloadQueue.clear();
} }
if ((currentMask & AUTODOWNLOAD_MASK_MUSIC) != 0) {
if (musicDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_MUSIC);
}
} else {
for (int a = 0; a < musicDownloadQueue.size(); a++) {
DownloadObject downloadObject = musicDownloadQueue.get(a);
TLRPC.Document document = (TLRPC.Document) downloadObject.object;
FileLoader.getInstance(currentAccount).cancelLoadFile(document);
}
musicDownloadQueue.clear();
}
if ((currentMask & AUTODOWNLOAD_MASK_GIF) != 0) {
if (gifDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_GIF);
}
} else {
for (int a = 0; a < gifDownloadQueue.size(); a++) {
DownloadObject downloadObject = gifDownloadQueue.get(a);
TLRPC.Document document = (TLRPC.Document) downloadObject.object;
FileLoader.getInstance(currentAccount).cancelLoadFile(document);
}
gifDownloadQueue.clear();
}
int mask = getAutodownloadMaskAll(); int mask = getAutodownloadMaskAll();
if (mask == 0) { if (mask == 0) {
MessagesStorage.getInstance(currentAccount).clearDownloadQueue(0); MessagesStorage.getInstance(currentAccount).clearDownloadQueue(0);
} else { } else {
if ((mask & AUTODOWNLOAD_MASK_PHOTO) == 0) { if ((mask & AUTODOWNLOAD_TYPE_PHOTO) == 0) {
MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_MASK_PHOTO); MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_TYPE_PHOTO);
} }
if ((mask & AUTODOWNLOAD_MASK_AUDIO) == 0) { if ((mask & AUTODOWNLOAD_TYPE_AUDIO) == 0) {
MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_MASK_AUDIO); MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_TYPE_AUDIO);
} }
if ((mask & AUTODOWNLOAD_MASK_VIDEOMESSAGE) == 0) { if ((mask & AUTODOWNLOAD_TYPE_VIDEO) == 0) {
MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_MASK_VIDEOMESSAGE); MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_TYPE_VIDEO);
} }
if ((mask & AUTODOWNLOAD_MASK_VIDEO) == 0) { if ((mask & AUTODOWNLOAD_TYPE_DOCUMENT) == 0) {
MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_MASK_VIDEO); MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_TYPE_DOCUMENT);
}
if ((mask & AUTODOWNLOAD_MASK_DOCUMENT) == 0) {
MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_MASK_DOCUMENT);
}
if ((mask & AUTODOWNLOAD_MASK_MUSIC) == 0) {
MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_MASK_MUSIC);
}
if ((mask & AUTODOWNLOAD_MASK_GIF) == 0) {
MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_MASK_GIF);
} }
} }
} }
public boolean canDownloadMedia(MessageObject messageObject) { public boolean canDownloadMedia(MessageObject messageObject) {
return canDownloadMedia(messageObject.messageOwner); return canDownloadMedia(messageObject.messageOwner) == 1;
} }
public boolean canDownloadMedia(TLRPC.Message message) { public int canDownloadMedia(TLRPC.Message message) {
if (!globalAutodownloadEnabled) {
return false;
}
int type; int type;
if (MessageObject.isPhoto(message) || MessageObject.isStickerMessage(message)) { boolean isVideo;
type = AUTODOWNLOAD_MASK_PHOTO; if ((isVideo = MessageObject.isVideoMessage(message)) || MessageObject.isGifMessage(message) || MessageObject.isRoundVideoMessage(message)) {
type = AUTODOWNLOAD_TYPE_VIDEO;
} else if (MessageObject.isVoiceMessage(message)) { } else if (MessageObject.isVoiceMessage(message)) {
type = AUTODOWNLOAD_MASK_AUDIO; type = AUTODOWNLOAD_TYPE_AUDIO;
} else if (MessageObject.isRoundVideoMessage(message)) { } else if (MessageObject.isPhoto(message) || MessageObject.isStickerMessage(message)) {
type = AUTODOWNLOAD_MASK_VIDEOMESSAGE; type = AUTODOWNLOAD_TYPE_PHOTO;
} else if (MessageObject.isVideoMessage(message)) {
type = AUTODOWNLOAD_MASK_VIDEO;
} else if (MessageObject.isMusicMessage(message)) {
type = AUTODOWNLOAD_MASK_MUSIC;
} else if (MessageObject.isGifMessage(message)) {
type = AUTODOWNLOAD_MASK_GIF;
} else { } else {
type = AUTODOWNLOAD_MASK_DOCUMENT; type = AUTODOWNLOAD_TYPE_DOCUMENT;
} }
int mask; int mask;
int index; int index;
@ -394,10 +565,18 @@ public class DownloadController implements NotificationCenter.NotificationCenter
index = 1; index = 1;
} }
} else if (peer.chat_id != 0) { } else if (peer.chat_id != 0) {
index = 2; if (message.from_id != 0 && ContactsController.getInstance(currentAccount).contactsDict.containsKey(message.from_id)) {
index = 0;
} else {
index = 2;
}
} else { } else {
if (MessageObject.isMegagroup(message)) { if (MessageObject.isMegagroup(message)) {
index = 2; if (message.from_id != 0 && ContactsController.getInstance(currentAccount).contactsDict.containsKey(message.from_id)) {
index = 0;
} else {
index = 2;
}
} else { } else {
index = 3; index = 3;
} }
@ -405,63 +584,132 @@ public class DownloadController implements NotificationCenter.NotificationCenter
} else { } else {
index = 1; index = 1;
} }
Preset preset;
if (ApplicationLoader.isConnectedToWiFi()) { if (ApplicationLoader.isConnectedToWiFi()) {
mask = wifiDownloadMask[index]; if (!wifiPreset.enabled) {
maxSize = wifiMaxFileSize[maskToIndex(type)]; return 0;
}
preset = getCurrentWiFiPreset();
} else if (ApplicationLoader.isRoaming()) { } else if (ApplicationLoader.isRoaming()) {
mask = roamingDownloadMask[index]; if (!roamingPreset.enabled) {
maxSize = roamingMaxFileSize[maskToIndex(type)]; return 0;
}
preset = getCurrentRoamingPreset();
} else { } else {
mask = mobileDataDownloadMask[index]; if (!mobilePreset.enabled) {
maxSize = mobileMaxFileSize[maskToIndex(type)]; return 0;
}
preset = getCurrentMobilePreset();
}
mask = preset.mask[index];
maxSize = preset.sizes[typeToIndex(type)];
int size = MessageObject.getMessageSize(message);
if (isVideo && preset.preloadVideo && size > maxSize && maxSize > 2 * 1024 * 1024) {
return (mask & type) != 0 ? 2 : 0;
} else {
return (type == AUTODOWNLOAD_TYPE_PHOTO || size != 0 && size <= maxSize) && (type == AUTODOWNLOAD_TYPE_AUDIO || (mask & type) != 0) ? 1 : 0;
}
}
protected boolean canDownloadNextTrack() {
if (ApplicationLoader.isConnectedToWiFi()) {
return wifiPreset.enabled && getCurrentWiFiPreset().preloadMusic;
} else if (ApplicationLoader.isRoaming()) {
return roamingPreset.enabled && getCurrentRoamingPreset().preloadMusic;
} else {
return mobilePreset.enabled && getCurrentMobilePreset().preloadMusic;
} }
return (type == AUTODOWNLOAD_MASK_PHOTO || MessageObject.getMessageSize(message) <= maxSize) && (mask & type) != 0;
} }
protected int getCurrentDownloadMask() { protected int getCurrentDownloadMask() {
if (!globalAutodownloadEnabled) {
return 0;
}
if (ApplicationLoader.isConnectedToWiFi()) { if (ApplicationLoader.isConnectedToWiFi()) {
if (!wifiPreset.enabled) {
return 0;
}
int mask = 0; int mask = 0;
for (int a = 0; a < 4; a++) { for (int a = 0; a < 4; a++) {
mask |= wifiDownloadMask[a]; mask |= getCurrentWiFiPreset().mask[a];
} }
return mask; return mask;
} else if (ApplicationLoader.isRoaming()) { } else if (ApplicationLoader.isRoaming()) {
if (!roamingPreset.enabled) {
return 0;
}
int mask = 0; int mask = 0;
for (int a = 0; a < 4; a++) { for (int a = 0; a < 4; a++) {
mask |= roamingDownloadMask[a]; mask |= getCurrentRoamingPreset().mask[a];
} }
return mask; return mask;
} else { } else {
if (!mobilePreset.enabled) {
return 0;
}
int mask = 0; int mask = 0;
for (int a = 0; a < 4; a++) { for (int a = 0; a < 4; a++) {
mask |= mobileDataDownloadMask[a]; mask |= getCurrentMobilePreset().mask[a];
} }
return mask; return mask;
} }
} }
public void savePresetToServer(int type) {
TLRPC.TL_account_saveAutoDownloadSettings req = new TLRPC.TL_account_saveAutoDownloadSettings();
Preset preset;
boolean enabled;
if (type == 0) {
preset = getCurrentMobilePreset();
enabled = mobilePreset.enabled;
} else if (type == 1) {
preset = getCurrentWiFiPreset();
enabled = wifiPreset.enabled;
} else {
preset = getCurrentRoamingPreset();
enabled = roamingPreset.enabled;
}
req.settings = new TLRPC.TL_autoDownloadSettings();
req.settings.audio_preload_next = preset.preloadMusic;
req.settings.video_preload_large = preset.preloadVideo;
req.settings.phonecalls_less_data = preset.lessCallData;
req.settings.disabled = !enabled;
boolean photo = false;
boolean video = false;
boolean document = false;
for (int a = 0; a < preset.mask.length; a++) {
if ((preset.mask[a] & AUTODOWNLOAD_TYPE_PHOTO) != 0) {
photo = true;
}
if ((preset.mask[a] & AUTODOWNLOAD_TYPE_VIDEO) != 0) {
video = true;
}
if ((preset.mask[a] & AUTODOWNLOAD_TYPE_DOCUMENT) != 0) {
document = true;
}
if (photo && video && document) {
break;
}
}
req.settings.photo_size_max = photo ? preset.sizes[PRESET_SIZE_NUM_PHOTO] : 0;
req.settings.video_size_max = video ? preset.sizes[PRESET_SIZE_NUM_VIDEO] : 0;
req.settings.file_size_max = document ? preset.sizes[PRESET_SIZE_NUM_DOCUMENT] : 0;
ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> {
});
}
protected void processDownloadObjects(int type, ArrayList<DownloadObject> objects) { protected void processDownloadObjects(int type, ArrayList<DownloadObject> objects) {
if (objects.isEmpty()) { if (objects.isEmpty()) {
return; return;
} }
ArrayList<DownloadObject> queue = null; ArrayList<DownloadObject> queue = null;
if (type == AUTODOWNLOAD_MASK_PHOTO) { if (type == AUTODOWNLOAD_TYPE_PHOTO) {
queue = photoDownloadQueue; queue = photoDownloadQueue;
} else if (type == AUTODOWNLOAD_MASK_AUDIO) { } else if (type == AUTODOWNLOAD_TYPE_AUDIO) {
queue = audioDownloadQueue; queue = audioDownloadQueue;
} else if (type == AUTODOWNLOAD_MASK_VIDEOMESSAGE) { } else if (type == AUTODOWNLOAD_TYPE_VIDEO) {
queue = videoMessageDownloadQueue;
} else if (type == AUTODOWNLOAD_MASK_VIDEO) {
queue = videoDownloadQueue; queue = videoDownloadQueue;
} else if (type == AUTODOWNLOAD_MASK_DOCUMENT) { } else if (type == AUTODOWNLOAD_TYPE_DOCUMENT) {
queue = documentDownloadQueue; queue = documentDownloadQueue;
} else if (type == AUTODOWNLOAD_MASK_MUSIC) {
queue = musicDownloadQueue;
} else if (type == AUTODOWNLOAD_MASK_GIF) {
queue = gifDownloadQueue;
} }
for (int a = 0; a < objects.size(); a++) { for (int a = 0; a < objects.size(); a++) {
DownloadObject downloadObject = objects.get(a); DownloadObject downloadObject = objects.get(a);
@ -494,26 +742,17 @@ public class DownloadController implements NotificationCenter.NotificationCenter
protected void newDownloadObjectsAvailable(int downloadMask) { protected void newDownloadObjectsAvailable(int downloadMask) {
int mask = getCurrentDownloadMask(); int mask = getCurrentDownloadMask();
if ((mask & AUTODOWNLOAD_MASK_PHOTO) != 0 && (downloadMask & AUTODOWNLOAD_MASK_PHOTO) != 0 && photoDownloadQueue.isEmpty()) { if ((mask & AUTODOWNLOAD_TYPE_PHOTO) != 0 && (downloadMask & AUTODOWNLOAD_TYPE_PHOTO) != 0 && photoDownloadQueue.isEmpty()) {
MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_MASK_PHOTO); MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_TYPE_PHOTO);
} }
if ((mask & AUTODOWNLOAD_MASK_AUDIO) != 0 && (downloadMask & AUTODOWNLOAD_MASK_AUDIO) != 0 && audioDownloadQueue.isEmpty()) { if ((mask & AUTODOWNLOAD_TYPE_AUDIO) != 0 && (downloadMask & AUTODOWNLOAD_TYPE_AUDIO) != 0 && audioDownloadQueue.isEmpty()) {
MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_MASK_AUDIO); MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_TYPE_AUDIO);
} }
if ((mask & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0 && (downloadMask & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0 && videoMessageDownloadQueue.isEmpty()) { if ((mask & AUTODOWNLOAD_TYPE_VIDEO) != 0 && (downloadMask & AUTODOWNLOAD_TYPE_VIDEO) != 0 && videoDownloadQueue.isEmpty()) {
MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_MASK_VIDEOMESSAGE); MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_TYPE_VIDEO);
} }
if ((mask & AUTODOWNLOAD_MASK_VIDEO) != 0 && (downloadMask & AUTODOWNLOAD_MASK_VIDEO) != 0 && videoDownloadQueue.isEmpty()) { if ((mask & AUTODOWNLOAD_TYPE_DOCUMENT) != 0 && (downloadMask & AUTODOWNLOAD_TYPE_DOCUMENT) != 0 && documentDownloadQueue.isEmpty()) {
MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_MASK_VIDEO); MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_TYPE_DOCUMENT);
}
if ((mask & AUTODOWNLOAD_MASK_DOCUMENT) != 0 && (downloadMask & AUTODOWNLOAD_MASK_DOCUMENT) != 0 && documentDownloadQueue.isEmpty()) {
MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_MASK_DOCUMENT);
}
if ((mask & AUTODOWNLOAD_MASK_MUSIC) != 0 && (downloadMask & AUTODOWNLOAD_MASK_MUSIC) != 0 && musicDownloadQueue.isEmpty()) {
MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_MASK_MUSIC);
}
if ((mask & AUTODOWNLOAD_MASK_GIF) != 0 && (downloadMask & AUTODOWNLOAD_MASK_GIF) != 0 && gifDownloadQueue.isEmpty()) {
MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_MASK_GIF);
} }
} }
@ -524,40 +763,25 @@ public class DownloadController implements NotificationCenter.NotificationCenter
if (state == 0 || state == 2) { if (state == 0 || state == 2) {
MessagesStorage.getInstance(currentAccount).removeFromDownloadQueue(downloadObject.id, downloadObject.type, false /*state != 0*/); MessagesStorage.getInstance(currentAccount).removeFromDownloadQueue(downloadObject.id, downloadObject.type, false /*state != 0*/);
} }
if (downloadObject.type == AUTODOWNLOAD_MASK_PHOTO) { if (downloadObject.type == AUTODOWNLOAD_TYPE_PHOTO) {
photoDownloadQueue.remove(downloadObject); photoDownloadQueue.remove(downloadObject);
if (photoDownloadQueue.isEmpty()) { if (photoDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_PHOTO); newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_PHOTO);
} }
} else if (downloadObject.type == AUTODOWNLOAD_MASK_AUDIO) { } else if (downloadObject.type == AUTODOWNLOAD_TYPE_AUDIO) {
audioDownloadQueue.remove(downloadObject); audioDownloadQueue.remove(downloadObject);
if (audioDownloadQueue.isEmpty()) { if (audioDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_AUDIO); newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_AUDIO);
} }
} else if (downloadObject.type == AUTODOWNLOAD_MASK_VIDEOMESSAGE) { } else if (downloadObject.type == AUTODOWNLOAD_TYPE_VIDEO) {
videoMessageDownloadQueue.remove(downloadObject);
if (videoMessageDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_VIDEOMESSAGE);
}
} else if (downloadObject.type == AUTODOWNLOAD_MASK_VIDEO) {
videoDownloadQueue.remove(downloadObject); videoDownloadQueue.remove(downloadObject);
if (videoDownloadQueue.isEmpty()) { if (videoDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_VIDEO); newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_VIDEO);
} }
} else if (downloadObject.type == AUTODOWNLOAD_MASK_DOCUMENT) { } else if (downloadObject.type == AUTODOWNLOAD_TYPE_DOCUMENT) {
documentDownloadQueue.remove(downloadObject); documentDownloadQueue.remove(downloadObject);
if (documentDownloadQueue.isEmpty()) { if (documentDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_DOCUMENT); newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_DOCUMENT);
}
} else if (downloadObject.type == AUTODOWNLOAD_MASK_MUSIC) {
musicDownloadQueue.remove(downloadObject);
if (musicDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_MUSIC);
}
} else if (downloadObject.type == AUTODOWNLOAD_MASK_GIF) {
gifDownloadQueue.remove(downloadObject);
if (gifDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_GIF);
} }
} }
} }

View File

@ -9,6 +9,7 @@
package org.telegram.messenger; package org.telegram.messenger;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.SparseIntArray;
import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.NativeByteBuffer; import org.telegram.tgnet.NativeByteBuffer;
@ -43,7 +44,19 @@ public class FileLoadOperation {
} }
} }
private ArrayList<FileStreamLoadOperation> streamListeners; private static class PreloadRange {
private int fileOffset;
private int start;
private int length;
private PreloadRange(int o, int s, int l) {
fileOffset = o;
start = s;
length = l;
}
}
private ArrayList<FileLoadOperationStream> streamListeners;
private final static int stateIdle = 0; private final static int stateIdle = 0;
private final static int stateDownloading = 1; private final static int stateDownloading = 1;
@ -58,6 +71,27 @@ public class FileLoadOperation {
private final static int bigFileSizeFrom = 1024 * 1024; private final static int bigFileSizeFrom = 1024 * 1024;
private final static int maxCdnParts = 1024 * 1024 * 1536 / downloadChunkSizeBig; private final static int maxCdnParts = 1024 * 1024 * 1536 / downloadChunkSizeBig;
private final static int preloadMaxBytes = 2 * 1024 * 1024;
private SparseArray<PreloadRange> preloadedBytesRanges;
private SparseIntArray requestedPreloadedBytesRanges;
private RandomAccessFile preloadStream;
private int preloadStreamFileOffset;
private int totalPreloadedBytes;
private boolean isPreloadVideoOperation;
private boolean preloadFinished;
private File cacheFilePreload;
private boolean supportsPreloading;
private int nextPreloadDownloadOffset;
private int nextAtomOffset;
private int foundMoovSize;
private int preloadNotRequestedBytesCount;
private int moovFound;
private byte[] preloadTempBuffer = new byte[16];
private int preloadTempBufferCount;
private boolean nextPartWasPreloaded;
private ArrayList<Range> notLoadedBytesRanges; private ArrayList<Range> notLoadedBytesRanges;
private volatile ArrayList<Range> notLoadedBytesRangesCopy; private volatile ArrayList<Range> notLoadedBytesRangesCopy;
private ArrayList<Range> notRequestedBytesRanges; private ArrayList<Range> notRequestedBytesRanges;
@ -177,7 +211,7 @@ public class FileLoadOperation {
webLocation = webDocument.location; webLocation = webDocument.location;
totalBytesCount = webDocument.size; totalBytesCount = webDocument.size;
initialDatacenterId = datacenterId = MessagesController.getInstance(currentAccount).webFileDatacenterId; initialDatacenterId = datacenterId = MessagesController.getInstance(currentAccount).webFileDatacenterId;
String defaultExt = FileLoader.getExtensionByMime(webDocument.mime_type); String defaultExt = FileLoader.getMimeTypePart(webDocument.mime_type);
if (webDocument.mime_type.startsWith("image/")) { if (webDocument.mime_type.startsWith("image/")) {
currentType = ConnectionsManager.FileTypePhoto; currentType = ConnectionsManager.FileTypePhoto;
} else if (webDocument.mime_type.equals("audio/ogg")) { } else if (webDocument.mime_type.equals("audio/ogg")) {
@ -212,6 +246,12 @@ public class FileLoadOperation {
} }
initialDatacenterId = datacenterId = documentLocation.dc_id; initialDatacenterId = datacenterId = documentLocation.dc_id;
allowDisordererFileSave = true; allowDisordererFileSave = true;
for (int a = 0, N = documentLocation.attributes.size(); a < N; a++) {
if (documentLocation.attributes.get(a) instanceof TLRPC.TL_documentAttributeVideo) {
supportsPreloading = true;
break;
}
}
} }
totalBytesCount = documentLocation.size; totalBytesCount = documentLocation.size;
if (key != null) { if (key != null) {
@ -230,27 +270,13 @@ public class FileLoadOperation {
} }
if ("audio/ogg".equals(documentLocation.mime_type)) { if ("audio/ogg".equals(documentLocation.mime_type)) {
currentType = ConnectionsManager.FileTypeAudio; currentType = ConnectionsManager.FileTypeAudio;
} else if ("video/mp4".equals(documentLocation.mime_type)) { } else if (FileLoader.isVideoMimeType(documentLocation.mime_type)) {
currentType = ConnectionsManager.FileTypeVideo; currentType = ConnectionsManager.FileTypeVideo;
} else { } else {
currentType = ConnectionsManager.FileTypeFile; currentType = ConnectionsManager.FileTypeFile;
} }
if (ext.length() <= 1) { if (ext.length() <= 1) {
if (documentLocation.mime_type != null) { ext = FileLoader.getExtensionByMimeType(documentLocation.mime_type);
switch (documentLocation.mime_type) {
case "video/mp4":
ext = ".mp4";
break;
case "audio/ogg":
ext = ".ogg";
break;
default:
ext = "";
break;
}
} else {
ext = "";
}
} }
} catch (Exception e) { } catch (Exception e) {
FileLog.e(e); FileLog.e(e);
@ -292,7 +318,7 @@ public class FileLoadOperation {
} }
public boolean wasStarted() { public boolean wasStarted() {
return started; return started && !paused;
} }
public int getCurrentType() { public int getCurrentType() {
@ -364,9 +390,6 @@ public class FileLoadOperation {
filePartsStream.writeInt(count); filePartsStream.writeInt(count);
for (int a = 0; a < count; a++) { for (int a = 0; a < count; a++) {
range = ranges.get(a); range = ranges.get(a);
/*if (BuildVars.LOGS_ENABLED) {
FileLog.d(cacheFileFinal + " save not loaded part " + range.start + " - " + range.end);
}*/
filePartsStream.writeInt(range.start); filePartsStream.writeInt(range.start);
filePartsStream.writeInt(range.end); filePartsStream.writeInt(range.end);
} }
@ -454,8 +477,8 @@ public class FileLoadOperation {
}); });
try { try {
countDownLatch.await(); countDownLatch.await();
} catch (Exception e) { } catch (Exception ignore) {
FileLog.e(e);
} }
return result[0]; return result[0];
} }
@ -477,7 +500,7 @@ public class FileLoadOperation {
}); });
} }
private void copytNotLoadedRanges() { private void copyNotLoadedRanges() {
if (notLoadedBytesRanges == null) { if (notLoadedBytesRanges == null) {
return; return;
} }
@ -488,14 +511,14 @@ public class FileLoadOperation {
if (state != stateDownloading) { if (state != stateDownloading) {
return; return;
} }
Utilities.stageQueue.postRunnable(() -> paused = true); paused = true;
} }
public boolean start() { public boolean start() {
return start(null, 0); return start(null, 0);
} }
public boolean start(final FileStreamLoadOperation stream, final int streamOffset) { public boolean start(final FileLoadOperationStream stream, final int streamOffset) {
if (currentDownloadChunkSize == 0) { if (currentDownloadChunkSize == 0) {
currentDownloadChunkSize = totalBytesCount >= bigFileSizeFrom ? downloadChunkSizeBig : downloadChunkSize; currentDownloadChunkSize = totalBytesCount >= bigFileSizeFrom ? downloadChunkSizeBig : downloadChunkSize;
currentMaxDownloadRequests = totalBytesCount >= bigFileSizeFrom ? maxDownloadRequestsBig : maxDownloadRequests; currentMaxDownloadRequests = totalBytesCount >= bigFileSizeFrom ? maxDownloadRequestsBig : maxDownloadRequests;
@ -511,8 +534,13 @@ public class FileLoadOperation {
streamStartOffset = streamOffset / currentDownloadChunkSize * currentDownloadChunkSize; streamStartOffset = streamOffset / currentDownloadChunkSize * currentDownloadChunkSize;
streamListeners.add(stream); streamListeners.add(stream);
if (alreadyStarted) { if (alreadyStarted) {
//clearOperaion(null); if (preloadedBytesRanges != null && getDownloadedLengthFromOffsetInternal(notLoadedBytesRanges, streamStartOffset, 1) == 0) {
if (preloadedBytesRanges.get(streamStartOffset) != null) {
nextPartWasPreloaded = true;
}
}
startDownloadRequest(); startDownloadRequest();
nextPartWasPreloaded = false;
} }
}); });
} else if (wasPaused && alreadyStarted) { } else if (wasPaused && alreadyStarted) {
@ -536,6 +564,7 @@ public class FileLoadOperation {
String fileNameFinal; String fileNameFinal;
String fileNameTemp; String fileNameTemp;
String fileNameParts = null; String fileNameParts = null;
String fileNamePreload = null;
String fileNameIv = null; String fileNameIv = null;
if (webLocation != null) { if (webLocation != null) {
String md5 = Utilities.MD5(webFile.url); String md5 = Utilities.MD5(webFile.url);
@ -574,6 +603,7 @@ public class FileLoadOperation {
if (notLoadedBytesRanges != null) { if (notLoadedBytesRanges != null) {
fileNameParts = location.volume_id + "_" + location.local_id + ".pt"; fileNameParts = location.volume_id + "_" + location.local_id + ".pt";
} }
fileNamePreload = location.volume_id + "_" + location.local_id + ".preload";
} }
} else { } else {
if (datacenterId == 0 || location.id == 0) { if (datacenterId == 0 || location.id == 0) {
@ -595,6 +625,7 @@ public class FileLoadOperation {
if (notLoadedBytesRanges != null) { if (notLoadedBytesRanges != null) {
fileNameParts = datacenterId + "_" + location.id + ".pt"; fileNameParts = datacenterId + "_" + location.id + ".pt";
} }
fileNamePreload = datacenterId + "_" + location.id + ".preload";
} }
} }
} }
@ -642,6 +673,82 @@ public class FileLoadOperation {
} }
} }
boolean[] preloaded = new boolean[]{false};
if (supportsPreloading && fileNamePreload != null) {
cacheFilePreload = new File(tempPath, fileNamePreload);
boolean closeStream = false;
try {
preloadStream = new RandomAccessFile(cacheFilePreload, "rws");
long len = preloadStream.length();
int readOffset = 0;
preloadStreamFileOffset = 1;
if (len - readOffset > 1) {
preloaded[0] = preloadStream.readByte() != 0;
readOffset += 1;
while (readOffset < len) {
if (len - readOffset < 4) {
break;
}
int offset = preloadStream.readInt();
readOffset += 4;
if (len - readOffset < 4 || offset < 0 || offset > totalBytesCount) {
break;
}
int size = preloadStream.readInt();
readOffset += 4;
if (len - readOffset < size || size > currentDownloadChunkSize) {
break;
}
PreloadRange range = new PreloadRange(readOffset, offset, size);
readOffset += size;
preloadStream.seek(readOffset);
if (len - readOffset < 12) {
break;
}
foundMoovSize = preloadStream.readInt();
if (foundMoovSize != 0) {
moovFound = nextPreloadDownloadOffset > totalBytesCount / 2 ? 2 : 1;
preloadNotRequestedBytesCount = foundMoovSize;
}
nextPreloadDownloadOffset = preloadStream.readInt();
nextAtomOffset = preloadStream.readInt();
readOffset += 12;
if (preloadedBytesRanges == null) {
preloadedBytesRanges = new SparseArray<>();
}
if (requestedPreloadedBytesRanges == null) {
requestedPreloadedBytesRanges = new SparseIntArray();
}
preloadedBytesRanges.put(offset, range);
requestedPreloadedBytesRanges.put(offset, 1);
totalPreloadedBytes += size;
preloadStreamFileOffset += 20 + size;
}
}
preloadStream.seek(preloadStreamFileOffset);
} catch (Exception e) {
FileLog.e(e);
}
if (!isPreloadVideoOperation && preloadedBytesRanges == null) {
cacheFilePreload = null;
try {
if (preloadStream != null) {
try {
preloadStream.getChannel().close();
} catch (Exception e) {
FileLog.e(e);
}
preloadStream.close();
preloadStream = null;
}
} catch (Exception e) {
FileLog.e(e);
}
}
}
if (fileNameParts != null) { if (fileNameParts != null) {
cacheFileParts = new File(tempPath, fileNameParts); cacheFileParts = new File(tempPath, fileNameParts);
try { try {
@ -691,10 +798,15 @@ public class FileLoadOperation {
range = notLoadedBytesRanges.get(a); range = notLoadedBytesRanges.get(a);
downloadedBytes -= (range.end - range.start); downloadedBytes -= (range.end - range.start);
} }
requestedBytesCount = downloadedBytes;
} }
if (BuildVars.LOGS_ENABLED) { if (BuildVars.LOGS_ENABLED) {
FileLog.d("start loading file to temp = " + cacheFileTemp + " final = " + cacheFileFinal); if (isPreloadVideoOperation) {
FileLog.d("start preloading file to temp = " + cacheFileTemp);
} else {
FileLog.d("start loading file to temp = " + cacheFileTemp + " final = " + cacheFileFinal);
}
} }
if (fileNameIv != null) { if (fileNameIv != null) {
@ -714,8 +826,8 @@ public class FileLoadOperation {
requestedBytesCount = downloadedBytes = 0; requestedBytesCount = downloadedBytes = 0;
} }
} }
if (downloadedBytes != 0 && totalBytesCount > 0) { if (!isPreloadVideoOperation && downloadedBytes != 0 && totalBytesCount > 0) {
copytNotLoadedRanges(); copyNotLoadedRanges();
delegate.didChangedLoadProgress(FileLoadOperation.this, Math.min(1.0f, (float) downloadedBytes / (float) totalBytesCount)); delegate.didChangedLoadProgress(FileLoadOperation.this, Math.min(1.0f, (float) downloadedBytes / (float) totalBytesCount));
} }
try { try {
@ -732,7 +844,7 @@ public class FileLoadOperation {
} }
started = true; started = true;
Utilities.stageQueue.postRunnable(() -> { Utilities.stageQueue.postRunnable(() -> {
if (totalBytesCount != 0 && downloadedBytes == totalBytesCount) { if (totalBytesCount != 0 && (isPreloadVideoOperation && preloaded[0] || downloadedBytes == totalBytesCount)) {
try { try {
onFinishLoadingFile(false); onFinishLoadingFile(false);
} catch (Exception e) { } catch (Exception e) {
@ -757,6 +869,39 @@ public class FileLoadOperation {
return paused; return paused;
} }
public void setIsPreloadVideoOperation(boolean value) {
if (isPreloadVideoOperation == value || value && totalBytesCount <= preloadMaxBytes) {
return;
}
if (!value && isPreloadVideoOperation) {
if (state == stateFinished) {
isPreloadVideoOperation = value;
state = stateIdle;
preloadFinished = false;
start();
} else if (state == stateDownloading) {
Utilities.stageQueue.postRunnable(() -> {
requestedBytesCount = 0;
clearOperaion(null, true);
isPreloadVideoOperation = value;
startDownloadRequest();
});
} else {
isPreloadVideoOperation = value;
}
} else {
isPreloadVideoOperation = value;
}
}
public boolean isPreloadVideoOperation() {
return isPreloadVideoOperation;
}
public boolean isPreloadFinished() {
return preloadFinished;
}
public void cancel() { public void cancel() {
Utilities.stageQueue.postRunnable(() -> { Utilities.stageQueue.postRunnable(() -> {
if (state == stateFinished || state == stateFailed) { if (state == stateFinished || state == stateFailed) {
@ -788,6 +933,19 @@ public class FileLoadOperation {
} catch (Exception e) { } catch (Exception e) {
FileLog.e(e); FileLog.e(e);
} }
try {
if (preloadStream != null) {
try {
preloadStream.getChannel().close();
} catch (Exception e) {
FileLog.e(e);
}
preloadStream.close();
preloadStream = null;
}
} catch (Exception e) {
FileLog.e(e);
}
try { try {
if (fileReadStream != null) { if (fileReadStream != null) {
try { try {
@ -847,50 +1005,61 @@ public class FileLoadOperation {
} }
state = stateFinished; state = stateFinished;
cleanup(); cleanup();
if (cacheIvTemp != null) { if (isPreloadVideoOperation) {
cacheIvTemp.delete(); preloadFinished = true;
cacheIvTemp = null; if (BuildVars.DEBUG_VERSION) {
} FileLog.d("finished preloading file to " + cacheFileTemp + " loaded " + totalPreloadedBytes + " of " + totalBytesCount);
if (cacheFileParts != null) { }
cacheFileParts.delete(); } else {
cacheFileParts = null; if (cacheIvTemp != null) {
} cacheIvTemp.delete();
if (cacheFileTemp != null) { cacheIvTemp = null;
boolean renameResult = cacheFileTemp.renameTo(cacheFileFinal); }
if (!renameResult) { if (cacheFileParts != null) {
if (BuildVars.LOGS_ENABLED) { cacheFileParts.delete();
FileLog.e("unable to rename temp = " + cacheFileTemp + " to final = " + cacheFileFinal + " retry = " + renameRetryCount); cacheFileParts = null;
} }
renameRetryCount++; if (cacheFilePreload != null) {
if (renameRetryCount < 3) { cacheFilePreload.delete();
state = stateDownloading; cacheFilePreload = null;
Utilities.stageQueue.postRunnable(() -> { }
try { if (cacheFileTemp != null) {
onFinishLoadingFile(increment); boolean renameResult = cacheFileTemp.renameTo(cacheFileFinal);
} catch (Exception e) { if (!renameResult) {
onFail(false, 0); if (BuildVars.LOGS_ENABLED) {
} FileLog.e("unable to rename temp = " + cacheFileTemp + " to final = " + cacheFileFinal + " retry = " + renameRetryCount);
}, 200); }
return; renameRetryCount++;
} if (renameRetryCount < 3) {
cacheFileFinal = cacheFileTemp; state = stateDownloading;
Utilities.stageQueue.postRunnable(() -> {
try {
onFinishLoadingFile(increment);
} catch (Exception e) {
onFail(false, 0);
}
}, 200);
return;
}
cacheFileFinal = cacheFileTemp;
}
}
if (BuildVars.LOGS_ENABLED) {
FileLog.d("finished downloading file to " + cacheFileFinal);
}
if (increment) {
if (currentType == ConnectionsManager.FileTypeAudio) {
StatsController.getInstance(currentAccount).incrementReceivedItemsCount(ApplicationLoader.getCurrentNetworkType(), StatsController.TYPE_AUDIOS, 1);
} else if (currentType == ConnectionsManager.FileTypeVideo) {
StatsController.getInstance(currentAccount).incrementReceivedItemsCount(ApplicationLoader.getCurrentNetworkType(), StatsController.TYPE_VIDEOS, 1);
} else if (currentType == ConnectionsManager.FileTypePhoto) {
StatsController.getInstance(currentAccount).incrementReceivedItemsCount(ApplicationLoader.getCurrentNetworkType(), StatsController.TYPE_PHOTOS, 1);
} else if (currentType == ConnectionsManager.FileTypeFile) {
StatsController.getInstance(currentAccount).incrementReceivedItemsCount(ApplicationLoader.getCurrentNetworkType(), StatsController.TYPE_FILES, 1);
}
} }
}
if (BuildVars.LOGS_ENABLED) {
FileLog.d("finished downloading file to " + cacheFileFinal);
} }
delegate.didFinishLoadingFile(FileLoadOperation.this, cacheFileFinal); delegate.didFinishLoadingFile(FileLoadOperation.this, cacheFileFinal);
if (increment) {
if (currentType == ConnectionsManager.FileTypeAudio) {
StatsController.getInstance(currentAccount).incrementReceivedItemsCount(ApplicationLoader.getCurrentNetworkType(), StatsController.TYPE_AUDIOS, 1);
} else if (currentType == ConnectionsManager.FileTypeVideo) {
StatsController.getInstance(currentAccount).incrementReceivedItemsCount(ApplicationLoader.getCurrentNetworkType(), StatsController.TYPE_VIDEOS, 1);
} else if (currentType == ConnectionsManager.FileTypePhoto) {
StatsController.getInstance(currentAccount).incrementReceivedItemsCount(ApplicationLoader.getCurrentNetworkType(), StatsController.TYPE_PHOTOS, 1);
} else if (currentType == ConnectionsManager.FileTypeFile) {
StatsController.getInstance(currentAccount).incrementReceivedItemsCount(ApplicationLoader.getCurrentNetworkType(), StatsController.TYPE_FILES, 1);
}
}
} }
private void delayRequestInfo(RequestInfo requestInfo) { private void delayRequestInfo(RequestInfo requestInfo) {
@ -904,6 +1073,42 @@ public class FileLoadOperation {
} }
} }
private int findNextPreloadDownloadOffset(int atomOffset, int partOffset, NativeByteBuffer partBuffer) {
int partSize = partBuffer.limit();
while (true) {
if (atomOffset < partOffset - (preloadTempBuffer != null ? 16 : 0) || atomOffset >= partOffset + partSize) {
return 0;
}
if (atomOffset >= partOffset + partSize - 16) {
preloadTempBufferCount = partOffset + partSize - atomOffset;
partBuffer.position(partBuffer.limit() - preloadTempBufferCount);
partBuffer.readBytes(preloadTempBuffer, 0, preloadTempBufferCount, false);
return partOffset + partSize;
}
if (preloadTempBufferCount != 0) {
partBuffer.position(0);
partBuffer.readBytes(preloadTempBuffer, preloadTempBufferCount, 16 - preloadTempBufferCount, false);
preloadTempBufferCount = 0;
} else {
partBuffer.position(atomOffset - partOffset);
partBuffer.readBytes(preloadTempBuffer, 0, 16, false);
}
int atomSize = (((int) preloadTempBuffer[0] & 0xFF) << 24) + (((int) preloadTempBuffer[1] & 0xFF) << 16) + (((int) preloadTempBuffer[2] & 0xFF) << 8) + ((int) preloadTempBuffer[3] & 0xFF);
if (atomSize == 0) {
return 0;
} else if (atomSize == 1) {
atomSize = (((int) preloadTempBuffer[12] & 0xFF) << 24) + (((int) preloadTempBuffer[13] & 0xFF) << 16) + (((int) preloadTempBuffer[14] & 0xFF) << 8) + ((int) preloadTempBuffer[15] & 0xFF);
}
if (preloadTempBuffer[4] == 'm' && preloadTempBuffer[5] == 'o' && preloadTempBuffer[6] == 'o' && preloadTempBuffer[7] == 'v') {
return -atomSize;
}
if (atomSize + atomOffset >= partOffset + partSize) {
return atomSize + atomOffset;
}
atomOffset += atomSize;
}
}
private void requestFileOffsets(int offset) { private void requestFileOffsets(int offset) {
if (requestingCdnOffsets) { if (requestingCdnOffsets) {
return; return;
@ -999,85 +1204,134 @@ public class FileLoadOperation {
Utilities.aesCtrDecryption(bytes.buffer, cdnKey, cdnIv, 0, bytes.limit()); Utilities.aesCtrDecryption(bytes.buffer, cdnKey, cdnIv, 0, bytes.limit());
} }
downloadedBytes += currentBytesSize;
boolean finishedDownloading; boolean finishedDownloading;
if (totalBytesCount > 0) { if (isPreloadVideoOperation) {
finishedDownloading = downloadedBytes >= totalBytesCount; preloadStream.writeInt(requestInfo.offset);
} else { preloadStream.writeInt(currentBytesSize);
finishedDownloading = currentBytesSize != currentDownloadChunkSize || (totalBytesCount == downloadedBytes || downloadedBytes % currentDownloadChunkSize != 0) && (totalBytesCount <= 0 || totalBytesCount <= downloadedBytes); preloadStreamFileOffset += 8;
} FileChannel channel = preloadStream.getChannel();
if (key != null) { channel.write(bytes.buffer);
Utilities.aesIgeEncryption(bytes.buffer, key, iv, false, true, 0, bytes.limit());
if (finishedDownloading && bytesCountPadding != 0) {
bytes.limit(bytes.limit() - bytesCountPadding);
}
}
if (encryptFile) {
int offset = requestInfo.offset / 16;
encryptIv[15] = (byte) (offset & 0xff);
encryptIv[14] = (byte) ((offset >> 8) & 0xff);
encryptIv[13] = (byte) ((offset >> 16) & 0xff);
encryptIv[12] = (byte) ((offset >> 24) & 0xff);
Utilities.aesCtrDecryption(bytes.buffer, encryptKey, encryptIv, 0, bytes.limit());
}
if (notLoadedBytesRanges != null) {
fileOutputStream.seek(requestInfo.offset);
if (BuildVars.DEBUG_VERSION) { if (BuildVars.DEBUG_VERSION) {
FileLog.d("save file part " + cacheFileFinal + " offset " + requestInfo.offset); FileLog.d("save preload file part " + cacheFilePreload + " offset " + requestInfo.offset + " size " + currentBytesSize);
} }
} if (preloadedBytesRanges == null) {
FileChannel channel = fileOutputStream.getChannel(); preloadedBytesRanges = new SparseArray<>();
channel.write(bytes.buffer); }
addPart(notLoadedBytesRanges, requestInfo.offset, requestInfo.offset + currentBytesSize, true); preloadedBytesRanges.put(requestInfo.offset, new PreloadRange(preloadStreamFileOffset, requestInfo.offset, currentBytesSize));
if (isCdn) {
int cdnCheckPart = requestInfo.offset / cdnChunkCheckSize;
int size = notCheckedCdnRanges.size(); totalPreloadedBytes += currentBytesSize;
Range range; preloadStreamFileOffset += currentBytesSize;
boolean checked = true;
for (int a = 0; a < size; a++) { if (moovFound == 0) {
range = notCheckedCdnRanges.get(a); int offset = findNextPreloadDownloadOffset(nextAtomOffset, requestInfo.offset, bytes);
if (range.start <= cdnCheckPart && cdnCheckPart <= range.end) { if (offset < 0) {
checked = false; offset *= -1;
break; nextPreloadDownloadOffset += currentDownloadChunkSize;
if (nextPreloadDownloadOffset < totalBytesCount / 2) {
preloadNotRequestedBytesCount = foundMoovSize = preloadMaxBytes / 2 + offset;
moovFound = 1;
} else {
preloadNotRequestedBytesCount = foundMoovSize = preloadMaxBytes;
moovFound = 2;
}
nextPreloadDownloadOffset = -1;
} else {
nextPreloadDownloadOffset = offset / currentDownloadChunkSize * currentDownloadChunkSize;
}
nextAtomOffset = offset;
}
preloadStream.writeInt(foundMoovSize);
preloadStream.writeInt(nextPreloadDownloadOffset);
preloadStream.writeInt(nextAtomOffset);
preloadStreamFileOffset += 12;
finishedDownloading = nextPreloadDownloadOffset == 0 || moovFound != 0 && foundMoovSize < 0 || totalPreloadedBytes > preloadMaxBytes || nextPreloadDownloadOffset >= totalBytesCount;
if (finishedDownloading) {
preloadStream.seek(0);
preloadStream.write((byte) 1);
} else if (moovFound != 0) {
foundMoovSize -= currentDownloadChunkSize;
}
} else {
downloadedBytes += currentBytesSize;
if (totalBytesCount > 0) {
finishedDownloading = downloadedBytes >= totalBytesCount;
} else {
finishedDownloading = currentBytesSize != currentDownloadChunkSize || (totalBytesCount == downloadedBytes || downloadedBytes % currentDownloadChunkSize != 0) && (totalBytesCount <= 0 || totalBytesCount <= downloadedBytes);
}
if (key != null) {
Utilities.aesIgeEncryption(bytes.buffer, key, iv, false, true, 0, bytes.limit());
if (finishedDownloading && bytesCountPadding != 0) {
bytes.limit(bytes.limit() - bytesCountPadding);
} }
} }
if (!checked) { if (encryptFile) {
int fileOffset = cdnCheckPart * cdnChunkCheckSize; int offset = requestInfo.offset / 16;
int availableSize = getDownloadedLengthFromOffsetInternal(notLoadedBytesRanges, fileOffset, cdnChunkCheckSize); encryptIv[15] = (byte) (offset & 0xff);
if (availableSize != 0 && (availableSize == cdnChunkCheckSize || totalBytesCount > 0 && availableSize == totalBytesCount - fileOffset || totalBytesCount <= 0 && finishedDownloading)) { encryptIv[14] = (byte) ((offset >> 8) & 0xff);
TLRPC.TL_fileHash hash = cdnHashes.get(fileOffset); encryptIv[13] = (byte) ((offset >> 16) & 0xff);
if (fileReadStream == null) { encryptIv[12] = (byte) ((offset >> 24) & 0xff);
cdnCheckBytes = new byte[cdnChunkCheckSize]; Utilities.aesCtrDecryption(bytes.buffer, encryptKey, encryptIv, 0, bytes.limit());
fileReadStream = new RandomAccessFile(cacheFileTemp, "r"); }
if (notLoadedBytesRanges != null) {
fileOutputStream.seek(requestInfo.offset);
if (BuildVars.DEBUG_VERSION) {
FileLog.d("save file part " + cacheFileFinal + " offset " + requestInfo.offset);
}
}
FileChannel channel = fileOutputStream.getChannel();
channel.write(bytes.buffer);
addPart(notLoadedBytesRanges, requestInfo.offset, requestInfo.offset + currentBytesSize, true);
if (isCdn) {
int cdnCheckPart = requestInfo.offset / cdnChunkCheckSize;
int size = notCheckedCdnRanges.size();
Range range;
boolean checked = true;
for (int a = 0; a < size; a++) {
range = notCheckedCdnRanges.get(a);
if (range.start <= cdnCheckPart && cdnCheckPart <= range.end) {
checked = false;
break;
} }
fileReadStream.seek(fileOffset); }
fileReadStream.readFully(cdnCheckBytes, 0, availableSize); if (!checked) {
byte[] sha256 = Utilities.computeSHA256(cdnCheckBytes, 0, availableSize); int fileOffset = cdnCheckPart * cdnChunkCheckSize;
if (!Arrays.equals(sha256, hash.hash)) { int availableSize = getDownloadedLengthFromOffsetInternal(notLoadedBytesRanges, fileOffset, cdnChunkCheckSize);
if (BuildVars.LOGS_ENABLED) { if (availableSize != 0 && (availableSize == cdnChunkCheckSize || totalBytesCount > 0 && availableSize == totalBytesCount - fileOffset || totalBytesCount <= 0 && finishedDownloading)) {
if (location != null) { TLRPC.TL_fileHash hash = cdnHashes.get(fileOffset);
FileLog.e("invalid cdn hash " + location + " id = " + location.id + " local_id = " + location.local_id + " access_hash = " + location.access_hash + " volume_id = " + location.volume_id + " secret = " + location.secret); if (fileReadStream == null) {
} else if (webLocation != null) { cdnCheckBytes = new byte[cdnChunkCheckSize];
FileLog.e("invalid cdn hash " + webLocation + " id = " + getFileName()); fileReadStream = new RandomAccessFile(cacheFileTemp, "r");
}
} }
onFail(false, 0); fileReadStream.seek(fileOffset);
cacheFileTemp.delete(); fileReadStream.readFully(cdnCheckBytes, 0, availableSize);
return false; byte[] sha256 = Utilities.computeSHA256(cdnCheckBytes, 0, availableSize);
if (!Arrays.equals(sha256, hash.hash)) {
if (BuildVars.LOGS_ENABLED) {
if (location != null) {
FileLog.e("invalid cdn hash " + location + " id = " + location.id + " local_id = " + location.local_id + " access_hash = " + location.access_hash + " volume_id = " + location.volume_id + " secret = " + location.secret);
} else if (webLocation != null) {
FileLog.e("invalid cdn hash " + webLocation + " id = " + getFileName());
}
}
onFail(false, 0);
cacheFileTemp.delete();
return false;
}
cdnHashes.remove(fileOffset);
addPart(notCheckedCdnRanges, cdnCheckPart, cdnCheckPart + 1, false);
} }
cdnHashes.remove(fileOffset);
addPart(notCheckedCdnRanges, cdnCheckPart, cdnCheckPart + 1, false);
} }
} }
} if (fiv != null) {
if (fiv != null) { fiv.seek(0);
fiv.seek(0); fiv.write(iv);
fiv.write(iv); }
} if (totalBytesCount > 0 && state == stateDownloading) {
if (totalBytesCount > 0 && state == stateDownloading) { copyNotLoadedRanges();
copytNotLoadedRanges(); delegate.didChangedLoadProgress(FileLoadOperation.this, Math.min(1.0f, (float) downloadedBytes / (float) totalBytesCount));
delegate.didChangedLoadProgress(FileLoadOperation.this, Math.min(1.0f, (float) downloadedBytes / (float) totalBytesCount)); }
} }
for (int a = 0; a < delayedRequestInfos.size(); a++) { for (int a = 0; a < delayedRequestInfos.size(); a++) {
@ -1164,12 +1418,16 @@ public class FileLoadOperation {
} }
} }
private void clearOperaion(RequestInfo currentInfo) { private void clearOperaion(RequestInfo currentInfo, boolean preloadChanged) {
int minOffset = Integer.MAX_VALUE; int minOffset = Integer.MAX_VALUE;
for (int a = 0; a < requestInfos.size(); a++) { for (int a = 0; a < requestInfos.size(); a++) {
RequestInfo info = requestInfos.get(a); RequestInfo info = requestInfos.get(a);
minOffset = Math.min(info.offset, minOffset); minOffset = Math.min(info.offset, minOffset);
removePart(notRequestedBytesRanges, info.offset, info.offset + currentDownloadChunkSize); if (isPreloadVideoOperation) {
requestedPreloadedBytesRanges.delete(info.offset);
} else {
removePart(notRequestedBytesRanges, info.offset, info.offset + currentDownloadChunkSize);
}
if (currentInfo == info) { if (currentInfo == info) {
continue; continue;
} }
@ -1180,7 +1438,11 @@ public class FileLoadOperation {
requestInfos.clear(); requestInfos.clear();
for (int a = 0; a < delayedRequestInfos.size(); a++) { for (int a = 0; a < delayedRequestInfos.size(); a++) {
RequestInfo info = delayedRequestInfos.get(a); RequestInfo info = delayedRequestInfos.get(a);
removePart(notRequestedBytesRanges, info.offset, info.offset + currentDownloadChunkSize); if (isPreloadVideoOperation) {
requestedPreloadedBytesRanges.delete(info.offset);
} else {
removePart(notRequestedBytesRanges, info.offset, info.offset + currentDownloadChunkSize);
}
if (info.response != null) { if (info.response != null) {
info.response.disableFree = false; info.response.disableFree = false;
info.response.freeResources(); info.response.freeResources();
@ -1195,7 +1457,9 @@ public class FileLoadOperation {
} }
delayedRequestInfos.clear(); delayedRequestInfos.clear();
requestsCount = 0; requestsCount = 0;
if (notLoadedBytesRanges == null) { if (!preloadChanged && isPreloadVideoOperation) {
requestedBytesCount = totalPreloadedBytes;
} else if (notLoadedBytesRanges == null) {
requestedBytesCount = downloadedBytes = minOffset; requestedBytesCount = downloadedBytes = minOffset;
} }
} }
@ -1204,7 +1468,7 @@ public class FileLoadOperation {
if (requestingReference) { if (requestingReference) {
return; return;
} }
clearOperaion(requestInfo); clearOperaion(requestInfo, false);
requestingReference = true; requestingReference = true;
if (parentObject instanceof MessageObject) { if (parentObject instanceof MessageObject) {
MessageObject messageObject = (MessageObject) parentObject; MessageObject messageObject = (MessageObject) parentObject;
@ -1216,45 +1480,86 @@ public class FileLoadOperation {
} }
protected void startDownloadRequest() { protected void startDownloadRequest() {
if (paused || state != stateDownloading || requestInfos.size() + delayedRequestInfos.size() >= currentMaxDownloadRequests) { if (paused ||
state != stateDownloading ||
!nextPartWasPreloaded && (requestInfos.size() + delayedRequestInfos.size() >= currentMaxDownloadRequests) ||
isPreloadVideoOperation && (requestedBytesCount > preloadMaxBytes || moovFound != 0 && requestInfos.size() > 0)) {
return; return;
} }
int count = 1; int count = 1;
if (totalBytesCount > 0) { if (!nextPartWasPreloaded && (!isPreloadVideoOperation || moovFound != 0) && totalBytesCount > 0) {
count = Math.max(0, currentMaxDownloadRequests - requestInfos.size()); count = Math.max(0, currentMaxDownloadRequests - requestInfos.size());
} }
for (int a = 0; a < count; a++) { for (int a = 0; a < count; a++) {
int downloadOffset; int downloadOffset;
if (notRequestedBytesRanges != null) { if (isPreloadVideoOperation) {
int size = notRequestedBytesRanges.size(); if (moovFound != 0 && preloadNotRequestedBytesCount <= 0) {
int minStart = Integer.MAX_VALUE; return;
int minStreamStart = Integer.MAX_VALUE; }
for (int b = 0; b < size; b++) { if (nextPreloadDownloadOffset == -1) {
Range range = notRequestedBytesRanges.get(b); downloadOffset = 0;
if (streamStartOffset != 0) { boolean found = false;
if (range.start <= streamStartOffset && range.end > streamStartOffset) { int tries = preloadMaxBytes / currentDownloadChunkSize + 2;
minStreamStart = streamStartOffset; while (tries != 0) {
minStart = Integer.MAX_VALUE; if (requestedPreloadedBytesRanges.get(downloadOffset, 0) == 0) {
found = true;
break; break;
} }
if (streamStartOffset < range.start && range.start < minStreamStart) { downloadOffset += currentDownloadChunkSize;
minStreamStart = range.start; if (downloadOffset > totalBytesCount) {
break;
} }
if (moovFound == 2 && downloadOffset == currentDownloadChunkSize * 8) {
downloadOffset = (totalBytesCount - preloadMaxBytes / 2) / currentDownloadChunkSize * currentDownloadChunkSize;
}
tries--;
}
if (!found && requestInfos.isEmpty()) {
onFinishLoadingFile(false);
} }
minStart = Math.min(minStart, range.start);
}
if (minStreamStart != Integer.MAX_VALUE) {
downloadOffset = minStreamStart;
} else if (minStart != Integer.MAX_VALUE) {
downloadOffset = minStart;
} else { } else {
break; downloadOffset = nextPreloadDownloadOffset;
} }
if (requestedPreloadedBytesRanges == null) {
requestedPreloadedBytesRanges = new SparseIntArray();
}
requestedPreloadedBytesRanges.put(downloadOffset, 1);
if (BuildVars.DEBUG_VERSION) {
FileLog.d("start next preload from " + downloadOffset + " size " + totalBytesCount + " for " + cacheFilePreload);
}
preloadNotRequestedBytesCount -= currentDownloadChunkSize;
} else { } else {
downloadOffset = requestedBytesCount; if (notRequestedBytesRanges != null) {
int size = notRequestedBytesRanges.size();
int minStart = Integer.MAX_VALUE;
int minStreamStart = Integer.MAX_VALUE;
for (int b = 0; b < size; b++) {
Range range = notRequestedBytesRanges.get(b);
if (streamStartOffset != 0) {
if (range.start <= streamStartOffset && range.end > streamStartOffset) {
minStreamStart = streamStartOffset;
minStart = Integer.MAX_VALUE;
break;
}
if (streamStartOffset < range.start && range.start < minStreamStart) {
minStreamStart = range.start;
}
}
minStart = Math.min(minStart, range.start);
}
if (minStreamStart != Integer.MAX_VALUE) {
downloadOffset = minStreamStart;
} else if (minStart != Integer.MAX_VALUE) {
downloadOffset = minStart;
} else {
break;
}
} else {
downloadOffset = requestedBytesCount;
}
} }
if (notRequestedBytesRanges != null) { if (!isPreloadVideoOperation && notRequestedBytesRanges != null) {
addPart(notRequestedBytesRanges, downloadOffset, downloadOffset + currentDownloadChunkSize, false); addPart(notRequestedBytesRanges, downloadOffset, downloadOffset + currentDownloadChunkSize, false);
} }
@ -1294,6 +1599,28 @@ public class FileLoadOperation {
final RequestInfo requestInfo = new RequestInfo(); final RequestInfo requestInfo = new RequestInfo();
requestInfos.add(requestInfo); requestInfos.add(requestInfo);
requestInfo.offset = downloadOffset; requestInfo.offset = downloadOffset;
if (!isPreloadVideoOperation && supportsPreloading && preloadStream != null && preloadedBytesRanges != null) {
PreloadRange range = preloadedBytesRanges.get(requestInfo.offset);
if (range != null) {
requestInfo.response = new TLRPC.TL_upload_file();
try {
NativeByteBuffer buffer = new NativeByteBuffer(range.length);
preloadStream.seek(range.fileOffset);
preloadStream.getChannel().read(buffer.buffer);
buffer.buffer.position(0);
requestInfo.response.bytes = buffer;
Utilities.stageQueue.postRunnable(() -> {
processRequestResult(requestInfo, null);
requestInfo.response.freeResources();
});
continue;
} catch (Exception ignore) {
}
}
}
requestInfo.requestToken = ConnectionsManager.getInstance(currentAccount).sendRequest(request, (response, error) -> { requestInfo.requestToken = ConnectionsManager.getInstance(currentAccount).sendRequest(request, (response, error) -> {
if (!requestInfos.contains(requestInfo)) { if (!requestInfos.contains(requestInfo)) {
return; return;
@ -1305,7 +1632,7 @@ public class FileLoadOperation {
} else if (request instanceof TLRPC.TL_upload_getCdnFile) { } else if (request instanceof TLRPC.TL_upload_getCdnFile) {
if (error.text.equals("FILE_TOKEN_INVALID")) { if (error.text.equals("FILE_TOKEN_INVALID")) {
isCdn = false; isCdn = false;
clearOperaion(requestInfo); clearOperaion(requestInfo, false);
startDownloadRequest(); startDownloadRequest();
return; return;
} }
@ -1337,12 +1664,12 @@ public class FileLoadOperation {
cdnIv = res.encryption_iv; cdnIv = res.encryption_iv;
cdnKey = res.encryption_key; cdnKey = res.encryption_key;
cdnToken = res.file_token; cdnToken = res.file_token;
clearOperaion(requestInfo); clearOperaion(requestInfo, false);
startDownloadRequest(); startDownloadRequest();
} }
} else if (response instanceof TLRPC.TL_upload_cdnFileReuploadNeeded) { } else if (response instanceof TLRPC.TL_upload_cdnFileReuploadNeeded) {
if (!reuploadingCdn) { if (!reuploadingCdn) {
clearOperaion(requestInfo); clearOperaion(requestInfo, false);
reuploadingCdn = true; reuploadingCdn = true;
TLRPC.TL_upload_cdnFileReuploadNeeded res = (TLRPC.TL_upload_cdnFileReuploadNeeded) response; TLRPC.TL_upload_cdnFileReuploadNeeded res = (TLRPC.TL_upload_cdnFileReuploadNeeded) response;
TLRPC.TL_upload_reuploadCdnFile req = new TLRPC.TL_upload_reuploadCdnFile(); TLRPC.TL_upload_reuploadCdnFile req = new TLRPC.TL_upload_reuploadCdnFile();
@ -1365,7 +1692,7 @@ public class FileLoadOperation {
} else { } else {
if (error1.text.equals("FILE_TOKEN_INVALID") || error1.text.equals("REQUEST_TOKEN_INVALID")) { if (error1.text.equals("FILE_TOKEN_INVALID") || error1.text.equals("REQUEST_TOKEN_INVALID")) {
isCdn = false; isCdn = false;
clearOperaion(requestInfo); clearOperaion(requestInfo, false);
startDownloadRequest(); startDownloadRequest();
} else { } else {
onFail(false, 0); onFail(false, 0);

View File

@ -0,0 +1,5 @@
package org.telegram.messenger;
public interface FileLoadOperationStream {
void newDataAvailable();
}

View File

@ -60,6 +60,8 @@ public class FileLoader {
private ConcurrentHashMap<String, Boolean> loadOperationPathsUI = new ConcurrentHashMap<>(10, 1, 2); private ConcurrentHashMap<String, Boolean> loadOperationPathsUI = new ConcurrentHashMap<>(10, 1, 2);
private HashMap<String, Long> uploadSizes = new HashMap<>(); private HashMap<String, Long> uploadSizes = new HashMap<>();
private HashMap<String, Boolean> loadingVideos = new HashMap<>();
private static SparseArray<File> mediaDirs = null; private static SparseArray<File> mediaDirs = null;
private FileLoaderDelegate delegate = null; private FileLoaderDelegate delegate = null;
@ -118,6 +120,61 @@ public class FileLoader {
return parentObjectReferences.get(reference); return parentObjectReferences.get(reference);
} }
public void setLoadingVideoInternal(TLRPC.Document document, boolean player) {
String key = getAttachFileName(document);
String dKey = key + (player ? "p" : "");
loadingVideos.put(dKey, true);
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.videoLoadingStateChanged, key);
}
public void setLoadingVideo(TLRPC.Document document, boolean player, boolean schedule) {
if (document == null) {
return;
}
if (schedule) {
AndroidUtilities.runOnUIThread(() -> setLoadingVideoInternal(document, player));
} else {
setLoadingVideoInternal(document, player);
}
}
public void setLoadingVideoForPlayer(TLRPC.Document document, boolean player) {
if (document == null) {
return;
}
String key = getAttachFileName(document);
if (loadingVideos.containsKey(key + (player ? "" : "p"))) {
loadingVideos.put(key + (player ? "p" : ""), true);
}
}
private void removeLoadingVideoInternal(TLRPC.Document document, boolean player) {
String key = getAttachFileName(document);
String dKey = key + (player ? "p" : "");
if (loadingVideos.remove(dKey) != null) {
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.videoLoadingStateChanged, key);
}
}
public void removeLoadingVideo(TLRPC.Document document, boolean player, boolean schedule) {
if (document == null) {
return;
}
if (schedule) {
AndroidUtilities.runOnUIThread(() -> removeLoadingVideoInternal(document, player));
} else {
removeLoadingVideoInternal(document, player);
}
}
public boolean isLoadingVideo(TLRPC.Document document, boolean player) {
return document != null && loadingVideos.containsKey(getAttachFileName(document) + (player ? "p" : ""));
}
public boolean isLoadingVideoAny(TLRPC.Document document) {
return isLoadingVideo(document, false) || isLoadingVideo(document, true);
}
public void cancelUploadFile(final String location, final boolean enc) { public void cancelUploadFile(final String location, final boolean enc) {
fileLoaderQueue.postRunnable(() -> { fileLoaderQueue.postRunnable(() -> {
FileUploadOperation operation; FileUploadOperation operation;
@ -440,22 +497,22 @@ public class FileLoader {
private void pauseCurrentFileLoadOperations(FileLoadOperation newOperation) { private void pauseCurrentFileLoadOperations(FileLoadOperation newOperation) {
for (int a = 0; a < activeFileLoadOperation.size(); a++) { for (int a = 0; a < activeFileLoadOperation.size(); a++) {
FileLoadOperation operation = activeFileLoadOperation.get(a); FileLoadOperation operation = activeFileLoadOperation.get(a);
if (operation == newOperation) { if (operation == newOperation || operation.getDatacenterId() != newOperation.getDatacenterId()) {
continue; continue;
} }
activeFileLoadOperation.remove(operation); activeFileLoadOperation.remove(operation);
a--; a--;
operation.pause();
int datacenterId = operation.getDatacenterId(); int datacenterId = operation.getDatacenterId();
LinkedList<FileLoadOperation> loadOperationQueue = getLoadOperationQueue(datacenterId); LinkedList<FileLoadOperation> loadOperationQueue = getLoadOperationQueue(datacenterId);
loadOperationQueue.add(0, operation); loadOperationQueue.add(0, operation);
if (operation.wasStarted()) { if (operation.wasStarted()) {
currentLoadOperationsCount.put(datacenterId, currentLoadOperationsCount.get(datacenterId) - 1); currentLoadOperationsCount.put(datacenterId, currentLoadOperationsCount.get(datacenterId) - 1);
} }
operation.pause();
} }
} }
private FileLoadOperation loadFileInternal(final TLRPC.Document document, final SecureDocument secureDocument, final WebFile webDocument, final TLRPC.FileLocation location, Object parentObject, final String locationExt, final int locationSize, final int priority, final FileStreamLoadOperation stream, final int streamOffset, final int cacheType) { private FileLoadOperation loadFileInternal(final TLRPC.Document document, final SecureDocument secureDocument, final WebFile webDocument, final TLRPC.FileLocation location, Object parentObject, final String locationExt, final int locationSize, final int priority, final FileLoadOperationStream stream, final int streamOffset, final int cacheType) {
String fileName = null; String fileName = null;
if (location != null) { if (location != null) {
fileName = getAttachFileName(location, locationExt); fileName = getAttachFileName(location, locationExt);
@ -469,14 +526,17 @@ public class FileLoader {
if (fileName == null || fileName.contains("" + Integer.MIN_VALUE)) { if (fileName == null || fileName.contains("" + Integer.MIN_VALUE)) {
return null; return null;
} }
if (!TextUtils.isEmpty(fileName) && !fileName.contains("" + Integer.MIN_VALUE)) { if (cacheType != 10 && !TextUtils.isEmpty(fileName) && !fileName.contains("" + Integer.MIN_VALUE)) {
loadOperationPathsUI.put(fileName, true); loadOperationPathsUI.put(fileName, true);
} }
FileLoadOperation operation; FileLoadOperation operation;
operation = loadOperationPaths.get(fileName); operation = loadOperationPaths.get(fileName);
if (operation != null) { if (operation != null) {
if (streamOffset != 0 || priority > 0) { if (cacheType != 10 && operation.isPreloadVideoOperation()) {
operation.setIsPreloadVideoOperation(false);
}
if (stream != null || priority > 0) {
int datacenterId = operation.getDatacenterId(); int datacenterId = operation.getDatacenterId();
LinkedList<FileLoadOperation> audioLoadOperationQueue = getAudioLoadOperationQueue(datacenterId); LinkedList<FileLoadOperation> audioLoadOperationQueue = getAudioLoadOperationQueue(datacenterId);
@ -494,9 +554,9 @@ public class FileLoader {
} }
if (downloadQueue != null) { if (downloadQueue != null) {
int index = downloadQueue.indexOf(operation); int index = downloadQueue.indexOf(operation);
if (index > 0) { if (index >= 0) {
downloadQueue.remove(index); downloadQueue.remove(index);
if (streamOffset != 0) { if (stream != null) {
if (downloadQueue == audioLoadOperationQueue) { if (downloadQueue == audioLoadOperationQueue) {
if (operation.start(stream, streamOffset)) { if (operation.start(stream, streamOffset)) {
currentAudioLoadOperationsCount.put(datacenterId, currentAudioLoadOperationsCount.get(datacenterId) + 1); currentAudioLoadOperationsCount.put(datacenterId, currentAudioLoadOperationsCount.get(datacenterId) + 1);
@ -564,21 +624,29 @@ public class FileLoader {
type = MEDIA_DIR_DOCUMENT; type = MEDIA_DIR_DOCUMENT;
} }
} }
if (cacheType == 0) { if (cacheType == 0 || cacheType == 10) {
storeDir = getDirectory(type); storeDir = getDirectory(type);
} else if (cacheType == 2) { } else if (cacheType == 2) {
operation.setEncryptFile(true); operation.setEncryptFile(true);
} }
operation.setPaths(currentAccount, storeDir, tempDir); operation.setPaths(currentAccount, storeDir, tempDir);
if (cacheType == 10) {
operation.setIsPreloadVideoOperation(true);
}
final String finalFileName = fileName; final String finalFileName = fileName;
final int finalType = type; final int finalType = type;
FileLoadOperation.FileLoadOperationDelegate fileLoadOperationDelegate = new FileLoadOperation.FileLoadOperationDelegate() { FileLoadOperation.FileLoadOperationDelegate fileLoadOperationDelegate = new FileLoadOperation.FileLoadOperationDelegate() {
@Override @Override
public void didFinishLoadingFile(FileLoadOperation operation, File finalFile) { public void didFinishLoadingFile(FileLoadOperation operation, File finalFile) {
loadOperationPathsUI.remove(finalFileName); if (!operation.isPreloadVideoOperation() && operation.isPreloadFinished()) {
if (delegate != null) { return;
delegate.fileDidLoaded(finalFileName, finalFile, finalType); }
if (!operation.isPreloadVideoOperation()) {
loadOperationPathsUI.remove(finalFileName);
if (delegate != null) {
delegate.fileDidLoaded(finalFileName, finalFile, finalType);
}
} }
checkDownloadQueue(operation.getDatacenterId(), document, webDocument, location, finalFileName); checkDownloadQueue(operation.getDatacenterId(), document, webDocument, location, finalFileName);
} }
@ -612,7 +680,7 @@ public class FileLoader {
if (type == MEDIA_DIR_AUDIO) { if (type == MEDIA_DIR_AUDIO) {
int maxCount = priority > 0 ? 3 : 1; int maxCount = priority > 0 ? 3 : 1;
int count = currentAudioLoadOperationsCount.get(datacenterId); int count = currentAudioLoadOperationsCount.get(datacenterId);
if (streamOffset != 0 || count < maxCount) { if (stream != null || count < maxCount) {
if (operation.start(stream, streamOffset)) { if (operation.start(stream, streamOffset)) {
currentAudioLoadOperationsCount.put(datacenterId, count + 1); currentAudioLoadOperationsCount.put(datacenterId, count + 1);
} }
@ -622,7 +690,7 @@ public class FileLoader {
} else if (location != null || MessageObject.isImageWebDocument(webDocument)) { } else if (location != null || MessageObject.isImageWebDocument(webDocument)) {
int maxCount = priority > 0 ? 6 : 2; int maxCount = priority > 0 ? 6 : 2;
int count = currentPhotoLoadOperationsCount.get(datacenterId); int count = currentPhotoLoadOperationsCount.get(datacenterId);
if (streamOffset != 0 || count < maxCount) { if (stream != null || count < maxCount) {
if (operation.start(stream, streamOffset)) { if (operation.start(stream, streamOffset)) {
currentPhotoLoadOperationsCount.put(datacenterId, count + 1); currentPhotoLoadOperationsCount.put(datacenterId, count + 1);
} }
@ -632,7 +700,7 @@ public class FileLoader {
} else { } else {
int maxCount = priority > 0 ? 3 : 1; int maxCount = priority > 0 ? 3 : 1;
int count = currentLoadOperationsCount.get(datacenterId); int count = currentLoadOperationsCount.get(datacenterId);
if (streamOffset != 0 || count < maxCount) { if (stream != null || count < maxCount) {
if (operation.start(stream, streamOffset)) { if (operation.start(stream, streamOffset)) {
currentLoadOperationsCount.put(datacenterId, count + 1); currentLoadOperationsCount.put(datacenterId, count + 1);
activeFileLoadOperation.add(operation); activeFileLoadOperation.add(operation);
@ -675,13 +743,13 @@ public class FileLoader {
} else { } else {
fileName = null; fileName = null;
} }
if (!TextUtils.isEmpty(fileName) && !fileName.contains("" + Integer.MIN_VALUE)) { if (cacheType != 10 && !TextUtils.isEmpty(fileName) && !fileName.contains("" + Integer.MIN_VALUE)) {
loadOperationPathsUI.put(fileName, true); loadOperationPathsUI.put(fileName, true);
} }
fileLoaderQueue.postRunnable(() -> loadFileInternal(document, secureDocument, webDocument, location, parentObject, locationExt, locationSize, priority, null, 0, cacheType)); fileLoaderQueue.postRunnable(() -> loadFileInternal(document, secureDocument, webDocument, location, parentObject, locationExt, locationSize, priority, null, 0, cacheType));
} }
protected FileLoadOperation loadStreamFile(final FileStreamLoadOperation stream, final TLRPC.Document document, final Object parentObject, final int offset) { protected FileLoadOperation loadStreamFile(final FileLoadOperationStream stream, final TLRPC.Document document, final Object parentObject, final int offset) {
final CountDownLatch semaphore = new CountDownLatch(1); final CountDownLatch semaphore = new CountDownLatch(1);
final FileLoadOperation[] result = new FileLoadOperation[1]; final FileLoadOperation[] result = new FileLoadOperation[1];
fileLoaderQueue.postRunnable(() -> { fileLoaderQueue.postRunnable(() -> {
@ -826,7 +894,7 @@ public class FileLoader {
} else if (message.media instanceof TLRPC.TL_messageMediaInvoice) { } else if (message.media instanceof TLRPC.TL_messageMediaInvoice) {
TLRPC.WebDocument document = ((TLRPC.TL_messageMediaInvoice) message.media).photo; TLRPC.WebDocument document = ((TLRPC.TL_messageMediaInvoice) message.media).photo;
if (document != null) { if (document != null) {
return Utilities.MD5(document.url) + "." + ImageLoader.getHttpUrlExtension(document.url, getExtensionByMime(document.mime_type)); return Utilities.MD5(document.url) + "." + ImageLoader.getHttpUrlExtension(document.url, getMimeTypePart(document.mime_type));
} }
} }
} }
@ -1009,7 +1077,7 @@ public class FileLoader {
return fileName != null ? fileName : ""; return fileName != null ? fileName : "";
} }
public static String getExtensionByMime(String mime) { public static String getMimeTypePart(String mime) {
int index; int index;
if ((index = mime.lastIndexOf('/')) != -1) { if ((index = mime.lastIndexOf('/')) != -1) {
return mime.substring(index + 1); return mime.substring(index + 1);
@ -1017,6 +1085,20 @@ public class FileLoader {
return ""; return "";
} }
public static String getExtensionByMimeType(String mime) {
if (mime != null) {
switch (mime) {
case "video/mp4":
return ".mp4";
case "video/x-matroska":
return ".mkv";
case "audio/ogg":
return ".ogg";
}
}
return "";
}
public static File getInternalCacheDir() { public static File getInternalCacheDir() {
return ApplicationLoader.applicationContext.getCacheDir(); return ApplicationLoader.applicationContext.getCacheDir();
} }
@ -1056,21 +1138,7 @@ public class FileLoader {
} }
} }
if (docExt.length() <= 1) { if (docExt.length() <= 1) {
if (document.mime_type != null) { docExt = getExtensionByMimeType(document.mime_type);
switch (document.mime_type) {
case "video/mp4":
docExt = ".mp4";
break;
case "audio/ogg":
docExt = ".ogg";
break;
default:
docExt = "";
break;
}
} else {
docExt = "";
}
} }
if (docExt.length() > 1) { if (docExt.length() > 1) {
return document.dc_id + "_" + document.id + docExt; return document.dc_id + "_" + document.id + docExt;
@ -1085,7 +1153,7 @@ public class FileLoader {
return secureFile.dc_id + "_" + secureFile.id + ".jpg"; return secureFile.dc_id + "_" + secureFile.id + ".jpg";
} else if (attach instanceof WebFile) { } else if (attach instanceof WebFile) {
WebFile document = (WebFile) attach; WebFile document = (WebFile) attach;
return Utilities.MD5(document.url) + "." + ImageLoader.getHttpUrlExtension(document.url, getExtensionByMime(document.mime_type)); return Utilities.MD5(document.url) + "." + ImageLoader.getHttpUrlExtension(document.url, getMimeTypePart(document.mime_type));
} else if (attach instanceof TLRPC.PhotoSize) { } else if (attach instanceof TLRPC.PhotoSize) {
TLRPC.PhotoSize photo = (TLRPC.PhotoSize) attach; TLRPC.PhotoSize photo = (TLRPC.PhotoSize) attach;
if (photo.location == null || photo.location instanceof TLRPC.TL_fileLocationUnavailable) { if (photo.location == null || photo.location instanceof TLRPC.TL_fileLocationUnavailable) {
@ -1154,4 +1222,8 @@ public class FileLoader {
} }
}); });
} }
public static boolean isVideoMimeType(String mime) {
return "video/mp4".equals(mime) || SharedConfig.streamMkv && "video/x-matroska".equals(mime);
}
} }

View File

@ -56,7 +56,7 @@ public class FileRefController {
if (parentObject instanceof MessageObject) { if (parentObject instanceof MessageObject) {
MessageObject messageObject = (MessageObject) parentObject; MessageObject messageObject = (MessageObject) parentObject;
int channelId = messageObject.getChannelId(); int channelId = messageObject.getChannelId();
return "message" + messageObject.getId() + "_" + channelId; return "message" + messageObject.getRealId() + "_" + channelId;
} else if (parentObject instanceof TLRPC.Message) { } else if (parentObject instanceof TLRPC.Message) {
TLRPC.Message message = (TLRPC.Message) parentObject; TLRPC.Message message = (TLRPC.Message) parentObject;
int channelId = message.to_id != null ? message.to_id.channel_id : 0; int channelId = message.to_id != null ? message.to_id.channel_id : 0;
@ -200,7 +200,7 @@ public class FileRefController {
} }
if (parentObject instanceof MessageObject) { if (parentObject instanceof MessageObject) {
MessageObject messageObject = (MessageObject) parentObject; MessageObject messageObject = (MessageObject) parentObject;
if (messageObject.getId() < 0 && messageObject.messageOwner.media.webpage != null) { if (messageObject.getRealId() < 0 && messageObject.messageOwner.media.webpage != null) {
parentObject = messageObject.messageOwner.media.webpage; parentObject = messageObject.messageOwner.media.webpage;
} }
} }
@ -266,11 +266,11 @@ public class FileRefController {
if (channelId != 0) { if (channelId != 0) {
TLRPC.TL_channels_getMessages req = new TLRPC.TL_channels_getMessages(); TLRPC.TL_channels_getMessages req = new TLRPC.TL_channels_getMessages();
req.channel = MessagesController.getInstance(currentAccount).getInputChannel(channelId); req.channel = MessagesController.getInstance(currentAccount).getInputChannel(channelId);
req.id.add(messageObject.getId()); req.id.add(messageObject.getRealId());
ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> onRequestComplete(locationKey, parentKey, response, true)); ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> onRequestComplete(locationKey, parentKey, response, true));
} else { } else {
TLRPC.TL_messages_getMessages req = new TLRPC.TL_messages_getMessages(); TLRPC.TL_messages_getMessages req = new TLRPC.TL_messages_getMessages();
req.id.add(messageObject.getId()); req.id.add(messageObject.getRealId());
ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> onRequestComplete(locationKey, parentKey, response, true)); ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> onRequestComplete(locationKey, parentKey, response, true));
} }
} else if (parentObject instanceof TLRPC.TL_wallPaper) { } else if (parentObject instanceof TLRPC.TL_wallPaper) {
@ -872,6 +872,6 @@ public class FileRefController {
} }
public static boolean isFileRefError(String error) { public static boolean isFileRefError(String error) {
return "FILEREF_EXPIRED".equals(error) || "FILE_REFERENCE_EXPIRED".equals(error) || "FILE_REFERENCE_EMPTY".equals(error); return "FILEREF_EXPIRED".equals(error) || "FILE_REFERENCE_EXPIRED".equals(error) || "FILE_REFERENCE_EMPTY".equals(error) || error != null && error.startsWith("FILE_REFERENCE_");
} }
} }

View File

@ -23,7 +23,7 @@ import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
public class FileStreamLoadOperation extends BaseDataSource { public class FileStreamLoadOperation extends BaseDataSource implements FileLoadOperationStream {
private FileLoadOperation loadOperation; private FileLoadOperation loadOperation;
@ -76,8 +76,10 @@ public class FileStreamLoadOperation extends BaseDataSource {
} }
opened = true; opened = true;
transferStarted(dataSpec); transferStarted(dataSpec);
file = new RandomAccessFile(loadOperation.getCurrentFile(), "r"); if (loadOperation != null) {
file.seek(currentOffset); file = new RandomAccessFile(loadOperation.getCurrentFile(), "r");
file.seek(currentOffset);
}
return bytesRemaining; return bytesRemaining;
} }
@ -96,9 +98,8 @@ public class FileStreamLoadOperation extends BaseDataSource {
while (availableLength == 0) { while (availableLength == 0) {
availableLength = loadOperation.getDownloadedLengthFromOffset(currentOffset, readLength); availableLength = loadOperation.getDownloadedLengthFromOffset(currentOffset, readLength);
if (availableLength == 0) { if (availableLength == 0) {
if (loadOperation.isPaused()) { FileLog.d("not found bytes " + offset);
FileLoader.getInstance(currentAccount).loadStreamFile(this, document, parentObject, currentOffset); FileLoader.getInstance(currentAccount).loadStreamFile(this, document, parentObject, currentOffset);
}
countDownLatch = new CountDownLatch(1); countDownLatch = new CountDownLatch(1);
countDownLatch.await(); countDownLatch.await();
} }
@ -142,7 +143,8 @@ public class FileStreamLoadOperation extends BaseDataSource {
} }
} }
protected void newDataAvailable() { @Override
public void newDataAvailable() {
if (countDownLatch != null) { if (countDownLatch != null) {
countDownLatch.countDown(); countDownLatch.countDown();
} }

View File

@ -199,7 +199,7 @@ public class FileUploadOperation {
storeFileUploadInfo(); storeFileUploadInfo();
} }
} }
availableSize = newAvailableSize; availableSize = finalSize > 0 ? finalSize : newAvailableSize;
if (currentUploadRequetsCount < maxRequestsCount) { if (currentUploadRequetsCount < maxRequestsCount) {
startUploadRequest(); startUploadRequest();
} }

View File

@ -77,12 +77,13 @@ public class ImageLoader {
private ConcurrentHashMap<String, Float> fileProgresses = new ConcurrentHashMap<>(); private ConcurrentHashMap<String, Float> fileProgresses = new ConcurrentHashMap<>();
private HashMap<String, ThumbGenerateTask> thumbGenerateTasks = new HashMap<>(); private HashMap<String, ThumbGenerateTask> thumbGenerateTasks = new HashMap<>();
private HashMap<String, Integer> forceLoadingImages = new HashMap<>(); private HashMap<String, Integer> forceLoadingImages = new HashMap<>();
private static byte[] bytes; private static ThreadLocal<byte[]> bytesLocal = new ThreadLocal<>();
private static byte[] bytesThumb; private static ThreadLocal<byte[]> bytesThumbLocal = new ThreadLocal<>();
private static byte[] header = new byte[12]; private static byte[] header = new byte[12];
private static byte[] headerThumb = new byte[12]; private static byte[] headerThumb = new byte[12];
private int currentHttpTasksCount = 0; private int currentHttpTasksCount = 0;
private int currentArtworkTasksCount = 0; private int currentArtworkTasksCount = 0;
private boolean canForce8888;
private ConcurrentHashMap<String, WebFile> testWebFile = new ConcurrentHashMap<>(); private ConcurrentHashMap<String, WebFile> testWebFile = new ConcurrentHashMap<>();
@ -99,10 +100,13 @@ public class ImageLoader {
private File telegramPath = null; private File telegramPath = null;
public static final String VIDEO_FILTER = "g";
private class ThumbGenerateInfo { private class ThumbGenerateInfo {
private TLRPC.Document parentDocument; private TLRPC.Document parentDocument;
private String filter; private String filter;
private ArrayList<ImageReceiver> imageReceiverArray = new ArrayList<>(); private ArrayList<ImageReceiver> imageReceiverArray = new ArrayList<>();
private boolean big;
} }
private class HttpFileTask extends AsyncTask<Void, Void, Boolean> { private class HttpFileTask extends AsyncTask<Void, Void, Boolean> {
@ -646,19 +650,19 @@ public class ImageLoader {
removeTask(); removeTask();
return; return;
} }
int size = Math.min(180, Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) / 4); int size = info.big ? Math.max(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) : Math.min(180, Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) / 4);
Bitmap originalBitmap = null; Bitmap originalBitmap = null;
if (mediaType == FileLoader.MEDIA_DIR_IMAGE) { if (mediaType == FileLoader.MEDIA_DIR_IMAGE) {
originalBitmap = ImageLoader.loadBitmap(originalPath.toString(), null, size, size, false); originalBitmap = ImageLoader.loadBitmap(originalPath.toString(), null, size, size, false);
} else if (mediaType == FileLoader.MEDIA_DIR_VIDEO) { } else if (mediaType == FileLoader.MEDIA_DIR_VIDEO) {
originalBitmap = ThumbnailUtils.createVideoThumbnail(originalPath.toString(), MediaStore.Video.Thumbnails.MINI_KIND); originalBitmap = ThumbnailUtils.createVideoThumbnail(originalPath.toString(), info.big ? MediaStore.Video.Thumbnails.FULL_SCREEN_KIND : MediaStore.Video.Thumbnails.MINI_KIND);
} else if (mediaType == FileLoader.MEDIA_DIR_DOCUMENT) { } else if (mediaType == FileLoader.MEDIA_DIR_DOCUMENT) {
String path = originalPath.toString().toLowerCase(); String path = originalPath.toString().toLowerCase();
if (!path.endsWith(".jpg") && !path.endsWith(".jpeg") && !path.endsWith(".png") && !path.endsWith(".gif")) { if (path.endsWith("mp4")) {
removeTask(); originalBitmap = ThumbnailUtils.createVideoThumbnail(originalPath.toString(), info.big ? MediaStore.Video.Thumbnails.FULL_SCREEN_KIND : MediaStore.Video.Thumbnails.MINI_KIND);
return; } else if (path.endsWith(".jpg") || path.endsWith(".jpeg") || path.endsWith(".png") || path.endsWith(".gif")) {
originalBitmap = ImageLoader.loadBitmap(path, null, size, size, false);
} }
originalBitmap = ImageLoader.loadBitmap(path, null, size, size, false);
} }
if (originalBitmap == null) { if (originalBitmap == null) {
removeTask(); removeTask();
@ -672,13 +676,15 @@ public class ImageLoader {
return; return;
} }
float scaleFactor = Math.min((float) w / size, (float) h / size); float scaleFactor = Math.min((float) w / size, (float) h / size);
Bitmap scaledBitmap = Bitmaps.createScaledBitmap(originalBitmap, (int) (w / scaleFactor), (int) (h / scaleFactor), true); if (scaleFactor > 1) {
if (scaledBitmap != originalBitmap) { Bitmap scaledBitmap = Bitmaps.createScaledBitmap(originalBitmap, (int) (w / scaleFactor), (int) (h / scaleFactor), true);
originalBitmap.recycle(); if (scaledBitmap != originalBitmap) {
originalBitmap = scaledBitmap; originalBitmap.recycle();
originalBitmap = scaledBitmap;
}
} }
FileOutputStream stream = new FileOutputStream(thumbFile); FileOutputStream stream = new FileOutputStream(thumbFile);
originalBitmap.compress(Bitmap.CompressFormat.JPEG, 60, stream); originalBitmap.compress(Bitmap.CompressFormat.JPEG, info.big ? 83 : 60, stream);
try { try {
stream.close(); stream.close();
} catch (Exception e) { } catch (Exception e) {
@ -696,7 +702,7 @@ public class ImageLoader {
for (int a = 0; a < finalImageReceiverArray.size(); a++) { for (int a = 0; a < finalImageReceiverArray.size(); a++) {
ImageReceiver imgView = finalImageReceiverArray.get(a); ImageReceiver imgView = finalImageReceiverArray.get(a);
imgView.setImageBitmapByKey(bitmapDrawable, kf, false, false); imgView.setImageBitmapByKey(bitmapDrawable, kf, ImageReceiver.TYPE_IMAGE, false);
} }
memCache.put(kf, bitmapDrawable); memCache.put(kf, bitmapDrawable);
@ -738,9 +744,11 @@ public class ImageLoader {
} }
TLRPC.TL_photoStrippedSize photoSize = (TLRPC.TL_photoStrippedSize) cacheImage.location; TLRPC.TL_photoStrippedSize photoSize = (TLRPC.TL_photoStrippedSize) cacheImage.location;
int len = photoSize.bytes.length - 3 + Bitmaps.header.length + Bitmaps.footer.length; int len = photoSize.bytes.length - 3 + Bitmaps.header.length + Bitmaps.footer.length;
byte[] bytes = bytesLocal.get();
byte[] data = bytes != null && bytes.length >= len ? bytes : null; byte[] data = bytes != null && bytes.length >= len ? bytes : null;
if (data == null) { if (data == null) {
bytes = data = new byte[len]; bytes = data = new byte[len];
bytesLocal.set(bytes);
} }
System.arraycopy(Bitmaps.header, 0, data, 0, Bitmaps.header.length); System.arraycopy(Bitmaps.header, 0, data, 0, Bitmaps.header.length);
System.arraycopy(photoSize.bytes, 3, data, Bitmaps.header.length, photoSize.bytes.length - 3); System.arraycopy(photoSize.bytes, 3, data, Bitmaps.header.length, photoSize.bytes.length - 3);
@ -760,7 +768,12 @@ public class ImageLoader {
return; return;
} }
} }
AnimatedFileDrawable fileDrawable = new AnimatedFileDrawable(cacheImage.finalFilePath, cacheImage.filter != null && cacheImage.filter.equals("d")); AnimatedFileDrawable fileDrawable;
if (VIDEO_FILTER.equals(cacheImage.filter) && !(cacheImage.location instanceof TLRPC.TL_documentEncrypted)) {
fileDrawable = new AnimatedFileDrawable(cacheImage.finalFilePath, false, cacheImage.size, cacheImage.location instanceof TLRPC.Document ? (TLRPC.Document) cacheImage.location : null, cacheImage.parentObject, cacheImage.currentAccount);
} else {
fileDrawable = new AnimatedFileDrawable(cacheImage.finalFilePath, "d".equals(cacheImage.filter), 0, null, null, cacheImage.currentAccount);
}
Thread.interrupted(); Thread.interrupted();
onPostExecute(fileDrawable); onPostExecute(fileDrawable);
} else { } else {
@ -792,7 +805,7 @@ public class ImageLoader {
try { try {
randomAccessFile = new RandomAccessFile(cacheFileFinal, "r"); randomAccessFile = new RandomAccessFile(cacheFileFinal, "r");
byte[] bytes; byte[] bytes;
if (cacheImage.selfThumb) { if (cacheImage.imageType == ImageReceiver.TYPE_THUMB) {
bytes = headerThumb; bytes = headerThumb;
} else { } else {
bytes = header; bytes = header;
@ -817,7 +830,7 @@ public class ImageLoader {
} }
} }
if (cacheImage.selfThumb) { if (cacheImage.imageType == ImageReceiver.TYPE_THUMB) {
int blurType = 0; int blurType = 0;
boolean checkInversion = false; boolean checkInversion = false;
if (cacheImage.filter != null) { if (cacheImage.filter != null) {
@ -864,9 +877,11 @@ public class ImageLoader {
RandomAccessFile f = new RandomAccessFile(cacheFileFinal, "r"); RandomAccessFile f = new RandomAccessFile(cacheFileFinal, "r");
int len = (int) f.length(); int len = (int) f.length();
int offset = 0; int offset = 0;
byte[] bytesThumb = bytesThumbLocal.get();
byte[] data = bytesThumb != null && bytesThumb.length >= len ? bytesThumb : null; byte[] data = bytesThumb != null && bytesThumb.length >= len ? bytesThumb : null;
if (data == null) { if (data == null) {
bytesThumb = data = new byte[len]; bytesThumb = data = new byte[len];
bytesThumbLocal.set(bytesThumb);
} }
f.readFully(data, 0, len); f.readFully(data, 0, len);
f.close(); f.close();
@ -972,7 +987,7 @@ public class ImageLoader {
float h_filter = 0; float h_filter = 0;
boolean blur = false; boolean blur = false;
boolean checkInversion = false; boolean checkInversion = false;
boolean force8888 = false; boolean force8888 = canForce8888;
if (cacheImage.filter != null) { if (cacheImage.filter != null) {
String args[] = cacheImage.filter.split("_"); String args[] = cacheImage.filter.split("_");
if (args.length >= 2) { if (args.length >= 2) {
@ -1001,9 +1016,11 @@ public class ImageLoader {
if (secureDocumentKey != null) { if (secureDocumentKey != null) {
RandomAccessFile f = new RandomAccessFile(cacheFileFinal, "r"); RandomAccessFile f = new RandomAccessFile(cacheFileFinal, "r");
int len = (int) f.length(); int len = (int) f.length();
byte[] bytes = bytesLocal.get();
byte[] data = bytes != null && bytes.length >= len ? bytes : null; byte[] data = bytes != null && bytes.length >= len ? bytes : null;
if (data == null) { if (data == null) {
bytes = data = new byte[len]; bytes = data = new byte[len];
bytesLocal.set(bytes);
} }
f.readFully(data, 0, len); f.readFully(data, 0, len);
f.close(); f.close();
@ -1041,7 +1058,7 @@ public class ImageLoader {
} }
} else if (mediaThumbPath != null) { } else if (mediaThumbPath != null) {
opts.inJustDecodeBounds = true; opts.inJustDecodeBounds = true;
opts.inPreferredConfig = Bitmap.Config.RGB_565; opts.inPreferredConfig = force8888 ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
FileInputStream is = new FileInputStream(cacheFileFinal); FileInputStream is = new FileInputStream(cacheFileFinal);
image = BitmapFactory.decodeStream(is, null, opts); image = BitmapFactory.decodeStream(is, null, opts);
is.close(); is.close();
@ -1098,9 +1115,11 @@ public class ImageLoader {
RandomAccessFile f = new RandomAccessFile(cacheFileFinal, "r"); RandomAccessFile f = new RandomAccessFile(cacheFileFinal, "r");
int len = (int) f.length(); int len = (int) f.length();
int offset = 0; int offset = 0;
byte[] bytes = bytesLocal.get();
byte[] data = bytes != null && bytes.length >= len ? bytes : null; byte[] data = bytes != null && bytes.length >= len ? bytes : null;
if (data == null) { if (data == null) {
bytes = data = new byte[len]; bytes = data = new byte[len];
bytesLocal.set(bytes);
} }
f.readFully(data, 0, len); f.readFully(data, 0, len);
f.close(); f.close();
@ -1248,8 +1267,10 @@ public class ImageLoader {
protected String ext; protected String ext;
protected SecureDocument secureDocument; protected SecureDocument secureDocument;
protected Object location; protected Object location;
protected Object parentObject;
protected int size;
protected boolean animatedFile; protected boolean animatedFile;
protected boolean selfThumb; protected int imageType;
protected int currentAccount; protected int currentAccount;
@ -1264,25 +1285,25 @@ public class ImageLoader {
protected ArrayList<ImageReceiver> imageReceiverArray = new ArrayList<>(); protected ArrayList<ImageReceiver> imageReceiverArray = new ArrayList<>();
protected ArrayList<String> keys = new ArrayList<>(); protected ArrayList<String> keys = new ArrayList<>();
protected ArrayList<String> filters = new ArrayList<>(); protected ArrayList<String> filters = new ArrayList<>();
protected ArrayList<Boolean> thumbs = new ArrayList<>(); protected ArrayList<Integer> thumbs = new ArrayList<>();
public void addImageReceiver(ImageReceiver imageReceiver, String key, String filter, boolean thumb) { public void addImageReceiver(ImageReceiver imageReceiver, String key, String filter, int type) {
if (imageReceiverArray.contains(imageReceiver)) { if (imageReceiverArray.contains(imageReceiver)) {
return; return;
} }
imageReceiverArray.add(imageReceiver); imageReceiverArray.add(imageReceiver);
keys.add(key); keys.add(key);
filters.add(filter); filters.add(filter);
thumbs.add(thumb); thumbs.add(type);
imageLoadingByTag.put(imageReceiver.getTag(thumb), this); imageLoadingByTag.put(imageReceiver.getTag(type), this);
} }
public void replaceImageReceiver(ImageReceiver imageReceiver, String key, String filter, boolean thumb) { public void replaceImageReceiver(ImageReceiver imageReceiver, String key, String filter, int type) {
int index = imageReceiverArray.indexOf(imageReceiver); int index = imageReceiverArray.indexOf(imageReceiver);
if (index == -1) { if (index == -1) {
return; return;
} }
if (thumbs.get(index) != thumb) { if (thumbs.get(index) != type) {
index = imageReceiverArray.subList(index + 1, imageReceiverArray.size()).indexOf(imageReceiver); index = imageReceiverArray.subList(index + 1, imageReceiverArray.size()).indexOf(imageReceiver);
if (index == -1) { if (index == -1) {
return; return;
@ -1293,23 +1314,23 @@ public class ImageLoader {
} }
public void removeImageReceiver(ImageReceiver imageReceiver) { public void removeImageReceiver(ImageReceiver imageReceiver) {
Boolean thumb = selfThumb; int currentImageType = imageType;
for (int a = 0; a < imageReceiverArray.size(); a++) { for (int a = 0; a < imageReceiverArray.size(); a++) {
ImageReceiver obj = imageReceiverArray.get(a); ImageReceiver obj = imageReceiverArray.get(a);
if (obj == null || obj == imageReceiver) { if (obj == null || obj == imageReceiver) {
imageReceiverArray.remove(a); imageReceiverArray.remove(a);
keys.remove(a); keys.remove(a);
filters.remove(a); filters.remove(a);
thumb = thumbs.remove(a); currentImageType = thumbs.remove(a);
if (obj != null) { if (obj != null) {
imageLoadingByTag.remove(obj.getTag(thumb)); imageLoadingByTag.remove(obj.getTag(currentImageType));
} }
a--; a--;
} }
} }
if (imageReceiverArray.size() == 0) { if (imageReceiverArray.size() == 0) {
for (int a = 0; a < imageReceiverArray.size(); a++) { for (int a = 0; a < imageReceiverArray.size(); a++) {
imageLoadingByTag.remove(imageReceiverArray.get(a).getTag(thumb)); imageLoadingByTag.remove(imageReceiverArray.get(a).getTag(currentImageType));
} }
imageReceiverArray.clear(); imageReceiverArray.clear();
if (location != null) { if (location != null) {
@ -1326,7 +1347,7 @@ public class ImageLoader {
} }
} }
if (cacheTask != null) { if (cacheTask != null) {
if (selfThumb) { if (currentImageType == ImageReceiver.TYPE_THUMB) {
cacheThumbOutQueue.cancelRunnable(cacheTask); cacheThumbOutQueue.cancelRunnable(cacheTask);
} else { } else {
cacheOutQueue.cancelRunnable(cacheTask); cacheOutQueue.cancelRunnable(cacheTask);
@ -1362,24 +1383,31 @@ public class ImageLoader {
AnimatedFileDrawable fileDrawable = (AnimatedFileDrawable) image; AnimatedFileDrawable fileDrawable = (AnimatedFileDrawable) image;
for (int a = 0; a < finalImageReceiverArray.size(); a++) { for (int a = 0; a < finalImageReceiverArray.size(); a++) {
ImageReceiver imgView = finalImageReceiverArray.get(a); ImageReceiver imgView = finalImageReceiverArray.get(a);
if (imgView.setImageBitmapByKey(a == 0 ? fileDrawable : fileDrawable.makeCopy(), key, selfThumb, false)) { AnimatedFileDrawable toSet = (a == 0 ? fileDrawable : fileDrawable.makeCopy());
imageSet = true; if (imgView.setImageBitmapByKey(toSet, key, imageType, false)) {
if (toSet == fileDrawable) {
imageSet = true;
}
} else {
if (toSet != fileDrawable) {
toSet.recycle();
}
} }
} }
if (!imageSet) { if (!imageSet) {
((AnimatedFileDrawable) image).recycle(); fileDrawable.recycle();
} }
} else { } else {
for (int a = 0; a < finalImageReceiverArray.size(); a++) { for (int a = 0; a < finalImageReceiverArray.size(); a++) {
ImageReceiver imgView = finalImageReceiverArray.get(a); ImageReceiver imgView = finalImageReceiverArray.get(a);
imgView.setImageBitmapByKey(image, key, selfThumb, false); imgView.setImageBitmapByKey(image, key, imageType, false);
} }
} }
}); });
} }
for (int a = 0; a < imageReceiverArray.size(); a++) { for (int a = 0; a < imageReceiverArray.size(); a++) {
ImageReceiver imageReceiver = imageReceiverArray.get(a); ImageReceiver imageReceiver = imageReceiverArray.get(a);
imageLoadingByTag.remove(imageReceiver.getTag(selfThumb)); imageLoadingByTag.remove(imageReceiver.getTag(imageType));
} }
imageReceiverArray.clear(); imageReceiverArray.clear();
if (url != null) { if (url != null) {
@ -1409,7 +1437,14 @@ public class ImageLoader {
public ImageLoader() { public ImageLoader() {
thumbGeneratingQueue.setPriority(Thread.MIN_PRIORITY); thumbGeneratingQueue.setPriority(Thread.MIN_PRIORITY);
int cacheSize = Math.min(15, ((ActivityManager) ApplicationLoader.applicationContext.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass() / 7) * 1024 * 1024; int memoryClass = ((ActivityManager) ApplicationLoader.applicationContext.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
int maxSize;
if (canForce8888 = memoryClass >= 256) {
maxSize = 30;
} else {
maxSize = 15;
}
int cacheSize = Math.min(maxSize, memoryClass / 7) * 1024 * 1024;
memCache = new LruCache(cacheSize) { memCache = new LruCache(cacheSize) {
@Override @Override
@ -1801,24 +1836,28 @@ public class ImageLoader {
} }
} }
public void cancelLoadingForImageReceiver(final ImageReceiver imageReceiver, final int type) { public void cancelLoadingForImageReceiver(final ImageReceiver imageReceiver, final boolean cancelAll) {
if (imageReceiver == null) { if (imageReceiver == null) {
return; return;
} }
imageLoadQueue.postRunnable(() -> { imageLoadQueue.postRunnable(() -> {
int start = 0; for (int a = 0; a < 3; a++) {
int count = 2; int imageType;
if (type == 1) { if (a > 0 && !cancelAll) {
count = 1; return;
} else if (type == 2) {
start = 1;
}
for (int a = start; a < count; a++) {
int TAG = imageReceiver.getTag(a == 0);
if (a == 0) {
removeFromWaitingForThumb(TAG, imageReceiver);
} }
if (a == 0) {
imageType = ImageReceiver.TYPE_THUMB;
} else if (a == 1) {
imageType = ImageReceiver.TYPE_IMAGE;
} else {
imageType = ImageReceiver.TYPE_MEDIA;
}
int TAG = imageReceiver.getTag(imageType);
if (TAG != 0) { if (TAG != 0) {
if (a == 0) {
removeFromWaitingForThumb(TAG, imageReceiver);
}
CacheImage ei = imageLoadingByTag.get(TAG); CacheImage ei = imageLoadingByTag.get(TAG);
if (ei != null) { if (ei != null) {
ei.removeImageReceiver(imageReceiver); ei.removeImageReceiver(imageReceiver);
@ -1915,20 +1954,20 @@ public class ImageLoader {
if (imageReceiver == null) { if (imageReceiver == null) {
return; return;
} }
final String key = imageReceiver.getKey(); final String key = imageReceiver.getImageKey();
if (key == null) { if (key == null) {
return; return;
} }
imageLoadQueue.postRunnable(() -> forceLoadingImages.remove(key)); imageLoadQueue.postRunnable(() -> forceLoadingImages.remove(key));
} }
private void createLoadOperationForImageReceiver(final ImageReceiver imageReceiver, final String key, final String url, final String ext, final Object imageLocation, final String filter, final int size, final int cacheType, final int thumb) { private void createLoadOperationForImageReceiver(final ImageReceiver imageReceiver, final String key, final String url, final String ext, final Object imageLocation, final String filter, final int size, final int cacheType, final int imageType, final int thumb) {
if (imageReceiver == null || url == null || key == null) { if (imageReceiver == null || url == null || key == null) {
return; return;
} }
int TAG = imageReceiver.getTag(thumb != 0); int TAG = imageReceiver.getTag(imageType);
if (TAG == 0) { if (TAG == 0) {
imageReceiver.setTag(TAG = lastImageNum, thumb != 0); imageReceiver.setTag(TAG = lastImageNum, imageType);
lastImageNum++; lastImageNum++;
if (lastImageNum == Integer.MAX_VALUE) { if (lastImageNum == Integer.MAX_VALUE) {
lastImageNum = 0; lastImageNum = 0;
@ -1938,9 +1977,10 @@ public class ImageLoader {
final int finalTag = TAG; final int finalTag = TAG;
final boolean finalIsNeedsQualityThumb = imageReceiver.isNeedsQualityThumb(); final boolean finalIsNeedsQualityThumb = imageReceiver.isNeedsQualityThumb();
final Object parentObject = imageReceiver.getParentObject(); final Object parentObject = imageReceiver.getParentObject();
final TLRPC.Document qualityDocument = imageReceiver.getQulityThumbDocument();
final boolean shouldGenerateQualityThumb = imageReceiver.isShouldGenerateQualityThumb(); final boolean shouldGenerateQualityThumb = imageReceiver.isShouldGenerateQualityThumb();
final int currentAccount = imageReceiver.getCurrentAccount(); final int currentAccount = imageReceiver.getCurrentAccount();
final boolean currentKeyQuality = imageReceiver.isCurrentKeyQuality(); final boolean currentKeyQuality = imageType == ImageReceiver.TYPE_IMAGE && imageReceiver.isCurrentKeyQuality();
imageLoadQueue.postRunnable(() -> { imageLoadQueue.postRunnable(() -> {
boolean added = false; boolean added = false;
if (thumb != 2) { if (thumb != 2) {
@ -1952,7 +1992,7 @@ public class ImageLoader {
added = true; added = true;
} else if (alreadyLoadingImage == alreadyLoadingUrl) { } else if (alreadyLoadingImage == alreadyLoadingUrl) {
if (alreadyLoadingCache == null) { if (alreadyLoadingCache == null) {
alreadyLoadingImage.replaceImageReceiver(imageReceiver, key, filter, thumb != 0); alreadyLoadingImage.replaceImageReceiver(imageReceiver, key, filter, imageType);
} }
added = true; added = true;
} else { } else {
@ -1961,11 +2001,11 @@ public class ImageLoader {
} }
if (!added && alreadyLoadingCache != null) { if (!added && alreadyLoadingCache != null) {
alreadyLoadingCache.addImageReceiver(imageReceiver, key, filter, thumb != 0); alreadyLoadingCache.addImageReceiver(imageReceiver, key, filter, imageType);
added = true; added = true;
} }
if (!added && alreadyLoadingUrl != null) { if (!added && alreadyLoadingUrl != null) {
alreadyLoadingUrl.addImageReceiver(imageReceiver, key, filter, thumb != 0); alreadyLoadingUrl.addImageReceiver(imageReceiver, key, filter, imageType);
added = true; added = true;
} }
} }
@ -1996,8 +2036,38 @@ public class ImageLoader {
} }
} else if (thumb == 0 && currentKeyQuality) { } else if (thumb == 0 && currentKeyQuality) {
onlyCache = true; onlyCache = true;
MessageObject parentMessageObject = (MessageObject) parentObject;
TLRPC.Document parentDocument = parentMessageObject.getDocument(); TLRPC.Document parentDocument;
String localPath;
File cachePath;
boolean forceCache;
String fileName;
int fileType;
boolean bigThumb;
if (parentObject instanceof MessageObject) {
MessageObject parentMessageObject = (MessageObject) parentObject;
parentDocument = parentMessageObject.getDocument();
localPath = parentMessageObject.messageOwner.attachPath;
cachePath = FileLoader.getPathToMessage(parentMessageObject.messageOwner);
fileType = parentMessageObject.getFileType();
bigThumb = false;
} else if (qualityDocument != null) {
parentDocument = qualityDocument;
cachePath = FileLoader.getPathToAttach(parentDocument, true);
if (MessageObject.isVideoDocument(parentDocument)) {
fileType = FileLoader.MEDIA_DIR_VIDEO;
} else {
fileType = FileLoader.MEDIA_DIR_DOCUMENT;
}
localPath = null;
bigThumb = true;
} else {
parentDocument = null;
localPath = null;
cachePath = null;
fileType = 0;
bigThumb = false;
}
if (parentDocument != null) { if (parentDocument != null) {
if (finalIsNeedsQualityThumb) { if (finalIsNeedsQualityThumb) {
cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), "q_" + parentDocument.dc_id + "_" + parentDocument.id + ".jpg"); cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), "q_" + parentDocument.dc_id + "_" + parentDocument.id + ".jpg");
@ -2009,23 +2079,24 @@ public class ImageLoader {
} }
File attachPath = null; File attachPath = null;
if (parentMessageObject.messageOwner.attachPath != null && parentMessageObject.messageOwner.attachPath.length() > 0) { if (!TextUtils.isEmpty(localPath)) {
attachPath = new File(parentMessageObject.messageOwner.attachPath); attachPath = new File(localPath);
if (!attachPath.exists()) { if (!attachPath.exists()) {
attachPath = null; attachPath = null;
} }
} }
if (attachPath == null) { if (attachPath == null) {
attachPath = FileLoader.getPathToMessage(parentMessageObject.messageOwner); attachPath = cachePath;
} }
if (cacheFile == null) { if (cacheFile == null) {
String location = parentMessageObject.getFileName(); String location = FileLoader.getAttachFileName(parentDocument);
ThumbGenerateInfo info = waitingForQualityThumb.get(location); ThumbGenerateInfo info = waitingForQualityThumb.get(location);
if (info == null) { if (info == null) {
info = new ThumbGenerateInfo(); info = new ThumbGenerateInfo();
info.parentDocument = parentDocument; info.parentDocument = parentDocument;
info.filter = filter; info.filter = filter;
info.big = bigThumb;
waitingForQualityThumb.put(location, info); waitingForQualityThumb.put(location, info);
} }
if (!info.imageReceiverArray.contains(imageReceiver)) { if (!info.imageReceiverArray.contains(imageReceiver)) {
@ -2033,7 +2104,7 @@ public class ImageLoader {
} }
waitingForQualityThumbByTag.put(finalTag, location); waitingForQualityThumbByTag.put(finalTag, location);
if (attachPath.exists() && shouldGenerateQualityThumb) { if (attachPath.exists() && shouldGenerateQualityThumb) {
generateThumb(parentMessageObject.getFileType(), attachPath, info); generateThumb(fileType, attachPath, info);
} }
return; return;
} }
@ -2063,7 +2134,7 @@ public class ImageLoader {
img.secureDocument = (SecureDocument) imageLocation; img.secureDocument = (SecureDocument) imageLocation;
onlyCache = img.secureDocument.secureFile.dc_id == Integer.MIN_VALUE; onlyCache = img.secureDocument.secureFile.dc_id == Integer.MIN_VALUE;
cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), url); cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), url);
} else if (cacheType != 0 || size <= 0 || imageLocation instanceof String || isEncrypted) { } else if (!VIDEO_FILTER.equals(filter) && (cacheType != 0 || size <= 0 || imageLocation instanceof String || isEncrypted)) {
cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), url); cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), url);
if (cacheFile.exists()) { if (cacheFile.exists()) {
cacheFileExists = true; cacheFileExists = true;
@ -2071,11 +2142,22 @@ public class ImageLoader {
cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), url + ".enc"); cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), url + ".enc");
} }
} else if (imageLocation instanceof TLRPC.Document) { } else if (imageLocation instanceof TLRPC.Document) {
if (MessageObject.isVideoDocument((TLRPC.Document) imageLocation)) { TLRPC.Document document = (TLRPC.Document) imageLocation;
if (document instanceof TLRPC.TL_documentEncrypted) {
cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), url);
} else if (MessageObject.isVideoDocument(document)) {
cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_VIDEO), url); cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_VIDEO), url);
} else { } else {
cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_DOCUMENT), url); cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_DOCUMENT), url);
} }
if (VIDEO_FILTER.equals(filter)) {
img.animatedFile = true;
img.size = document.size;
onlyCache = true;
if (!cacheFile.exists()) {
cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), document.dc_id + "_" + document.id + ".temp");
}
}
} else if (imageLocation instanceof WebFile) { } else if (imageLocation instanceof WebFile) {
cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_DOCUMENT), url); cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_DOCUMENT), url);
} else { } else {
@ -2083,16 +2165,17 @@ public class ImageLoader {
} }
} }
img.selfThumb = thumb != 0; img.imageType = imageType;
img.key = key; img.key = key;
img.filter = filter; img.filter = filter;
img.location = imageLocation; img.location = imageLocation;
img.ext = ext; img.ext = ext;
img.currentAccount = currentAccount; img.currentAccount = currentAccount;
img.parentObject = parentObject;
if (cacheType == 2) { if (cacheType == 2) {
img.encryptionKeyPath = new File(FileLoader.getInternalCacheDir(), url + ".enc.key"); img.encryptionKeyPath = new File(FileLoader.getInternalCacheDir(), url + ".enc.key");
} }
img.addImageReceiver(imageReceiver, key, filter, thumb != 0); img.addImageReceiver(imageReceiver, key, filter, imageType);
if (onlyCache || cacheFileExists || cacheFile.exists()) { if (onlyCache || cacheFileExists || cacheFile.exists()) {
img.finalFilePath = cacheFile; img.finalFilePath = cacheFile;
img.location = imageLocation; img.location = imageLocation;
@ -2160,25 +2243,37 @@ public class ImageLoader {
} }
boolean imageSet = false; boolean imageSet = false;
String key = imageReceiver.getKey(); String mediaKey = imageReceiver.getMediaKey();
if (key != null) { if (mediaKey != null) {
BitmapDrawable bitmapDrawable = memCache.get(key); BitmapDrawable bitmapDrawable = memCache.get(mediaKey);
if (bitmapDrawable != null) { if (bitmapDrawable != null) {
cancelLoadingForImageReceiver(imageReceiver, 0); cancelLoadingForImageReceiver(imageReceiver, true);
imageReceiver.setImageBitmapByKey(bitmapDrawable, key, false, true); imageReceiver.setImageBitmapByKey(bitmapDrawable, mediaKey, ImageReceiver.TYPE_MEDIA, true);
imageSet = true; imageSet = true;
if (!imageReceiver.isForcePreview()) { if (!imageReceiver.isForcePreview()) {
return; return;
} }
} }
} }
String imageKey = imageReceiver.getImageKey();
if (!imageSet && imageKey != null) {
BitmapDrawable bitmapDrawable = memCache.get(imageKey);
if (bitmapDrawable != null) {
cancelLoadingForImageReceiver(imageReceiver, true);
imageReceiver.setImageBitmapByKey(bitmapDrawable, imageKey, ImageReceiver.TYPE_IMAGE, true);
imageSet = true;
if (!imageReceiver.isForcePreview() && mediaKey == null) {
return;
}
}
}
boolean thumbSet = false; boolean thumbSet = false;
String thumbKey = imageReceiver.getThumbKey(); String thumbKey = imageReceiver.getThumbKey();
if (thumbKey != null) { if (thumbKey != null) {
BitmapDrawable bitmapDrawable = memCache.get(thumbKey); BitmapDrawable bitmapDrawable = memCache.get(thumbKey);
if (bitmapDrawable != null) { if (bitmapDrawable != null) {
imageReceiver.setImageBitmapByKey(bitmapDrawable, thumbKey, true, true); imageReceiver.setImageBitmapByKey(bitmapDrawable, thumbKey, ImageReceiver.TYPE_THUMB, true);
cancelLoadingForImageReceiver(imageReceiver, 1); cancelLoadingForImageReceiver(imageReceiver, false);
if (imageSet && imageReceiver.isForcePreview()) { if (imageSet && imageReceiver.isForcePreview()) {
return; return;
} }
@ -2188,61 +2283,79 @@ public class ImageLoader {
boolean qualityThumb = false; boolean qualityThumb = false;
Object parentObject = imageReceiver.getParentObject(); Object parentObject = imageReceiver.getParentObject();
TLRPC.Document qualityDocument = imageReceiver.getQulityThumbDocument();
Object thumbLocation = imageReceiver.getThumbLocation(); Object thumbLocation = imageReceiver.getThumbLocation();
Object mediaLocation = imageReceiver.getMediaLocation();
Object imageLocation = imageReceiver.getImageLocation(); Object imageLocation = imageReceiver.getImageLocation();
if (imageLocation == null && imageReceiver.isNeedsQualityThumb() && imageReceiver.isCurrentKeyQuality() && parentObject instanceof MessageObject) { if (imageLocation == null && imageReceiver.isNeedsQualityThumb() && imageReceiver.isCurrentKeyQuality()) {
imageLocation = ((MessageObject) parentObject).getDocument(); if (parentObject instanceof MessageObject) {
qualityThumb = true; imageLocation = ((MessageObject) parentObject).getDocument();
qualityThumb = true;
} else if (qualityDocument != null) {
imageLocation = qualityDocument;
qualityThumb = true;
}
} }
boolean saveImageToCache = false; boolean saveImageToCache = false;
String url = null; String imageUrl = null;
String thumbUrl = null; String thumbUrl = null;
key = null; String mediaUrl = null;
imageKey = null;
thumbKey = null; thumbKey = null;
mediaKey = null;
String ext = imageReceiver.getExt(); String ext = imageReceiver.getExt();
if (ext == null) { if (ext == null) {
ext = "jpg"; ext = "jpg";
} }
if (imageLocation != null) {
if (imageLocation instanceof String) { for (int a = 0; a < 2; a++) {
String location = (String) imageLocation; Object object;
if (a == 0) {
object = imageLocation;
} else {
object = mediaLocation;
}
if (object == null) {
continue;
}
String key = null;
String url = null;
if (object instanceof String) {
String location = (String) object;
key = Utilities.MD5(location); key = Utilities.MD5(location);
url = key + "." + getHttpUrlExtension(location, "jpg"); url = key + "." + getHttpUrlExtension(location, "jpg");
} else if (imageLocation instanceof TLRPC.FileLocation) { } else if (object instanceof TLRPC.FileLocation) {
TLRPC.FileLocation location = (TLRPC.FileLocation) imageLocation; TLRPC.FileLocation location = (TLRPC.FileLocation) object;
key = location.volume_id + "_" + location.local_id; key = location.volume_id + "_" + location.local_id;
url = key + "." + ext; url = key + "." + ext;
if (imageReceiver.getExt() != null || location.key != null || location.volume_id == Integer.MIN_VALUE && location.local_id < 0) { if (imageReceiver.getExt() != null || location.key != null || location.volume_id == Integer.MIN_VALUE && location.local_id < 0) {
saveImageToCache = true; saveImageToCache = true;
} }
} else if (imageLocation instanceof TLRPC.TL_photoStrippedSize) { } else if (object instanceof TLRPC.TL_photoStrippedSize) {
TLRPC.TL_photoStrippedSize location = (TLRPC.TL_photoStrippedSize) imageLocation; TLRPC.TL_photoStrippedSize location = (TLRPC.TL_photoStrippedSize) object;
key = "stripped" + FileRefController.getKeyForParentObject(parentObject); key = "stripped" + FileRefController.getKeyForParentObject(parentObject);
url = key + "." + ext; url = key + "." + ext;
} else if (imageLocation instanceof TLRPC.TL_photoSize) { } else if (object instanceof TLRPC.TL_photoSize) {
TLRPC.TL_photoSize photoSize = (TLRPC.TL_photoSize) imageLocation; TLRPC.TL_photoSize photoSize = (TLRPC.TL_photoSize) object;
key = photoSize.location.volume_id + "_" + photoSize.location.local_id; key = photoSize.location.volume_id + "_" + photoSize.location.local_id;
url = key + "." + ext; url = key + "." + ext;
if (imageReceiver.getExt() != null || photoSize.location.key != null || photoSize.location.volume_id == Integer.MIN_VALUE && photoSize.location.local_id < 0) { if (imageReceiver.getExt() != null || photoSize.location.key != null || photoSize.location.volume_id == Integer.MIN_VALUE && photoSize.location.local_id < 0) {
saveImageToCache = true; saveImageToCache = true;
} }
} else if (imageLocation instanceof WebFile) { } else if (object instanceof WebFile) {
WebFile document = (WebFile) imageLocation; WebFile document = (WebFile) object;
String defaultExt = FileLoader.getExtensionByMime(document.mime_type); String defaultExt = FileLoader.getMimeTypePart(document.mime_type);
key = Utilities.MD5(document.url); key = Utilities.MD5(document.url);
url = key + "." + getHttpUrlExtension(document.url, defaultExt); url = key + "." + getHttpUrlExtension(document.url, defaultExt);
} else if (imageLocation instanceof SecureDocument) { } else if (object instanceof SecureDocument) {
SecureDocument document = (SecureDocument) imageLocation; SecureDocument document = (SecureDocument) object;
key = document.secureFile.dc_id + "_" + document.secureFile.id; key = document.secureFile.dc_id + "_" + document.secureFile.id;
url = key + "." + ext; url = key + "." + ext;
if (thumbKey != null) { } else if (object instanceof TLRPC.Document) {
thumbUrl = thumbKey + "." + ext; TLRPC.Document document = (TLRPC.Document) object;
}
} else if (imageLocation instanceof TLRPC.Document) {
TLRPC.Document document = (TLRPC.Document) imageLocation;
if (document.id != 0 && document.dc_id != 0) { if (document.id != 0 && document.dc_id != 0) {
if (qualityThumb) { if (a == 0 && qualityThumb) {
key = "q_" + document.dc_id + "_" + document.id; key = "q_" + document.dc_id + "_" + document.id;
} else { } else {
key = document.dc_id + "_" + document.id; key = document.dc_id + "_" + document.id;
@ -2256,23 +2369,35 @@ public class ImageLoader {
docExt = docExt.substring(idx); docExt = docExt.substring(idx);
} }
if (docExt.length() <= 1) { if (docExt.length() <= 1) {
if (document.mime_type != null && document.mime_type.equals("video/mp4")) { if ("video/mp4".equals(document.mime_type)) {
docExt = ".mp4"; docExt = ".mp4";
} else if ("video/x-matroska".equals(document.mime_type)) {
docExt = ".mkv";
} else { } else {
docExt = ""; docExt = "";
} }
} }
url = key + docExt; url = key + docExt;
if (thumbKey != null) { saveImageToCache = !MessageObject.isVideoDocument(document) && !MessageObject.isGifDocument(document) && !MessageObject.isRoundVideoDocument(document) && !MessageObject.canPreviewDocument(document);
thumbUrl = thumbKey + "." + ext;
}
saveImageToCache = !MessageObject.isGifDocument(document) && !MessageObject.isRoundVideoDocument((TLRPC.Document) imageLocation) && !MessageObject.canPreviewDocument(document);
} }
} }
if (imageLocation == thumbLocation) { if (a == 0) {
imageLocation = null; imageKey = key;
key = null; imageUrl = url;
url = null; } else {
mediaKey = key;
mediaUrl = url;
}
if (object == thumbLocation) {
if (a == 0) {
imageLocation = null;
imageKey = null;
imageUrl = null;
} else {
mediaLocation = null;
mediaKey = null;
mediaUrl = null;
}
} }
} }
@ -2294,29 +2419,45 @@ public class ImageLoader {
thumbUrl = thumbKey + "." + ext; thumbUrl = thumbKey + "." + ext;
} }
String filter = imageReceiver.getFilter(); String mediaFilter = imageReceiver.getMediaFilter();
String imageFilter = imageReceiver.getImageFilter();
String thumbFilter = imageReceiver.getThumbFilter(); String thumbFilter = imageReceiver.getThumbFilter();
if (key != null && filter != null) { if (mediaKey != null && mediaFilter != null) {
key += "@" + filter; mediaKey += "@" + mediaFilter;
}
if (imageKey != null && imageFilter != null) {
imageKey += "@" + imageFilter;
} }
if (thumbKey != null && thumbFilter != null) { if (thumbKey != null && thumbFilter != null) {
thumbKey += "@" + thumbFilter; thumbKey += "@" + thumbFilter;
} }
int cacheType;
int thumbCacheType;
if (imageLocation instanceof String) { if (imageLocation instanceof String) {
cacheType = 1; createLoadOperationForImageReceiver(imageReceiver, thumbKey, thumbUrl, ext, thumbLocation, thumbFilter, 0, 1, ImageReceiver.TYPE_THUMB, thumbSet ? 2 : 1);
thumbCacheType = 1; createLoadOperationForImageReceiver(imageReceiver, imageKey, imageUrl, ext, imageLocation, imageFilter, imageReceiver.getSize(), 1, ImageReceiver.TYPE_IMAGE, 0);
} else { } else if (mediaLocation != null) {
cacheType = imageReceiver.getCacheType(); int mediaCacheType = imageReceiver.getCacheType();
if (cacheType == 0 && saveImageToCache) { int imageCacheType = 1;
cacheType = 1; if (mediaCacheType == 0 && saveImageToCache) {
mediaCacheType = 1;
} }
thumbCacheType = cacheType == 0 ? 1 : cacheType; int thumbCacheType = mediaCacheType == 0 ? 1 : mediaCacheType;
if (!thumbSet) {
createLoadOperationForImageReceiver(imageReceiver, thumbKey, thumbUrl, ext, thumbLocation, thumbFilter, 0, thumbCacheType, ImageReceiver.TYPE_THUMB, thumbSet ? 2 : 1);
}
if (!imageSet) {
createLoadOperationForImageReceiver(imageReceiver, imageKey, imageUrl, ext, imageLocation, imageFilter, 0, imageCacheType, ImageReceiver.TYPE_IMAGE, 0);
}
createLoadOperationForImageReceiver(imageReceiver, mediaKey, mediaUrl, ext, mediaLocation, mediaFilter, imageReceiver.getSize(), mediaCacheType, ImageReceiver.TYPE_MEDIA, 0);
} else {
int imageCacheType = imageReceiver.getCacheType();
if (imageCacheType == 0 && saveImageToCache) {
imageCacheType = 1;
}
int thumbCacheType = imageCacheType == 0 ? 1 : imageCacheType;
createLoadOperationForImageReceiver(imageReceiver, thumbKey, thumbUrl, ext, thumbLocation, thumbFilter, 0, thumbCacheType, ImageReceiver.TYPE_THUMB, thumbSet ? 2 : 1);
createLoadOperationForImageReceiver(imageReceiver, imageKey, imageUrl, ext, imageLocation, imageFilter, imageReceiver.getSize(), imageCacheType, ImageReceiver.TYPE_IMAGE, 0);
} }
createLoadOperationForImageReceiver(imageReceiver, thumbKey, thumbUrl, ext, thumbLocation, thumbFilter, 0, thumbCacheType, thumbSet ? 2 : 1);
createLoadOperationForImageReceiver(imageReceiver, key, url, ext, imageLocation, filter, imageReceiver.getSize(), cacheType, 0);
} }
private void httpFileLoadError(final String location) { private void httpFileLoadError(final String location) {
@ -2361,7 +2502,7 @@ public class ImageLoader {
for (int a = 0; a < img.imageReceiverArray.size(); a++) { for (int a = 0; a < img.imageReceiverArray.size(); a++) {
String key = img.keys.get(a); String key = img.keys.get(a);
String filter = img.filters.get(a); String filter = img.filters.get(a);
Boolean thumb = img.thumbs.get(a); int imageType = img.thumbs.get(a);
ImageReceiver imageReceiver = img.imageReceiverArray.get(a); ImageReceiver imageReceiver = img.imageReceiverArray.get(a);
CacheImage cacheImage = imageLoadingByKeys.get(key); CacheImage cacheImage = imageLoadingByKeys.get(key);
if (cacheImage == null) { if (cacheImage == null) {
@ -2371,7 +2512,7 @@ public class ImageLoader {
cacheImage.finalFilePath = finalFile; cacheImage.finalFilePath = finalFile;
cacheImage.key = key; cacheImage.key = key;
cacheImage.location = img.location; cacheImage.location = img.location;
cacheImage.selfThumb = thumb; cacheImage.imageType = imageType;
cacheImage.ext = img.ext; cacheImage.ext = img.ext;
cacheImage.encryptionKeyPath = img.encryptionKeyPath; cacheImage.encryptionKeyPath = img.encryptionKeyPath;
cacheImage.cacheTask = new CacheOutTask(cacheImage); cacheImage.cacheTask = new CacheOutTask(cacheImage);
@ -2380,11 +2521,11 @@ public class ImageLoader {
imageLoadingByKeys.put(key, cacheImage); imageLoadingByKeys.put(key, cacheImage);
tasks.add(cacheImage.cacheTask); tasks.add(cacheImage.cacheTask);
} }
cacheImage.addImageReceiver(imageReceiver, key, filter, thumb); cacheImage.addImageReceiver(imageReceiver, key, filter, imageType);
} }
for (int a = 0; a < tasks.size(); a++) { for (int a = 0; a < tasks.size(); a++) {
CacheOutTask task = tasks.get(a); CacheOutTask task = tasks.get(a);
if (task.cacheImage.selfThumb) { if (task.cacheImage.imageType == ImageReceiver.TYPE_THUMB) {
cacheThumbOutQueue.postRunnable(task); cacheThumbOutQueue.postRunnable(task);
} else { } else {
cacheOutQueue.postRunnable(task); cacheOutQueue.postRunnable(task);

View File

@ -366,6 +366,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
private float seekToProgressPending; private float seekToProgressPending;
private long lastProgress = 0; private long lastProgress = 0;
private MessageObject playingMessageObject; private MessageObject playingMessageObject;
private MessageObject goingToShowMessageObject;
private Timer progressTimer = null; private Timer progressTimer = null;
private final Object progressTimerSync = new Object(); private final Object progressTimerSync = new Object();
private ArrayList<MessageObject> playlist = new ArrayList<>(); private ArrayList<MessageObject> playlist = new ArrayList<>();
@ -376,6 +377,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
private boolean playMusicAgain; private boolean playMusicAgain;
private AudioInfo audioInfo; private AudioInfo audioInfo;
private VideoPlayer videoPlayer; private VideoPlayer videoPlayer;
private boolean playerWasReady;
private TextureView currentTextureView; private TextureView currentTextureView;
private PipRoundVideoView pipRoundVideoView; private PipRoundVideoView pipRoundVideoView;
private int pipSwitchingState; private int pipSwitchingState;
@ -389,6 +391,16 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
private float currentAspectRatioFrameLayoutRatio; private float currentAspectRatioFrameLayoutRatio;
private boolean currentAspectRatioFrameLayoutReady; private boolean currentAspectRatioFrameLayoutReady;
private Runnable setLoadingRunnable = new Runnable() {
@Override
public void run() {
if (playingMessageObject == null) {
return;
}
FileLoader.getInstance(playingMessageObject.currentAccount).setLoadingVideo(playingMessageObject.getDocument(), true, false);
}
};
private AudioRecord audioRecorder; private AudioRecord audioRecorder;
private TLRPC.TL_document recordingAudio; private TLRPC.TL_document recordingAudio;
private int recordingCurrentAccount; private int recordingCurrentAccount;
@ -869,11 +881,11 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (videoPlayer != null) { if (videoPlayer != null) {
duration = videoPlayer.getDuration(); duration = videoPlayer.getDuration();
progress = videoPlayer.getCurrentPosition(); progress = videoPlayer.getCurrentPosition();
bufferedValue = videoPlayer.getBufferedPosition() / (float) duration; if (duration == C.TIME_UNSET || progress == C.TIME_UNSET || progress < 0 || duration <= 0) {
value = duration >= 0 ? progress / (float) duration : 0.0f;
if (progress < 0) {
return; return;
} }
bufferedValue = videoPlayer.getBufferedPosition() / (float) duration;
value = duration >= 0 ? progress / (float) duration : 0.0f;
if (value >= 1) { if (value >= 1) {
return; return;
} }
@ -1420,7 +1432,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (!paused) { if (!paused) {
videoPlayer.play(); videoPlayer.play();
} else { } else {
videoPlayer.pause(); pauseMessage(playingMessageObject);
} }
} else { } else {
boolean post = audioPlayer != null; boolean post = audioPlayer != null;
@ -1519,13 +1531,13 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} }
public void cleanupPlayer(boolean notify, boolean stopService) { public void cleanupPlayer(boolean notify, boolean stopService) {
cleanupPlayer(notify, stopService, false); cleanupPlayer(notify, stopService, false, false);
} }
public void cleanupPlayer(boolean notify, boolean stopService, boolean byVoiceEnd) { public void cleanupPlayer(boolean notify, boolean stopService, boolean byVoiceEnd, boolean transferPlayerToPhotoViewer) {
if (audioPlayer != null) { if (audioPlayer != null) {
try { try {
audioPlayer.releasePlayer(); audioPlayer.releasePlayer(true);
} catch (Exception e) { } catch (Exception e) {
FileLog.e(e); FileLog.e(e);
} }
@ -1534,14 +1546,31 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
currentAspectRatioFrameLayout = null; currentAspectRatioFrameLayout = null;
currentTextureViewContainer = null; currentTextureViewContainer = null;
currentAspectRatioFrameLayoutReady = false; currentAspectRatioFrameLayoutReady = false;
isDrawingWasReady = false;
currentTextureView = null; currentTextureView = null;
videoPlayer.releasePlayer(); goingToShowMessageObject = null;
videoPlayer = null; if (transferPlayerToPhotoViewer) {
PhotoViewer.getInstance().injectVideoPlayer(videoPlayer);
goingToShowMessageObject = playingMessageObject;
NotificationCenter.getInstance(playingMessageObject.currentAccount).postNotificationName(NotificationCenter.messagePlayingGoingToStop, playingMessageObject, true);
} else {
long position = videoPlayer.getCurrentPosition();
if (playingMessageObject != null && playingMessageObject.isVideo() && position > 0 && position != C.TIME_UNSET) {
playingMessageObject.audioProgressMs = (int) position;
NotificationCenter.getInstance(playingMessageObject.currentAccount).postNotificationName(NotificationCenter.messagePlayingGoingToStop, playingMessageObject, false);
}
videoPlayer.releasePlayer(true);
videoPlayer = null;
}
try { try {
baseActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); baseActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} catch (Exception e) { } catch (Exception e) {
FileLog.e(e); FileLog.e(e);
} }
if (playingMessageObject != null && !transferPlayerToPhotoViewer) {
AndroidUtilities.cancelRunOnUIThread(setLoadingRunnable);
FileLoader.getInstance(playingMessageObject.currentAccount).removeLoadingVideo(playingMessageObject.getDocument(), true, false);
}
} }
stopProgressTimer(); stopProgressTimer();
lastProgress = 0; lastProgress = 0;
@ -1606,6 +1635,14 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} }
} }
public boolean isGoingToShowMessageObject(MessageObject messageObject) {
return goingToShowMessageObject == messageObject;
}
public void resetGoingToShowMessageObject() {
goingToShowMessageObject = null;
}
private boolean isSamePlayingMessage(MessageObject messageObject) { private boolean isSamePlayingMessage(MessageObject messageObject) {
return playingMessageObject != null && playingMessageObject.getDialogId() == messageObject.getDialogId() && playingMessageObject.getId() == messageObject.getId() && ((playingMessageObject.eventId == 0) == (messageObject.eventId == 0)); return playingMessageObject != null && playingMessageObject.getDialogId() == messageObject.getDialogId() && playingMessageObject.getId() == messageObject.getId() && ((playingMessageObject.eventId == 0) == (messageObject.eventId == 0));
} }
@ -1754,7 +1791,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (audioPlayer != null || videoPlayer != null) { if (audioPlayer != null || videoPlayer != null) {
if (audioPlayer != null) { if (audioPlayer != null) {
try { try {
audioPlayer.releasePlayer(); audioPlayer.releasePlayer(true);
} catch (Exception e) { } catch (Exception e) {
FileLog.e(e); FileLog.e(e);
} }
@ -1764,13 +1801,15 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
currentTextureViewContainer = null; currentTextureViewContainer = null;
currentAspectRatioFrameLayoutReady = false; currentAspectRatioFrameLayoutReady = false;
currentTextureView = null; currentTextureView = null;
videoPlayer.releasePlayer(); videoPlayer.releasePlayer(true);
videoPlayer = null; videoPlayer = null;
try { try {
baseActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); baseActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} catch (Exception e) { } catch (Exception e) {
FileLog.e(e); FileLog.e(e);
} }
AndroidUtilities.cancelRunOnUIThread(setLoadingRunnable);
FileLoader.getInstance(playingMessageObject.currentAccount).removeLoadingVideo(playingMessageObject.getDocument(), true, false);
} }
stopProgressTimer(); stopProgressTimer();
lastProgress = 0; lastProgress = 0;
@ -1848,7 +1887,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} }
private void checkIsNextMusicFileDownloaded(int currentAccount) { private void checkIsNextMusicFileDownloaded(int currentAccount) {
if ((DownloadController.getInstance(currentAccount).getCurrentDownloadMask() & DownloadController.AUTODOWNLOAD_MASK_MUSIC) == 0) { if (!DownloadController.getInstance(currentAccount).canDownloadNextTrack()) {
return; return;
} }
ArrayList<MessageObject> currentPlayList = SharedConfig.shuffleMusic ? shuffledPlaylist : playlist; ArrayList<MessageObject> currentPlayList = SharedConfig.shuffleMusic ? shuffledPlaylist : playlist;
@ -1872,9 +1911,6 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} }
MessageObject nextAudio = currentPlayList.get(nextIndex); MessageObject nextAudio = currentPlayList.get(nextIndex);
if (!DownloadController.getInstance(currentAccount).canDownloadMedia(nextAudio)) {
return;
}
File file = null; File file = null;
if (!TextUtils.isEmpty(nextAudio.messageOwner.attachPath)) { if (!TextUtils.isEmpty(nextAudio.messageOwner.attachPath)) {
file = new File(nextAudio.messageOwner.attachPath); file = new File(nextAudio.messageOwner.attachPath);
@ -1926,7 +1962,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} }
} }
public void setCurrentRoundVisible(boolean visible) { public void setCurrentVideoVisible(boolean visible) {
if (currentAspectRatioFrameLayout == null) { if (currentAspectRatioFrameLayout == null) {
return; return;
} }
@ -1988,9 +2024,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (currentAspectRatioFrameLayout != null) { if (currentAspectRatioFrameLayout != null) {
currentAspectRatioFrameLayout.setAspectRatio(currentAspectRatioFrameLayoutRatio, currentAspectRatioFrameLayoutRotation); currentAspectRatioFrameLayout.setAspectRatio(currentAspectRatioFrameLayoutRatio, currentAspectRatioFrameLayoutRotation);
} }
if (currentTextureViewContainer.getVisibility() != View.VISIBLE) { //if (currentTextureViewContainer.getVisibility() != View.VISIBLE) {
currentTextureViewContainer.setVisibility(View.VISIBLE); // currentTextureViewContainer.setVisibility(View.VISIBLE);
} //}
} }
} }
@ -2082,7 +2118,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} }
} }
final File cacheFile = file != null ? file : FileLoader.getPathToMessage(messageObject.messageOwner); final File cacheFile = file != null ? file : FileLoader.getPathToMessage(messageObject.messageOwner);
boolean canStream = SharedConfig.streamMedia && messageObject.isMusic() && (int) messageObject.getDialogId() != 0; boolean canStream = SharedConfig.streamMedia && (messageObject.isMusic() || messageObject.isVideo() && messageObject.canStreamVideo()) && (int) messageObject.getDialogId() != 0;
if (cacheFile != null && cacheFile != file && !(exists = cacheFile.exists()) && !canStream) { if (cacheFile != null && cacheFile != file && !(exists = cacheFile.exists()) && !canStream) {
FileLoader.getInstance(messageObject.currentAccount).loadFile(messageObject.getDocument(), messageObject, 0, 0); FileLoader.getInstance(messageObject.currentAccount).loadFile(messageObject.getDocument(), messageObject, 0, 0);
downloadingCurrentMessage = true; downloadingCurrentMessage = true;
@ -2120,7 +2156,14 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
isDrawingWasReady = false; isDrawingWasReady = false;
currentAspectRatioFrameLayout.setDrawingReady(false); currentAspectRatioFrameLayout.setDrawingReady(false);
} }
if (messageObject.isRoundVideo()) { boolean isVideo = messageObject.isVideo();
if (messageObject.isRoundVideo() || isVideo) {
if (isVideo) {
FileLoader.getInstance(messageObject.currentAccount).setLoadingVideoForPlayer(messageObject.getDocument(), true);
}
playerWasReady = false;
boolean destroyAtEnd = !isVideo || messageObject.messageOwner.to_id.channel_id == 0 && messageObject.audioProgress <= 0.1f;
int[] playCount = isVideo && messageObject.getDuration() <= 30 ? new int[]{1} : null;
playlist.clear(); playlist.clear();
shuffledPlaylist.clear(); shuffledPlaylist.clear();
videoPlayer = new VideoPlayer(); videoPlayer = new VideoPlayer();
@ -2144,12 +2187,29 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} }
} }
if (playbackState == ExoPlayer.STATE_READY) { if (playbackState == ExoPlayer.STATE_READY) {
playerWasReady = true;
if (playingMessageObject != null && playingMessageObject.isVideo()) {
AndroidUtilities.cancelRunOnUIThread(setLoadingRunnable);
FileLoader.getInstance(messageObject.currentAccount).removeLoadingVideo(playingMessageObject.getDocument(), true, false);
}
currentAspectRatioFrameLayoutReady = true; currentAspectRatioFrameLayoutReady = true;
if (currentTextureViewContainer != null && currentTextureViewContainer.getVisibility() != View.VISIBLE) { } else if (playbackState == ExoPlayer.STATE_BUFFERING) {
currentTextureViewContainer.setVisibility(View.VISIBLE); if (playWhenReady && playingMessageObject != null && playingMessageObject.isVideo()) {
if (playerWasReady) {
setLoadingRunnable.run();
} else {
AndroidUtilities.runOnUIThread(setLoadingRunnable, 1000);
}
} }
} else if (videoPlayer.isPlaying() && playbackState == ExoPlayer.STATE_ENDED) { } else if (videoPlayer.isPlaying() && playbackState == ExoPlayer.STATE_ENDED) {
cleanupPlayer(true, true, true); if (playingMessageObject.isVideo() && !destroyAtEnd && (playCount == null || playCount[0] < 4)) {
videoPlayer.seekTo(0);
if (playCount != null) {
playCount[0]++;
}
} else {
cleanupPlayer(true, true, true, false);
}
} }
} }
@ -2178,9 +2238,10 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (currentAspectRatioFrameLayout != null && !currentAspectRatioFrameLayout.isDrawingReady()) { if (currentAspectRatioFrameLayout != null && !currentAspectRatioFrameLayout.isDrawingReady()) {
isDrawingWasReady = true; isDrawingWasReady = true;
currentAspectRatioFrameLayout.setDrawingReady(true); currentAspectRatioFrameLayout.setDrawingReady(true);
if (currentTextureViewContainer != null && currentTextureViewContainer.getVisibility() != View.VISIBLE) { currentTextureViewContainer.setTag(1);
currentTextureViewContainer.setVisibility(View.VISIBLE); //if (currentTextureViewContainer != null && currentTextureViewContainer.getVisibility() != View.VISIBLE) {
} // currentTextureViewContainer.setVisibility(View.VISIBLE);
//}
} }
} }
@ -2223,6 +2284,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} }
pipSwitchingState = 0; pipSwitchingState = 0;
return true; return true;
} else if (PhotoViewer.hasInstance() && PhotoViewer.getInstance().isInjectingVideoPlayer()) {
PhotoViewer.getInstance().injectVideoPlayerSurface(surfaceTexture);
return true;
} }
return false; return false;
} }
@ -2248,12 +2312,39 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} else if (currentTextureView != null) { } else if (currentTextureView != null) {
videoPlayer.setTextureView(currentTextureView); videoPlayer.setTextureView(currentTextureView);
} }
videoPlayer.preparePlayer(Uri.fromFile(cacheFile), "other");
videoPlayer.setStreamType(useFrontSpeaker ? AudioManager.STREAM_VOICE_CALL : AudioManager.STREAM_MUSIC); if (exists) {
if (currentPlaybackSpeed > 1.0f) { if (!messageObject.mediaExists && cacheFile != file) {
videoPlayer.setPlaybackSpeed(currentPlaybackSpeed); AndroidUtilities.runOnUIThread(() -> NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.fileDidLoad, FileLoader.getAttachFileName(messageObject.getDocument())));
}
videoPlayer.preparePlayer(Uri.fromFile(cacheFile), "other");
} else {
try {
int reference = FileLoader.getInstance(messageObject.currentAccount).getFileReference(messageObject);
TLRPC.Document document = messageObject.getDocument();
String params = "?account=" + messageObject.currentAccount +
"&id=" + document.id +
"&hash=" + document.access_hash +
"&dc=" + document.dc_id +
"&size=" + document.size +
"&mime=" + URLEncoder.encode(document.mime_type, "UTF-8") +
"&rid=" + reference +
"&name=" + URLEncoder.encode(FileLoader.getDocumentFileName(document), "UTF-8") +
"&reference=" + Utilities.bytesToHex(document.file_reference != null ? document.file_reference : new byte[0]);
Uri uri = Uri.parse("tg://" + messageObject.getFileName() + params);
videoPlayer.preparePlayer(uri, "other");
} catch (Exception e) {
FileLog.e(e);
}
}
if (messageObject.isRoundVideo()) {
videoPlayer.setStreamType(useFrontSpeaker ? AudioManager.STREAM_VOICE_CALL : AudioManager.STREAM_MUSIC);
if (currentPlaybackSpeed > 1.0f) {
videoPlayer.setPlaybackSpeed(currentPlaybackSpeed);
}
} else {
videoPlayer.setStreamType(AudioManager.STREAM_MUSIC);
} }
videoPlayer.play();
} else { } else {
if (pipRoundVideoView != null) { if (pipRoundVideoView != null) {
pipRoundVideoView.close(true); pipRoundVideoView.close(true);
@ -2268,7 +2359,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (!playlist.isEmpty() && playlist.size() > 1) { if (!playlist.isEmpty() && playlist.size() > 1) {
playNextMessageWithoutOrder(true); playNextMessageWithoutOrder(true);
} else { } else {
cleanupPlayer(true, true, messageObject != null && messageObject.isVoice()); cleanupPlayer(true, true, messageObject != null && messageObject.isVoice(), false);
} }
} else if (seekToProgressPending != 0 && (playbackState == ExoPlayer.STATE_READY || playbackState == ExoPlayer.STATE_IDLE)) { } else if (seekToProgressPending != 0 && (playbackState == ExoPlayer.STATE_READY || playbackState == ExoPlayer.STATE_IDLE)) {
int seekTo = (int) (audioPlayer.getDuration() * seekToProgressPending); int seekTo = (int) (audioPlayer.getDuration() * seekToProgressPending);
@ -2343,7 +2434,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
FileLog.e(e); FileLog.e(e);
NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.messagePlayingPlayStateChanged, playingMessageObject != null ? playingMessageObject.getId() : 0); NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.messagePlayingPlayStateChanged, playingMessageObject != null ? playingMessageObject.getId() : 0);
if (audioPlayer != null) { if (audioPlayer != null) {
audioPlayer.releasePlayer(); audioPlayer.releasePlayer(true);
audioPlayer = null; audioPlayer = null;
isPaused = false; isPaused = false;
playingMessageObject = null; playingMessageObject = null;
@ -2367,11 +2458,15 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (videoPlayer != null) { if (videoPlayer != null) {
try { try {
if (playingMessageObject.audioProgress != 0) { if (playingMessageObject.audioProgress != 0) {
long duration = audioPlayer.getDuration(); long duration = videoPlayer.getDuration();
if (duration == C.TIME_UNSET) { if (duration == C.TIME_UNSET) {
duration = (long) playingMessageObject.getDuration() * 1000; duration = (long) playingMessageObject.getDuration() * 1000;
} }
int seekTo = (int) (duration * playingMessageObject.audioProgress); int seekTo = (int) (duration * playingMessageObject.audioProgress);
if (playingMessageObject.audioProgressMs != 0) {
seekTo = playingMessageObject.audioProgressMs;
playingMessageObject.audioProgressMs = 0;
}
videoPlayer.seekTo(seekTo); videoPlayer.seekTo(seekTo);
} }
} catch (Exception e2) { } catch (Exception e2) {
@ -2380,6 +2475,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.messagePlayingProgressDidChanged, playingMessageObject.getId(), 0); NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.messagePlayingProgressDidChanged, playingMessageObject.getId(), 0);
FileLog.e(e2); FileLog.e(e2);
} }
videoPlayer.play();
} else if (audioPlayer != null) { } else if (audioPlayer != null) {
try { try {
if (playingMessageObject.audioProgress != 0) { if (playingMessageObject.audioProgress != 0) {
@ -2397,7 +2493,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} }
} }
if (playingMessageObject.isMusic()) { if (playingMessageObject != null && playingMessageObject.isMusic()) {
Intent intent = new Intent(ApplicationLoader.applicationContext, MusicPlayerService.class); Intent intent = new Intent(ApplicationLoader.applicationContext, MusicPlayerService.class);
try { try {
/*if (Build.VERSION.SDK_INT >= 26) { /*if (Build.VERSION.SDK_INT >= 26) {
@ -2416,48 +2512,6 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
return true; return true;
} }
public void stopAudio() {
if (audioPlayer == null && videoPlayer == null || playingMessageObject == null) {
return;
}
try {
if (audioPlayer != null) {
audioPlayer.pause();
} else if (videoPlayer != null) {
videoPlayer.pause();
}
} catch (Exception e) {
FileLog.e(e);
}
try {
if (audioPlayer != null) {
audioPlayer.releasePlayer();
audioPlayer = null;
} else if (videoPlayer != null) {
currentAspectRatioFrameLayout = null;
currentTextureViewContainer = null;
currentAspectRatioFrameLayoutReady = false;
currentTextureView = null;
videoPlayer.releasePlayer();
videoPlayer = null;
try {
baseActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} catch (Exception e) {
FileLog.e(e);
}
}
} catch (Exception e) {
FileLog.e(e);
}
stopProgressTimer();
playingMessageObject = null;
downloadingCurrentMessage = false;
isPaused = false;
Intent intent = new Intent(ApplicationLoader.applicationContext, MusicPlayerService.class);
ApplicationLoader.applicationContext.stopService(intent);
}
public AudioInfo getAudioInfo() { public AudioInfo getAudioInfo() {
return audioInfo; return audioInfo;
} }
@ -2529,7 +2583,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
return true; return true;
} }
public boolean isRoundVideoDrawingReady() { public boolean isVideoDrawingReady() {
return currentAspectRatioFrameLayout != null && currentAspectRatioFrameLayout.isDrawingReady(); return currentAspectRatioFrameLayout != null && currentAspectRatioFrameLayout.isDrawingReady();
} }
@ -2551,6 +2605,10 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
return false; return false;
} }
public boolean isPlayingMessageAndReadyToDraw(MessageObject messageObject) {
return isDrawingWasReady && isPlayingMessage(messageObject);
}
public boolean isMessagePaused() { public boolean isMessagePaused() {
return isPaused || downloadingCurrentMessage; return isPaused || downloadingCurrentMessage;
} }
@ -3370,7 +3428,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
return -5; return -5;
} }
private void didWriteData(final MessageObject messageObject, final File file, final boolean last, final boolean error) { private void didWriteData(final MessageObject messageObject, final File file, final boolean last, long availableSize, final boolean error) {
final boolean firstWrite = videoConvertFirstWrite; final boolean firstWrite = videoConvertFirstWrite;
if (firstWrite) { if (firstWrite) {
videoConvertFirstWrite = false; videoConvertFirstWrite = false;
@ -3389,7 +3447,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (firstWrite) { if (firstWrite) {
NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.filePreparingStarted, messageObject, file.toString()); NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.filePreparingStarted, messageObject, file.toString());
} }
NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.fileNewChunkAvailable, messageObject, file.toString(), file.length(), last ? file.length() : 0); NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.fileNewChunkAvailable, messageObject, file.toString(), availableSize, last ? file.length() : 0);
} }
}); });
} }
@ -3481,8 +3539,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (end < 0 || info.presentationTimeUs < end) { if (end < 0 || info.presentationTimeUs < end) {
info.offset = 0; info.offset = 0;
info.flags = extractor.getSampleFlags(); info.flags = extractor.getSampleFlags();
if (mediaMuxer.writeSampleData(muxerTrackIndex, buffer, info, false)) { long availableSize = mediaMuxer.writeSampleData(muxerTrackIndex, buffer, info, false);
didWriteData(messageObject, file, false, false); if (availableSize != 0) {
didWriteData(messageObject, file, false, availableSize, false);
} }
} else { } else {
eof = true; eof = true;
@ -3600,7 +3659,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
boolean isPreviousOk = preferences.getBoolean("isPreviousOk", true); boolean isPreviousOk = preferences.getBoolean("isPreviousOk", true);
preferences.edit().putBoolean("isPreviousOk", false).commit(); preferences.edit().putBoolean("isPreviousOk", false).commit();
if (!inputFile.canRead() || !isPreviousOk) { if (!inputFile.canRead() || !isPreviousOk) {
didWriteData(messageObject, cacheFile, true, true); didWriteData(messageObject, cacheFile, true, 0, true);
preferences.edit().putBoolean("isPreviousOk", true).commit(); preferences.edit().putBoolean("isPreviousOk", true).commit();
return false; return false;
} }
@ -3814,8 +3873,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (info.size > 0 && (endTime < 0 || info.presentationTimeUs < endTime)) { if (info.size > 0 && (endTime < 0 || info.presentationTimeUs < endTime)) {
info.offset = 0; info.offset = 0;
info.flags = extractor.getSampleFlags(); info.flags = extractor.getSampleFlags();
if (mediaMuxer.writeSampleData(audioTrackIndex, audioBuffer, info, false)) { long availableSize = mediaMuxer.writeSampleData(audioTrackIndex, audioBuffer, info, false);
didWriteData(messageObject, cacheFile, false, false); if (availableSize != 0) {
didWriteData(messageObject, cacheFile, false, availableSize, false);
} }
} }
} else if (index == -1) { } else if (index == -1) {
@ -3860,8 +3920,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} }
if (info.size > 1) { if (info.size > 1) {
if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) { if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
if (mediaMuxer.writeSampleData(videoTrackIndex, encodedData, info, true)) { long availableSize = mediaMuxer.writeSampleData(videoTrackIndex, encodedData, info, true);
didWriteData(messageObject, cacheFile, false, false); if (availableSize != 0) {
didWriteData(messageObject, cacheFile, false, availableSize, false);
} }
} else if (videoTrackIndex == -5) { } else if (videoTrackIndex == -5) {
byte[] csd = new byte[info.size]; byte[] csd = new byte[info.size];
@ -4031,11 +4092,11 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} }
} else { } else {
preferences.edit().putBoolean("isPreviousOk", true).commit(); preferences.edit().putBoolean("isPreviousOk", true).commit();
didWriteData(messageObject, cacheFile, true, true); didWriteData(messageObject, cacheFile, true, 0, true);
return false; return false;
} }
preferences.edit().putBoolean("isPreviousOk", true).commit(); preferences.edit().putBoolean("isPreviousOk", true).commit();
didWriteData(messageObject, cacheFile, true, error); didWriteData(messageObject, cacheFile, true, 0, error);
return true; return true;
} }
} }

View File

@ -81,6 +81,7 @@ public class MessageObject {
public String monthKey; public String monthKey;
public boolean deleted; public boolean deleted;
public float audioProgress; public float audioProgress;
public int audioProgressMs;
public float bufferedProgress; public float bufferedProgress;
public float gifState; public float gifState;
public int audioProgressSec; public int audioProgressSec;
@ -100,6 +101,8 @@ public class MessageObject {
public boolean useCustomPhoto; public boolean useCustomPhoto;
public StringBuilder botButtonsLayout; public StringBuilder botButtonsLayout;
public boolean hadAnimationNotReadyLoading;
public boolean cancelEditing; public boolean cancelEditing;
public CharSequence editingMessage; public CharSequence editingMessage;
@ -422,6 +425,8 @@ public class MessageObject {
float maxAspectRatio = maxSizeWidth / maxSizeHeight; float maxAspectRatio = maxSizeWidth / maxSizeHeight;
averageAspectRatio = averageAspectRatio / count; averageAspectRatio = averageAspectRatio / count;
float minH = AndroidUtilities.dp(100) / maxSizeHeight;
if (!forceCalc && (count == 2 || count == 3 || count == 4)) { if (!forceCalc && (count == 2 || count == 3 || count == 4)) {
if (count == 2) { if (count == 2) {
GroupedMessagePosition position1 = posArray.get(0); GroupedMessagePosition position1 = posArray.get(0);
@ -484,6 +489,9 @@ public class MessageObject {
int width = maxSizeWidth / 2; int width = maxSizeWidth / 2;
float secondHeight = Math.min(maxSizeHeight - firstHeight, Math.round(Math.min(width / position2.aspectRatio, width / position3.aspectRatio))) / maxSizeHeight; float secondHeight = Math.min(maxSizeHeight - firstHeight, Math.round(Math.min(width / position2.aspectRatio, width / position3.aspectRatio))) / maxSizeHeight;
if (secondHeight < minH) {
secondHeight = minH;
}
position2.set(0, 0, 1, 1, width, secondHeight, POSITION_FLAG_LEFT | POSITION_FLAG_BOTTOM); position2.set(0, 0, 1, 1, width, secondHeight, POSITION_FLAG_LEFT | POSITION_FLAG_BOTTOM);
position3.set(1, 1, 1, 1, width, secondHeight, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM); position3.set(1, 1, 1, 1, width, secondHeight, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM);
maxX = 1; maxX = 1;
@ -509,6 +517,9 @@ public class MessageObject {
} }
h = Math.min(maxSizeHeight - h0, h); h = Math.min(maxSizeHeight - h0, h);
h /= maxSizeHeight; h /= maxSizeHeight;
if (h < minH) {
h = minH;
}
position2.set(0, 0, 1, 1, w0, h, POSITION_FLAG_LEFT | POSITION_FLAG_BOTTOM); position2.set(0, 0, 1, 1, w0, h, POSITION_FLAG_LEFT | POSITION_FLAG_BOTTOM);
position3.set(1, 1, 1, 1, w1, h, POSITION_FLAG_BOTTOM); position3.set(1, 1, 1, 1, w1, h, POSITION_FLAG_BOTTOM);
position4.set(2, 2, 1, 1, w2, h, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM); position4.set(2, 2, 1, 1, w2, h, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM);
@ -655,7 +666,7 @@ public class MessageObject {
posToFix = pos; posToFix = pos;
} }
} }
pos.set(k, k, i, i, width, lineHeight / maxSizeHeight, flags); pos.set(k, k, i, i, width, Math.max(minH, lineHeight / maxSizeHeight), flags);
index++; index++;
} }
posToFix.pw += spanLeft; posToFix.pw += spanLeft;
@ -2107,6 +2118,7 @@ public class MessageObject {
message.media.document = getDocumentWithId(webPage, pageBlockVideo.video_id); message.media.document = getDocumentWithId(webPage, pageBlockVideo.video_id);
} }
message.message = ""; message.message = "";
message.realId = getId();
message.id = Utilities.random.nextInt(); message.id = Utilities.random.nextInt();
message.date = messageOwner.date; message.date = messageOwner.date;
message.to_id = messageOwner.to_id; message.to_id = messageOwner.to_id;
@ -2381,7 +2393,7 @@ public class MessageObject {
} }
public static boolean isRoundVideoDocument(TLRPC.Document document) { public static boolean isRoundVideoDocument(TLRPC.Document document) {
if (document != null && document.mime_type != null && document.mime_type.equals("video/mp4")) { if (document != null && "video/mp4".equals(document.mime_type)) {
int width = 0; int width = 0;
int height = 0; int height = 0;
boolean round = false; boolean round = false;
@ -2401,7 +2413,7 @@ public class MessageObject {
} }
public static boolean isNewGifDocument(WebFile document) { public static boolean isNewGifDocument(WebFile document) {
if (document != null && document.mime_type != null && document.mime_type.equals("video/mp4")) { if (document != null && "video/mp4".equals(document.mime_type)) {
int width = 0; int width = 0;
int height = 0; int height = 0;
boolean animated = false; boolean animated = false;
@ -2422,7 +2434,7 @@ public class MessageObject {
} }
public static boolean isNewGifDocument(TLRPC.Document document) { public static boolean isNewGifDocument(TLRPC.Document document) {
if (document != null && document.mime_type != null && document.mime_type.equals("video/mp4")) { if (document != null && "video/mp4".equals(document.mime_type)) {
int width = 0; int width = 0;
int height = 0; int height = 0;
boolean animated = false; boolean animated = false;
@ -3390,7 +3402,7 @@ public class MessageObject {
} }
int selfUserId = UserConfig.getInstance(currentAccount).getClientUserId(); int selfUserId = UserConfig.getInstance(currentAccount).getClientUserId();
if (getDialogId() == selfUserId) { if (getDialogId() == selfUserId) {
return messageOwner.fwd_from.from_id == selfUserId || messageOwner.fwd_from.saved_from_peer != null && messageOwner.fwd_from.saved_from_peer.user_id == selfUserId; return messageOwner.fwd_from.from_id == selfUserId && (messageOwner.fwd_from.saved_from_peer == null || messageOwner.fwd_from.saved_from_peer.user_id == selfUserId) || messageOwner.fwd_from.saved_from_peer != null && messageOwner.fwd_from.saved_from_peer.user_id == selfUserId;
} }
return messageOwner.fwd_from.saved_from_peer == null || messageOwner.fwd_from.saved_from_peer.user_id == selfUserId; return messageOwner.fwd_from.saved_from_peer == null || messageOwner.fwd_from.saved_from_peer.user_id == selfUserId;
} }
@ -3453,9 +3465,19 @@ public class MessageObject {
return messageOwner.id; return messageOwner.id;
} }
public int getRealId() {
return messageOwner.realId != 0 ? messageOwner.realId : messageOwner.id;
}
public static int getMessageSize(TLRPC.Message message) { public static int getMessageSize(TLRPC.Message message) {
if (message.media != null && message.media.document != null) { TLRPC.Document document;
return message.media.document.size; if (message.media instanceof TLRPC.TL_messageMediaWebPage) {
document = message.media.webpage.document;
} else {
document = message.media != null ? message.media.document : null;
}
if (document != null) {
return document.size;
} }
return 0; return 0;
} }
@ -3570,9 +3592,9 @@ public class MessageObject {
return attribute.supports_streaming; return attribute.supports_streaming;
} }
} }
/*if ("video/x-matroska".equals(document.mime_type)) { if (SharedConfig.streamMkv && "video/x-matroska".equals(document.mime_type)) {
return true; return true;
}*/ }
return false; return false;
} }
@ -3733,9 +3755,9 @@ public class MessageObject {
if (isAnimated && (width > 1280 || height > 1280)) { if (isAnimated && (width > 1280 || height > 1280)) {
isAnimated = false; isAnimated = false;
} }
/*if (!isVideo && "video/x-matroska".equals(document.mime_type)) { if (SharedConfig.streamMkv && !isVideo && "video/x-matroska".equals(document.mime_type)) {
isVideo = true; isVideo = true;
}*/ }
return isVideo && !isAnimated; return isVideo && !isAnimated;
} }
return false; return false;
@ -3744,6 +3766,8 @@ public class MessageObject {
public TLRPC.Document getDocument() { public TLRPC.Document getDocument() {
if (messageOwner.media instanceof TLRPC.TL_messageMediaWebPage) { if (messageOwner.media instanceof TLRPC.TL_messageMediaWebPage) {
return messageOwner.media.webpage.document; return messageOwner.media.webpage.document;
} else if (messageOwner.media instanceof TLRPC.TL_messageMediaGame) {
return messageOwner.media.game.document;
} }
return messageOwner.media != null ? messageOwner.media.document : null; return messageOwner.media != null ? messageOwner.media.document : null;
} }
@ -3780,7 +3804,7 @@ public class MessageObject {
public static boolean isPhoto(TLRPC.Message message) { public static boolean isPhoto(TLRPC.Message message) {
if (message.media instanceof TLRPC.TL_messageMediaWebPage) { if (message.media instanceof TLRPC.TL_messageMediaWebPage) {
return message.media.webpage.photo instanceof TLRPC.TL_photo; return message.media.webpage.photo instanceof TLRPC.TL_photo && !(message.media.webpage.document instanceof TLRPC.TL_document);
} }
return message.media instanceof TLRPC.TL_messageMediaPhoto; return message.media instanceof TLRPC.TL_messageMediaPhoto;
} }
@ -4068,15 +4092,13 @@ public class MessageObject {
} }
public int getDuration() { public int getDuration() {
TLRPC.Document document; TLRPC.Document document = getDocument();
if (type == 0) {
document = messageOwner.media.webpage.document;
} else {
document = messageOwner.media.document;
}
if (document == null) { if (document == null) {
return 0; return 0;
} }
if (audioPlayerDuration > 0) {
return audioPlayerDuration;
}
for (int a = 0; a < document.attributes.size(); a++) { for (int a = 0; a < document.attributes.size(); a++) {
TLRPC.DocumentAttribute attribute = document.attributes.get(a); TLRPC.DocumentAttribute attribute = document.attributes.get(a);
if (attribute instanceof TLRPC.TL_documentAttributeAudio) { if (attribute instanceof TLRPC.TL_documentAttributeAudio) {

View File

@ -48,6 +48,8 @@ import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import android.support.v4.app.NotificationManagerCompat;
public class MessagesController implements NotificationCenter.NotificationCenterDelegate { public class MessagesController implements NotificationCenter.NotificationCenterDelegate {
private ConcurrentHashMap<Integer, TLRPC.Chat> chats = new ConcurrentHashMap<>(100, 1.0f, 2); private ConcurrentHashMap<Integer, TLRPC.Chat> chats = new ConcurrentHashMap<>(100, 1.0f, 2);
@ -63,9 +65,10 @@ public class MessagesController implements NotificationCenter.NotificationCenter
public ArrayList<TLRPC.TL_dialog> dialogs = new ArrayList<>(); public ArrayList<TLRPC.TL_dialog> dialogs = new ArrayList<>();
public ArrayList<TLRPC.TL_dialog> dialogsForward = new ArrayList<>(); public ArrayList<TLRPC.TL_dialog> dialogsForward = new ArrayList<>();
public ArrayList<TLRPC.TL_dialog> dialogsServerOnly = new ArrayList<>(); public ArrayList<TLRPC.TL_dialog> dialogsServerOnly = new ArrayList<>();
public ArrayList<TLRPC.TL_dialog> dialogsGroupsOnly = new ArrayList<>(); public ArrayList<TLRPC.TL_dialog> dialogsCanAddUsers = new ArrayList<>();
public ArrayList<TLRPC.TL_dialog> dialogsChannelsOnly = new ArrayList<>(); public ArrayList<TLRPC.TL_dialog> dialogsChannelsOnly = new ArrayList<>();
public ArrayList<TLRPC.TL_dialog> dialogsUsersOnly = new ArrayList<>(); public ArrayList<TLRPC.TL_dialog> dialogsUsersOnly = new ArrayList<>();
public ArrayList<TLRPC.TL_dialog> dialogsGroupsOnly = new ArrayList<>();
public int unreadUnmutedDialogs; public int unreadUnmutedDialogs;
public int nextDialogsCacheOffset; public int nextDialogsCacheOffset;
public ConcurrentHashMap<Long, Integer> dialogs_read_inbox_max = new ConcurrentHashMap<>(100, 1.0f, 2); public ConcurrentHashMap<Long, Integer> dialogs_read_inbox_max = new ConcurrentHashMap<>(100, 1.0f, 2);
@ -167,6 +170,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter
private TLRPC.TL_dialog proxyDialog; private TLRPC.TL_dialog proxyDialog;
private boolean isLeftProxyChannel; private boolean isLeftProxyChannel;
private long proxyDialogId; private long proxyDialogId;
private String proxyDialogAddress;
private boolean checkingTosUpdate; private boolean checkingTosUpdate;
private int nextTosCheckTime; private int nextTosCheckTime;
@ -414,6 +418,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter
canRevokePmInbox = mainPreferences.getBoolean("canRevokePmInbox", canRevokePmInbox); canRevokePmInbox = mainPreferences.getBoolean("canRevokePmInbox", canRevokePmInbox);
preloadFeaturedStickers = mainPreferences.getBoolean("preloadFeaturedStickers", false); preloadFeaturedStickers = mainPreferences.getBoolean("preloadFeaturedStickers", false);
proxyDialogId = mainPreferences.getLong("proxy_dialog", 0); proxyDialogId = mainPreferences.getLong("proxy_dialog", 0);
proxyDialogAddress = mainPreferences.getString("proxyDialogAddress", null);
nextTosCheckTime = notificationsPreferences.getInt("nextTosCheckTime", 0); nextTosCheckTime = notificationsPreferences.getInt("nextTosCheckTime", 0);
venueSearchBot = mainPreferences.getString("venueSearchBot", "foursquare"); venueSearchBot = mainPreferences.getString("venueSearchBot", "foursquare");
gifSearchBot = mainPreferences.getString("gifSearchBot", "gif"); gifSearchBot = mainPreferences.getString("gifSearchBot", "gif");
@ -426,6 +431,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter
public void updateConfig(final TLRPC.TL_config config) { public void updateConfig(final TLRPC.TL_config config) {
AndroidUtilities.runOnUIThread(() -> { AndroidUtilities.runOnUIThread(() -> {
DownloadController.getInstance(currentAccount).loadAutoDownloadConfig(false);
LocaleController.getInstance().loadRemoteLanguages(currentAccount); LocaleController.getInstance().loadRemoteLanguages(currentAccount);
maxMegagroupCount = config.megagroup_size_max; maxMegagroupCount = config.megagroup_size_max;
maxGroupCount = config.chat_size_max; maxGroupCount = config.chat_size_max;
@ -790,7 +796,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter
editor = emojiPreferences.edit(); editor = emojiPreferences.edit();
editor.putLong("lastGifLoadTime", 0).putLong("lastStickersLoadTime", 0).putLong("lastStickersLoadTimeMask", 0).putLong("lastStickersLoadTimeFavs", 0).commit(); editor.putLong("lastGifLoadTime", 0).putLong("lastStickersLoadTime", 0).putLong("lastStickersLoadTimeMask", 0).putLong("lastStickersLoadTimeFavs", 0).commit();
editor = mainPreferences.edit(); editor = mainPreferences.edit();
editor.remove("gifhint").remove("dcDomainName").remove("webFileDatacenterId").commit(); editor.remove("gifhint").remove("soundHint").remove("dcDomainName").remove("webFileDatacenterId").commit();
reloadingWebpages.clear(); reloadingWebpages.clear();
reloadingWebpagesPending.clear(); reloadingWebpagesPending.clear();
@ -808,8 +814,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter
pollsToCheckSize = 0; pollsToCheckSize = 0;
dialogsServerOnly.clear(); dialogsServerOnly.clear();
dialogsForward.clear(); dialogsForward.clear();
dialogsGroupsOnly.clear(); dialogsCanAddUsers.clear();
dialogsChannelsOnly.clear(); dialogsChannelsOnly.clear();
dialogsGroupsOnly.clear();
dialogsUsersOnly.clear(); dialogsUsersOnly.clear();
dialogMessagesByIds.clear(); dialogMessagesByIds.clear();
dialogMessagesByRandomIds.clear(); dialogMessagesByRandomIds.clear();
@ -2507,8 +2514,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter
needShortPollOnlines.delete(-(int) did); needShortPollOnlines.delete(-(int) did);
}); });
} }
dialogsGroupsOnly.remove(dialog); dialogsCanAddUsers.remove(dialog);
dialogsChannelsOnly.remove(dialog); dialogsChannelsOnly.remove(dialog);
dialogsGroupsOnly.remove(dialog);
dialogsUsersOnly.remove(dialog); dialogsUsersOnly.remove(dialog);
dialogsForward.remove(dialog); dialogsForward.remove(dialog);
dialogs_dict.remove(did); dialogs_dict.remove(did);
@ -3003,10 +3011,18 @@ public class MessagesController implements NotificationCenter.NotificationCenter
if (!reset && nextProxyInfoCheckTime > ConnectionsManager.getInstance(currentAccount).getCurrentTime() || checkingProxyInfo) { if (!reset && nextProxyInfoCheckTime > ConnectionsManager.getInstance(currentAccount).getCurrentTime() || checkingProxyInfo) {
return; return;
} }
if (checkingProxyInfoRequestId != 0) {
ConnectionsManager.getInstance(currentAccount).cancelRequest(checkingProxyInfoRequestId, true);
checkingProxyInfoRequestId = 0;
}
SharedPreferences preferences = getGlobalMainSettings(); SharedPreferences preferences = getGlobalMainSettings();
boolean enabled = preferences.getBoolean("proxy_enabled", false); boolean enabled = preferences.getBoolean("proxy_enabled", false);
String proxyAddress = preferences.getString("proxy_ip", ""); String proxyAddress = preferences.getString("proxy_ip", "");
String proxySecret = preferences.getString("proxy_secret", ""); String proxySecret = preferences.getString("proxy_secret", "");
int removeCurrent = 0;
if (proxyDialogId != 0 && proxyDialogAddress != null && !proxyDialogAddress.equals(proxyAddress + proxySecret)) {
removeCurrent = 1;
}
if (enabled && !TextUtils.isEmpty(proxyAddress) && !TextUtils.isEmpty(proxySecret)) { if (enabled && !TextUtils.isEmpty(proxyAddress) && !TextUtils.isEmpty(proxySecret)) {
checkingProxyInfo = true; checkingProxyInfo = true;
TLRPC.TL_help_getProxyData req = new TLRPC.TL_help_getProxyData(); TLRPC.TL_help_getProxyData req = new TLRPC.TL_help_getProxyData();
@ -3049,7 +3065,8 @@ public class MessagesController implements NotificationCenter.NotificationCenter
} }
} }
proxyDialogId = did; proxyDialogId = did;
getGlobalMainSettings().edit().putLong("proxy_dialog", proxyDialogId).commit(); proxyDialogAddress = proxyAddress + proxySecret;
getGlobalMainSettings().edit().putLong("proxy_dialog", proxyDialogId).putString("proxyDialogAddress", proxyDialogAddress).commit();
nextProxyInfoCheckTime = res.expires; nextProxyInfoCheckTime = res.expires;
if (!noDialog) { if (!noDialog) {
AndroidUtilities.runOnUIThread(() -> { AndroidUtilities.runOnUIThread(() -> {
@ -3180,7 +3197,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter
} }
if (noDialog) { if (noDialog) {
proxyDialogId = 0; proxyDialogId = 0;
getGlobalMainSettings().edit().putLong("proxy_dialog", proxyDialogId).commit(); getGlobalMainSettings().edit().putLong("proxy_dialog", proxyDialogId).remove("proxyDialogAddress").commit();
checkingProxyInfoRequestId = 0; checkingProxyInfoRequestId = 0;
checkingProxyInfo = false; checkingProxyInfo = false;
AndroidUtilities.runOnUIThread(() -> { AndroidUtilities.runOnUIThread(() -> {
@ -3200,13 +3217,19 @@ public class MessagesController implements NotificationCenter.NotificationCenter
} }
}); });
} else { } else {
removeCurrent = 2;
}
if (removeCurrent != 0) {
proxyDialogId = 0; proxyDialogId = 0;
getGlobalMainSettings().edit().putLong("proxy_dialog", proxyDialogId).commit(); proxyDialogAddress = null;
getGlobalMainSettings().edit().putLong("proxy_dialog", proxyDialogId).remove("proxyDialogAddress").commit();
nextProxyInfoCheckTime = ConnectionsManager.getInstance(currentAccount).getCurrentTime() + 60 * 60; nextProxyInfoCheckTime = ConnectionsManager.getInstance(currentAccount).getCurrentTime() + 60 * 60;
checkingProxyInfo = false; if (removeCurrent == 2) {
if (checkingProxyInfoRequestId != 0) { checkingProxyInfo = false;
ConnectionsManager.getInstance(currentAccount).cancelRequest(checkingProxyInfoRequestId, true); if (checkingProxyInfoRequestId != 0) {
checkingProxyInfoRequestId = 0; ConnectionsManager.getInstance(currentAccount).cancelRequest(checkingProxyInfoRequestId, true);
checkingProxyInfoRequestId = 0;
}
} }
AndroidUtilities.runOnUIThread(() -> { AndroidUtilities.runOnUIThread(() -> {
if (proxyDialog != null) { if (proxyDialog != null) {
@ -6462,18 +6485,18 @@ public class MessagesController implements NotificationCenter.NotificationCenter
getChannelDifference(channelId, 0, 0, null); getChannelDifference(channelId, 0, 0, null);
} }
public static boolean isSupportId(int id) { public static boolean isSupportUser(TLRPC.User user) {
return id / 1000 == 777 || id == 333000 || return user != null && (user.support || user.id / 1000 == 777 || user.id == 333000 ||
id == 4240000 || id == 4240000 || id == 4244000 || user.id == 4240000 || user.id == 4240000 || user.id == 4244000 ||
id == 4245000 || id == 4246000 || id == 410000 || user.id == 4245000 || user.id == 4246000 || user.id == 410000 ||
id == 420000 || id == 431000 || id == 431415000 || user.id == 420000 || user.id == 431000 || user.id == 431415000 ||
id == 434000 || id == 4243000 || id == 439000 || user.id == 434000 || user.id == 4243000 || user.id == 439000 ||
id == 449000 || id == 450000 || id == 452000 || user.id == 449000 || user.id == 450000 || user.id == 452000 ||
id == 454000 || id == 4254000 || id == 455000 || user.id == 454000 || user.id == 4254000 || user.id == 455000 ||
id == 460000 || id == 470000 || id == 479000 || user.id == 460000 || user.id == 470000 || user.id == 479000 ||
id == 796000 || id == 482000 || id == 490000 || user.id == 796000 || user.id == 482000 || user.id == 490000 ||
id == 496000 || id == 497000 || id == 498000 || user.id == 496000 || user.id == 497000 || user.id == 498000 ||
id == 4298000; user.id == 4298000);
} }
protected void getChannelDifference(final int channelId, final int newDialogType, final long taskId, TLRPC.InputChannel inputChannel) { protected void getChannelDifference(final int channelId, final int newDialogType, final long taskId, TLRPC.InputChannel inputChannel) {
@ -9298,8 +9321,13 @@ public class MessagesController implements NotificationCenter.NotificationCenter
} }
continue; continue;
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !NotificationManagerCompat.from(ApplicationLoader.applicationContext).areNotificationsEnabled()) {
if (BuildVars.LOGS_ENABLED)
FileLog.d("Ignoring incoming call because notifications are disabled in system");
continue;
}
TelephonyManager tm = (TelephonyManager) ApplicationLoader.applicationContext.getSystemService(Context.TELEPHONY_SERVICE); TelephonyManager tm = (TelephonyManager) ApplicationLoader.applicationContext.getSystemService(Context.TELEPHONY_SERVICE);
if (svc != null || VoIPService.callIShouldHavePutIntoIntent!=null || tm.getCallState() != TelephonyManager.CALL_STATE_IDLE) { if (svc != null || VoIPService.callIShouldHavePutIntoIntent != null || tm.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
if (BuildVars.LOGS_ENABLED) { if (BuildVars.LOGS_ENABLED) {
FileLog.d("Auto-declining call " + call.id + " because there's already active one"); FileLog.d("Auto-declining call " + call.id + " because there's already active one");
} }
@ -9325,9 +9353,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter
intent.putExtra("user_id", call.participant_id == UserConfig.getInstance(currentAccount).getClientUserId() ? call.admin_id : call.participant_id); intent.putExtra("user_id", call.participant_id == UserConfig.getInstance(currentAccount).getClientUserId() ? call.admin_id : call.participant_id);
intent.putExtra("account", currentAccount); intent.putExtra("account", currentAccount);
try { try {
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ApplicationLoader.applicationContext.startForegroundService(intent); ApplicationLoader.applicationContext.startForegroundService(intent);
}else{ } else {
ApplicationLoader.applicationContext.startService(intent); ApplicationLoader.applicationContext.startService(intent);
} }
} catch (Throwable e) { } catch (Throwable e) {
@ -9738,8 +9766,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter
if (dialog != null) { if (dialog != null) {
dialogs.remove(dialog); dialogs.remove(dialog);
dialogsServerOnly.remove(dialog); dialogsServerOnly.remove(dialog);
dialogsGroupsOnly.remove(dialog); dialogsCanAddUsers.remove(dialog);
dialogsChannelsOnly.remove(dialog); dialogsChannelsOnly.remove(dialog);
dialogsGroupsOnly.remove(dialog);
dialogsUsersOnly.remove(dialog); dialogsUsersOnly.remove(dialog);
dialogsForward.remove(dialog); dialogsForward.remove(dialog);
dialogs_dict.remove(dialog.id); dialogs_dict.remove(dialog.id);
@ -9858,8 +9887,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter
public void sortDialogs(SparseArray<TLRPC.Chat> chatsDict) { public void sortDialogs(SparseArray<TLRPC.Chat> chatsDict) {
dialogsServerOnly.clear(); dialogsServerOnly.clear();
dialogsGroupsOnly.clear(); dialogsCanAddUsers.clear();
dialogsChannelsOnly.clear(); dialogsChannelsOnly.clear();
dialogsGroupsOnly.clear();
dialogsUsersOnly.clear(); dialogsUsersOnly.clear();
dialogsForward.clear(); dialogsForward.clear();
unreadUnmutedDialogs = 0; unreadUnmutedDialogs = 0;
@ -9888,10 +9918,10 @@ public class MessagesController implements NotificationCenter.NotificationCenter
if (DialogObject.isChannel(d)) { if (DialogObject.isChannel(d)) {
TLRPC.Chat chat = getChat(-lower_id); TLRPC.Chat chat = getChat(-lower_id);
if (chat != null && (chat.megagroup && (chat.admin_rights != null && (chat.admin_rights.post_messages || chat.admin_rights.add_admins)) || chat.creator)) { if (chat != null && (chat.megagroup && (chat.admin_rights != null && (chat.admin_rights.post_messages || chat.admin_rights.add_admins)) || chat.creator)) {
dialogsGroupsOnly.add(d); dialogsCanAddUsers.add(d);
} }
if (chat != null && chat.megagroup) { if (chat != null && chat.megagroup) {
dialogsGroupsOnly.add(d);
} else { } else {
dialogsChannelsOnly.add(d); dialogsChannelsOnly.add(d);
} }
@ -9904,6 +9934,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter
continue; continue;
} }
} }
dialogsCanAddUsers.add(d);
dialogsGroupsOnly.add(d); dialogsGroupsOnly.add(d);
} else if (lower_id > 0) { } else if (lower_id > 0) {
dialogsUsersOnly.add(d); dialogsUsersOnly.add(d);

View File

@ -5500,47 +5500,41 @@ public class MessagesStorage {
data.reuse(); data.reuse();
if (downloadMask != 0 && (message.to_id.channel_id == 0 || message.post) && message.date >= ConnectionsManager.getInstance(currentAccount).getCurrentTime() - 60 * 60 && DownloadController.getInstance(currentAccount).canDownloadMedia(message)) { if (downloadMask != 0 && (message.to_id.channel_id == 0 || message.post) && message.date >= ConnectionsManager.getInstance(currentAccount).getCurrentTime() - 60 * 60 && DownloadController.getInstance(currentAccount).canDownloadMedia(message) == 1) {
if (message.media instanceof TLRPC.TL_messageMediaPhoto || message.media instanceof TLRPC.TL_messageMediaDocument) { if (message.media instanceof TLRPC.TL_messageMediaPhoto || message.media instanceof TLRPC.TL_messageMediaDocument) {
int type = 0; int type = 0;
long id = 0; long id = 0;
TLRPC.MessageMedia object = null; TLRPC.MessageMedia object = null;
if (MessageObject.isVoiceMessage(message)) { if (MessageObject.isVoiceMessage(message)) {
id = message.media.document.id; id = message.media.document.id;
type = DownloadController.AUTODOWNLOAD_MASK_AUDIO; type = DownloadController.AUTODOWNLOAD_TYPE_AUDIO;
object = new TLRPC.TL_messageMediaDocument();
object.document = message.media.document;
object.flags |= 1;
} else if (MessageObject.isRoundVideoMessage(message)) {
id = message.media.document.id;
type = DownloadController.AUTODOWNLOAD_MASK_VIDEOMESSAGE;
object = new TLRPC.TL_messageMediaDocument(); object = new TLRPC.TL_messageMediaDocument();
object.document = message.media.document; object.document = message.media.document;
object.flags |= 1; object.flags |= 1;
} else if (MessageObject.isStickerMessage(message)) { } else if (MessageObject.isStickerMessage(message)) {
id = message.media.document.id; id = message.media.document.id;
type = DownloadController.AUTODOWNLOAD_MASK_PHOTO; type = DownloadController.AUTODOWNLOAD_TYPE_PHOTO;
object = new TLRPC.TL_messageMediaDocument(); object = new TLRPC.TL_messageMediaDocument();
object.document = message.media.document; object.document = message.media.document;
object.flags |= 1; object.flags |= 1;
}else if (message.media instanceof TLRPC.TL_messageMediaPhoto) { } else if (message.media instanceof TLRPC.TL_messageMediaPhoto) {
TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(message.media.photo.sizes, AndroidUtilities.getPhotoSize()); TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(message.media.photo.sizes, AndroidUtilities.getPhotoSize());
if (photoSize != null) { if (photoSize != null) {
id = message.media.photo.id; id = message.media.photo.id;
type = DownloadController.AUTODOWNLOAD_MASK_PHOTO; type = DownloadController.AUTODOWNLOAD_TYPE_PHOTO;
object = new TLRPC.TL_messageMediaPhoto(); object = new TLRPC.TL_messageMediaPhoto();
object.photo = message.media.photo; object.photo = message.media.photo;
object.flags |= 1; object.flags |= 1;
} }
} else if (MessageObject.isVideoMessage(message)) { } else if (MessageObject.isVideoMessage(message) || MessageObject.isRoundVideoMessage(message)) {
id = message.media.document.id; id = message.media.document.id;
type = DownloadController.AUTODOWNLOAD_MASK_VIDEO; type = DownloadController.AUTODOWNLOAD_TYPE_VIDEO;
object = new TLRPC.TL_messageMediaDocument(); object = new TLRPC.TL_messageMediaDocument();
object.document = message.media.document; object.document = message.media.document;
object.flags |= 1; object.flags |= 1;
} else if (message.media instanceof TLRPC.TL_messageMediaDocument && !MessageObject.isMusicMessage(message) && !MessageObject.isGifDocument(message.media.document)) { } else if (message.media instanceof TLRPC.TL_messageMediaDocument && !MessageObject.isMusicMessage(message) && !MessageObject.isGifDocument(message.media.document)) {
id = message.media.document.id; id = message.media.document.id;
type = DownloadController.AUTODOWNLOAD_MASK_DOCUMENT; type = DownloadController.AUTODOWNLOAD_TYPE_DOCUMENT;
object = new TLRPC.TL_messageMediaDocument(); object = new TLRPC.TL_messageMediaDocument();
object.document = message.media.document; object.document = message.media.document;
object.flags |= 1; object.flags |= 1;

View File

@ -24,7 +24,7 @@ import java.util.zip.ZipFile;
public class NativeLoader { public class NativeLoader {
private final static int LIB_VERSION = 29; private final static int LIB_VERSION = 30;
private final static String LIB_NAME = "tmessages." + LIB_VERSION; private final static String LIB_NAME = "tmessages." + LIB_VERSION;
private final static String LIB_SO_NAME = "lib" + LIB_NAME + ".so"; private final static String LIB_SO_NAME = "lib" + LIB_NAME + ".so";
private final static String LOCALE_LIB_SO_NAME = "lib" + LIB_NAME + "loc.so"; private final static String LOCALE_LIB_SO_NAME = "lib" + LIB_NAME + "loc.so";

View File

@ -87,6 +87,7 @@ public class NotificationCenter {
public static final int updateMentionsCount = totalEvents++; public static final int updateMentionsCount = totalEvents++;
public static final int didUpdatePollResults = totalEvents++; public static final int didUpdatePollResults = totalEvents++;
public static final int chatOnlineCountDidLoad = totalEvents++; public static final int chatOnlineCountDidLoad = totalEvents++;
public static final int videoLoadingStateChanged = totalEvents++;
public static final int httpFileDidLoad = totalEvents++; public static final int httpFileDidLoad = totalEvents++;
public static final int httpFileDidFailedLoad = totalEvents++; public static final int httpFileDidFailedLoad = totalEvents++;
@ -110,6 +111,7 @@ public class NotificationCenter {
public static final int messagePlayingPlayStateChanged = totalEvents++; public static final int messagePlayingPlayStateChanged = totalEvents++;
public static final int messagePlayingDidStart = totalEvents++; public static final int messagePlayingDidStart = totalEvents++;
public static final int messagePlayingDidSeek = totalEvents++; public static final int messagePlayingDidSeek = totalEvents++;
public static final int messagePlayingGoingToStop = totalEvents++;
public static final int recordProgressChanged = totalEvents++; public static final int recordProgressChanged = totalEvents++;
public static final int recordStarted = totalEvents++; public static final int recordStarted = totalEvents++;
public static final int recordStartError = totalEvents++; public static final int recordStartError = totalEvents++;

View File

@ -699,7 +699,11 @@ public class NotificationsController {
if (popupFinal == 3 || popupFinal == 1 && ApplicationLoader.isScreenOn || popupFinal == 2 && !ApplicationLoader.isScreenOn) { if (popupFinal == 3 || popupFinal == 1 && ApplicationLoader.isScreenOn || popupFinal == 2 && !ApplicationLoader.isScreenOn) {
Intent popupIntent = new Intent(ApplicationLoader.applicationContext, PopupNotificationActivity.class); Intent popupIntent = new Intent(ApplicationLoader.applicationContext, PopupNotificationActivity.class);
popupIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_FROM_BACKGROUND); popupIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_FROM_BACKGROUND);
ApplicationLoader.applicationContext.startActivity(popupIntent); try {
ApplicationLoader.applicationContext.startActivity(popupIntent);
} catch (Throwable ignore) {
}
} }
} }
}); });

View File

@ -4890,7 +4890,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
bitmap = ImageLoader.loadBitmap(finalPath, null, side, side, true); bitmap = ImageLoader.loadBitmap(finalPath, null, side, side, true);
} }
if (bitmap != null) { if (bitmap != null) {
TLRPC.PhotoSize thumb = ImageLoader.scaleAndSaveImage(bitmap, side, side, 55, false); TLRPC.PhotoSize thumb = ImageLoader.scaleAndSaveImage(bitmap, side, side, side > 90 ? 70 : 55, false);
if (thumb != null) { if (thumb != null) {
document.thumbs.add(thumb); document.thumbs.add(thumb);
document.flags |= 1; document.flags |= 1;
@ -5174,7 +5174,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
thumb = ThumbnailUtils.createVideoThumbnail(path, MediaStore.Video.Thumbnails.MINI_KIND); thumb = ThumbnailUtils.createVideoThumbnail(path, MediaStore.Video.Thumbnails.MINI_KIND);
} }
int side = isEncrypted ? 90 : 320; int side = isEncrypted ? 90 : 320;
document.thumbs.set(0, ImageLoader.scaleAndSaveImage(photoSize, thumb, side, side, 55, false)); document.thumbs.set(0, ImageLoader.scaleAndSaveImage(photoSize, thumb, side, side, side > 90 ? 70 : 55, false));
} }
} }
} }
@ -5335,7 +5335,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
} }
if (thumbFile != null) { if (thumbFile != null) {
try { try {
int side = isEncrypted ? 90 : 320; int side = isEncrypted || info.ttl != 0 ? 90 : 320;
Bitmap bitmap; Bitmap bitmap;
if (thumbFile.getAbsolutePath().endsWith("mp4")) { if (thumbFile.getAbsolutePath().endsWith("mp4")) {
bitmap = ThumbnailUtils.createVideoThumbnail(thumbFile.getAbsolutePath(), MediaStore.Video.Thumbnails.MINI_KIND); bitmap = ThumbnailUtils.createVideoThumbnail(thumbFile.getAbsolutePath(), MediaStore.Video.Thumbnails.MINI_KIND);
@ -5343,7 +5343,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
bitmap = ImageLoader.loadBitmap(thumbFile.getAbsolutePath(), null, side, side, true); bitmap = ImageLoader.loadBitmap(thumbFile.getAbsolutePath(), null, side, side, true);
} }
if (bitmap != null) { if (bitmap != null) {
TLRPC.PhotoSize thumb = ImageLoader.scaleAndSaveImage(bitmap, side, side, 55, isEncrypted); TLRPC.PhotoSize thumb = ImageLoader.scaleAndSaveImage(bitmap, side, side, side > 90 ? 70 : 55, isEncrypted);
if (thumb != null) { if (thumb != null) {
document.thumbs.add(thumb); document.thumbs.add(thumb);
document.flags |= 1; document.flags |= 1;
@ -5492,8 +5492,8 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
if (thumb == null) { if (thumb == null) {
thumb = ThumbnailUtils.createVideoThumbnail(info.path, MediaStore.Video.Thumbnails.MINI_KIND); thumb = ThumbnailUtils.createVideoThumbnail(info.path, MediaStore.Video.Thumbnails.MINI_KIND);
} }
int side = isEncrypted ? 90 : 320; int side = isEncrypted || info.ttl != 0 ? 90 : 320;
TLRPC.PhotoSize size = ImageLoader.scaleAndSaveImage(thumb, side, side, 55, isEncrypted); TLRPC.PhotoSize size = ImageLoader.scaleAndSaveImage(thumb, side, side, side > 90 ? 70 : 55, isEncrypted);
if (thumb != null && size != null) { if (thumb != null && size != null) {
thumb = null; thumb = null;
} }
@ -5707,7 +5707,11 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
if (AndroidUtilities.isTablet()) { if (AndroidUtilities.isTablet()) {
maxPhotoWidth = photoWidth = (int) (AndroidUtilities.getMinTabletSide() * 0.7f); maxPhotoWidth = photoWidth = (int) (AndroidUtilities.getMinTabletSide() * 0.7f);
} else { } else {
maxPhotoWidth = photoWidth = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.7f); if (currentPhotoObject.w >= currentPhotoObject.h) {
maxPhotoWidth = photoWidth = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(64);
} else {
maxPhotoWidth = photoWidth = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.7f);
}
} }
photoHeight = photoWidth + AndroidUtilities.dp(100); photoHeight = photoWidth + AndroidUtilities.dp(100);
@ -5988,8 +5992,36 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
for (int a = 0; a < sizes.length; a++) { for (int a = 0; a < sizes.length; a++) {
sampleSizes += sizes[a]; sampleSizes += sizes[a];
} }
videoDuration = (float) mediaHeaderBox.getDuration() / (float) mediaHeaderBox.getTimescale(); if (videoDuration == 0) {
trackBitrate = (int) (sampleSizes * 8 / videoDuration); videoDuration = (float) mediaHeaderBox.getDuration() / (float) mediaHeaderBox.getTimescale();
if (videoDuration == 0) {
MediaPlayer player = null;
try {
player = new MediaPlayer();
player.setDataSource(videoPath);
player.prepare();
videoDuration = player.getDuration() / 1000.0f;
if (videoDuration < 0) {
videoDuration = 0;
}
} catch (Throwable ignore) {
} finally {
try {
if (player != null) {
player.release();
}
} catch (Throwable ignore) {
}
}
}
}
if (videoDuration != 0) {
trackBitrate = (int) (sampleSizes * 8 / videoDuration);
} else {
trackBitrate = 400000;
}
} catch (Exception e) { } catch (Exception e) {
FileLog.e(e); FileLog.e(e);
} }
@ -6149,6 +6181,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
videoEditedInfo.estimatedSize = (int) (audioFramesSize + videoFramesSize); videoEditedInfo.estimatedSize = (int) (audioFramesSize + videoFramesSize);
videoEditedInfo.estimatedSize += videoEditedInfo.estimatedSize / (32 * 1024) * 16; videoEditedInfo.estimatedSize += videoEditedInfo.estimatedSize / (32 * 1024) * 16;
} }
if (videoEditedInfo.estimatedSize == 0) {
videoEditedInfo.estimatedSize = 1;
}
return videoEditedInfo; return videoEditedInfo;
} }
@ -6200,8 +6235,8 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
if (thumb == null) { if (thumb == null) {
thumb = ThumbnailUtils.createVideoThumbnail(videoPath, MediaStore.Video.Thumbnails.MINI_KIND); thumb = ThumbnailUtils.createVideoThumbnail(videoPath, MediaStore.Video.Thumbnails.MINI_KIND);
} }
int side = isEncrypted ? 90 : 320; int side = isEncrypted || ttl != 0 ? 90 : 320;
TLRPC.PhotoSize size = ImageLoader.scaleAndSaveImage(thumb, side, side, 55, isEncrypted); TLRPC.PhotoSize size = ImageLoader.scaleAndSaveImage(thumb, side, side, side > 90 ? 70 : 55, isEncrypted);
if (thumb != null && size != null) { if (thumb != null && size != null) {
if (isRound) { if (isRound) {
if (isEncrypted) { if (isEncrypted) {

View File

@ -60,14 +60,17 @@ public class SharedConfig {
public static boolean saveToGallery; public static boolean saveToGallery;
public static int mapPreviewType = 2; public static int mapPreviewType = 2;
public static boolean autoplayGifs = true; public static boolean autoplayGifs = true;
public static boolean autoplayVideo = true;
public static boolean raiseToSpeak = true; public static boolean raiseToSpeak = true;
public static boolean customTabs = true; public static boolean customTabs = true;
public static boolean directShare = true; public static boolean directShare = true;
public static boolean inappCamera = true; public static boolean inappCamera = true;
public static boolean roundCamera16to9 = true; public static boolean roundCamera16to9 = true;
public static boolean groupPhotosEnabled = true; public static boolean groupPhotosEnabled = true;
public static boolean noSoundHintShowed = false;
public static boolean streamMedia = true; public static boolean streamMedia = true;
public static boolean streamAllVideo = false; public static boolean streamAllVideo = false;
public static boolean streamMkv = false;
public static boolean saveStreamMedia = true; public static boolean saveStreamMedia = true;
public static boolean sortContactsByName; public static boolean sortContactsByName;
public static boolean shuffleMusic; public static boolean shuffleMusic;
@ -204,6 +207,7 @@ public class SharedConfig {
preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
saveToGallery = preferences.getBoolean("save_gallery", false); saveToGallery = preferences.getBoolean("save_gallery", false);
autoplayGifs = preferences.getBoolean("autoplay_gif", true); autoplayGifs = preferences.getBoolean("autoplay_gif", true);
autoplayVideo = preferences.getBoolean("autoplay_video", true);
mapPreviewType = preferences.getInt("mapPreviewType", 2); mapPreviewType = preferences.getInt("mapPreviewType", 2);
raiseToSpeak = preferences.getBoolean("raise_to_speak", true); raiseToSpeak = preferences.getBoolean("raise_to_speak", true);
customTabs = preferences.getBoolean("custom_tabs", true); customTabs = preferences.getBoolean("custom_tabs", true);
@ -221,8 +225,10 @@ public class SharedConfig {
streamMedia = preferences.getBoolean("streamMedia", true); streamMedia = preferences.getBoolean("streamMedia", true);
saveStreamMedia = preferences.getBoolean("saveStreamMedia", true); saveStreamMedia = preferences.getBoolean("saveStreamMedia", true);
streamAllVideo = preferences.getBoolean("streamAllVideo", BuildVars.DEBUG_VERSION); streamAllVideo = preferences.getBoolean("streamAllVideo", BuildVars.DEBUG_VERSION);
streamMkv = preferences.getBoolean("streamMkv", false);
suggestStickers = preferences.getInt("suggestStickers", 0); suggestStickers = preferences.getInt("suggestStickers", 0);
sortContactsByName = preferences.getBoolean("sortContactsByName", false); sortContactsByName = preferences.getBoolean("sortContactsByName", false);
noSoundHintShowed = preferences.getBoolean("noSoundHintShowed", false);
configLoaded = true; configLoaded = true;
} }
@ -388,6 +394,14 @@ public class SharedConfig {
editor.commit(); editor.commit();
} }
public static void toggleAutoplayVideo() {
autoplayVideo = !autoplayVideo;
SharedPreferences preferences = MessagesController.getGlobalMainSettings();
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("autoplay_video", autoplayVideo);
editor.commit();
}
public static boolean isSecretMapPreviewSet() { public static boolean isSecretMapPreviewSet() {
SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences preferences = MessagesController.getGlobalMainSettings();
return preferences.contains("mapPreviewType"); return preferences.contains("mapPreviewType");
@ -401,6 +415,17 @@ public class SharedConfig {
editor.commit(); editor.commit();
} }
public static void setNoSoundHintShowed(boolean value) {
if (noSoundHintShowed == value) {
return;
}
noSoundHintShowed = value;
SharedPreferences preferences = MessagesController.getGlobalMainSettings();
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("noSoundHintShowed", noSoundHintShowed);
editor.commit();
}
public static void toogleRaiseToSpeak() { public static void toogleRaiseToSpeak() {
raiseToSpeak = !raiseToSpeak; raiseToSpeak = !raiseToSpeak;
SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences preferences = MessagesController.getGlobalMainSettings();
@ -449,6 +474,14 @@ public class SharedConfig {
editor.commit(); editor.commit();
} }
public static void toggleStreamMkv() {
streamMkv = !streamMkv;
SharedPreferences preferences = MessagesController.getGlobalMainSettings();
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("streamMkv", streamMkv);
editor.commit();
}
public static void toggleSaveStreamMedia() { public static void toggleSaveStreamMedia() {
saveStreamMedia = !saveStreamMedia; saveStreamMedia = !saveStreamMedia;
SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences preferences = MessagesController.getGlobalMainSettings();

View File

@ -66,6 +66,7 @@ public class UserConfig {
public int pendingAppUpdateBuildVersion; public int pendingAppUpdateBuildVersion;
public long pendingAppUpdateInstallTime; public long pendingAppUpdateInstallTime;
public long lastUpdateCheckTime; public long lastUpdateCheckTime;
public long autoDownloadConfigLoadTime;
public volatile byte[] savedPasswordHash; public volatile byte[] savedPasswordHash;
public volatile byte[] savedSaltedPassword; public volatile byte[] savedSaltedPassword;
@ -145,6 +146,7 @@ public class UserConfig {
editor.putBoolean("hasSecureData", hasSecureData); editor.putBoolean("hasSecureData", hasSecureData);
editor.putBoolean("notificationsSettingsLoaded3", notificationsSettingsLoaded); editor.putBoolean("notificationsSettingsLoaded3", notificationsSettingsLoaded);
editor.putBoolean("notificationsSignUpSettingsLoaded", notificationsSignUpSettingsLoaded); editor.putBoolean("notificationsSignUpSettingsLoaded", notificationsSignUpSettingsLoaded);
editor.putLong("autoDownloadConfigLoadTime", autoDownloadConfigLoadTime);
editor.putInt("3migrateOffsetId", migrateOffsetId); editor.putInt("3migrateOffsetId", migrateOffsetId);
if (migrateOffsetId != -1) { if (migrateOffsetId != -1) {
@ -292,6 +294,7 @@ public class UserConfig {
hasSecureData = preferences.getBoolean("hasSecureData", false); hasSecureData = preferences.getBoolean("hasSecureData", false);
notificationsSettingsLoaded = preferences.getBoolean("notificationsSettingsLoaded3", false); notificationsSettingsLoaded = preferences.getBoolean("notificationsSettingsLoaded3", false);
notificationsSignUpSettingsLoaded = preferences.getBoolean("notificationsSignUpSettingsLoaded", false); notificationsSignUpSettingsLoaded = preferences.getBoolean("notificationsSignUpSettingsLoaded", false);
autoDownloadConfigLoadTime = preferences.getLong("autoDownloadConfigLoadTime", 0);
try { try {
String terms = preferences.getString("terms", null); String terms = preferences.getString("terms", null);

View File

@ -31,7 +31,7 @@ public class Utilities {
public static volatile DispatchQueue stageQueue = new DispatchQueue("stageQueue"); public static volatile DispatchQueue stageQueue = new DispatchQueue("stageQueue");
public static volatile DispatchQueue globalQueue = new DispatchQueue("globalQueue"); public static volatile DispatchQueue globalQueue = new DispatchQueue("globalQueue");
public static volatile DispatchQueue searchQueue = new DispatchQueue("searchQueue"); public static volatile DispatchQueue searchQueue = new DispatchQueue("searchQueue");
public static volatile DispatchQueue phoneBookQueue = new DispatchQueue("photoBookQueue"); public static volatile DispatchQueue phoneBookQueue = new DispatchQueue("phoneBookQueue");
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray(); final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
@ -358,6 +358,10 @@ public class Utilities {
+ (((long) bytes[3] & 0xFF) << 24) + (((long) bytes[2] & 0xFF) << 16) + (((long) bytes[1] & 0xFF) << 8) + ((long) bytes[0] & 0xFF); + (((long) bytes[3] & 0xFF) << 24) + (((long) bytes[2] & 0xFF) << 16) + (((long) bytes[1] & 0xFF) << 8) + ((long) bytes[0] & 0xFF);
} }
public static int bytesToInt(byte[] bytes) {
return (((int) bytes[3] & 0xFF) << 24) + (((int) bytes[2] & 0xFF) << 16) + (((int) bytes[1] & 0xFF) << 8) + ((int) bytes[0] & 0xFF);
}
public static String MD5(String md5) { public static String MD5(String md5) {
if (md5 == null) { if (md5 == null) {
return null; return null;

View File

@ -124,7 +124,11 @@ public class LinearSmoothScrollerMiddle extends RecyclerView.SmoothScroller {
int boxSize = end - start; int boxSize = end - start;
int viewSize = bottom - top; int viewSize = bottom - top;
start = (boxSize - viewSize) / 2; if (viewSize > boxSize) {
start = 0;
} else {
start = (boxSize - viewSize) / 2;
}
end = start + viewSize; end = start + viewSize;
final int dtStart = start - top; final int dtStart = start - top;
if (dtStart > 0) { if (dtStart > 0) {

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