1
0
mirror of https://github.com/MGislv/NekoX.git synced 2024-06-28 09:34:05 +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 {
compileOnly 'org.checkerframework:checker-qual:2.5.2'
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-config:16.1.3'
implementation 'com.google.android.gms:play-services-maps:16.0.0'
@ -86,6 +86,16 @@ android {
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 {
debuggable false
jniDebuggable false
@ -103,20 +113,19 @@ android {
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
foss {
debuggable false
jniDebuggable false
signingConfig signingConfigs.release
}
}
defaultConfig.versionCode = 1497
defaultConfig.versionCode = 1517
sourceSets.debug {
manifest.srcFile 'config/debug/AndroidManifest.xml'
}
sourceSets.debugMultidex {
manifest.srcFile 'config/debug/AndroidManifest.xml'
java.srcDirs = ['src/multidex/java']
}
sourceSets.HA {
manifest.srcFile 'config/debug/AndroidManifest.xml'
}
@ -125,10 +134,6 @@ android {
manifest.srcFile 'config/release/AndroidManifest.xml'
}
sourceSets.foss {
manifest.srcFile 'config/foss/AndroidManifest.xml'
}
flavorDimensions "minApi"
productFlavors {
@ -249,10 +254,17 @@ android {
}
}
variantFilter { variant ->
def names = variant.flavors*.name
if(variant.buildType.name!="release" && !names.contains("afat")){
setIgnore(true)
}
}
defaultConfig {
minSdkVersion 16
targetSdkVersion 27
versionName "5.3.1"
versionName "5.4.0"
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
@ -262,6 +274,8 @@ android {
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
}
}
manifestPlaceholders = [applicationClassName:"ApplicationLoader"]
}
}

View File

@ -65,6 +65,22 @@ include $(PREBUILT_STATIC_LIBRARY)
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
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
@ -255,13 +271,13 @@ include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
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 += -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_CPPFLAGS := -DBSD=1 -ffast-math -Os -funroll-loops -std=c++11
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 := \
./opus/src/opus.c \
@ -523,10 +539,10 @@ endif
LOCAL_SRC_FILES += \
./jni.c \
./audio.c \
./utils.c \
./image.c \
./video.c \
./intro/IntroRenderer.c \
./utilities.cpp \
./gifvideo.cpp \
./SqliteWrapper.cpp \
./TgNetWrapper.cpp \

View File

@ -6,7 +6,7 @@
#include <time.h>
#include <opusfile.h>
#include <math.h>
#include "utils.h"
#include "c_utils.h"
typedef struct {
int version;
@ -335,7 +335,7 @@ int initRecorder(const char *path) {
}
#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) {
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;
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)) {
@ -548,7 +548,7 @@ JNIEXPORT jbyteArray Java_org_telegram_messenger_MediaController_getWaveform2(JN
uint16_t *samples = malloc(100 * 2);
uint64_t sampleIndex = 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;
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);
memset(bytes, 0, bitstreamLength + 4);
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);
}
(*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) {
int64_t totalSamples = op_pcm_total(opusFile, -1);
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);
@ -667,7 +667,7 @@ JNIEXPORT jbyteArray Java_org_telegram_messenger_MediaController_getWaveform(JNI
memset(bytes, 0, bitstreamLength + 4);
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);
}

View File

@ -17,13 +17,11 @@
#define LOGV(...)
#endif
#ifndef max
#define max(x, y) ((x) > (y)) ? (x) : (y)
#ifndef MAX
#define MAX(x, y) ((x) > (y)) ? (x) : (y)
#endif
#ifndef min
#define min(x, y) ((x) < (y)) ? (x) : (y)
#ifndef MIN
#define MIN(x, y) ((x) < (y)) ? (x) : (y)
#endif
void throwException(JNIEnv *env, char *format, ...);
#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_SCREENPRESSO,
AV_CODEC_ID_RSCC,
AV_CODEC_ID_AVS2,
AV_CODEC_ID_Y41P = 0x8000,
AV_CODEC_ID_AVRP,
@ -446,6 +447,13 @@ enum AVCodecID {
AV_CODEC_ID_SVG,
AV_CODEC_ID_GDV,
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" */
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_F16LE,
AV_CODEC_ID_PCM_F24LE,
AV_CODEC_ID_PCM_VIDC,
/* various ADPCM codecs */
AV_CODEC_ID_ADPCM_IMA_QT = 0x11000,
@ -615,6 +624,7 @@ enum AVCodecID {
AV_CODEC_ID_PAF_AUDIO,
AV_CODEC_ID_ON2AVC,
AV_CODEC_ID_DSS_SP,
AV_CODEC_ID_CODEC2,
AV_CODEC_ID_FFWAVESYNTH = 0x15800,
AV_CODEC_ID_SONIC,
@ -634,6 +644,10 @@ enum AVCodecID {
AV_CODEC_ID_ATRAC3PAL,
AV_CODEC_ID_DOLBY_E,
AV_CODEC_ID_APTX,
AV_CODEC_ID_APTX_HD,
AV_CODEC_ID_SBC,
AV_CODEC_ID_ATRAC9,
AV_CODEC_ID_HCOM,
/* 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_ASS,
AV_CODEC_ID_HDMV_TEXT_SUBTITLE,
AV_CODEC_ID_TTML,
AV_CODEC_ID_ARIB_CAPTION,
/* 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.
@ -1059,6 +1075,13 @@ typedef struct RcOverride{
*/
#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.
* This specifies the area which should be displayed.
@ -1098,17 +1121,29 @@ typedef struct AVCPBProperties {
* Maximum bitrate of the stream, in bits per second.
* Zero if unknown or unspecified.
*/
#if FF_API_UNSANITIZED_BITRATES
int max_bitrate;
#else
int64_t max_bitrate;
#endif
/**
* Minimum bitrate of the stream, in bits per second.
* Zero if unknown or unspecified.
*/
#if FF_API_UNSANITIZED_BITRATES
int min_bitrate;
#else
int64_t min_bitrate;
#endif
/**
* Average bitrate of the stream, in bits per second.
* Zero if unknown or unspecified.
*/
#if FF_API_UNSANITIZED_BITRATES
int avg_bitrate;
#else
int64_t avg_bitrate;
#endif
/**
* The size of the buffer to which the ratecontrol is applied, in bits.
@ -1309,7 +1344,7 @@ enum AVPacketSideDataType {
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.
*/
AV_PKT_DATA_MPEGTS_STREAM_ID,
@ -1341,6 +1376,25 @@ enum AVPacketSideDataType {
*/
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.
* 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
* 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.
* Must be allocated with the av_malloc() family of functions.
* - encoding: Set/allocated/freed by libavcodec.
* - 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
* 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.
*/
int64_t reordered_opaque;
@ -2917,6 +2975,28 @@ typedef struct AVCodecContext {
#define FF_PROFILE_HEVC_MAIN_STILL_PICTURE 3
#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
* - 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_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_IGNORE 2 ///< neither convert the subtitles, nor check them for valid UTF-8
/**
* Skip processing alpha if supported by codec.
@ -3254,6 +3335,28 @@ typedef struct AVCodecContext {
* (with the display dimensions being determined by the crop_* fields).
*/
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;
#if FF_API_CODEC_GET_SET
@ -3439,6 +3542,9 @@ typedef struct AVCodec {
/**
* 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);
@ -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
* 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
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);
/**
* 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
* timebase to another. Timestamps with unknown values (AV_NOPTS_VALUE) will be
@ -5693,6 +5826,7 @@ typedef struct AVBitStreamFilter {
int (*init)(AVBSFContext *ctx);
int (*filter)(AVBSFContext *ctx, AVPacket *pkt);
void (*close)(AVBSFContext *ctx);
void (*flush)(AVBSFContext *ctx);
} AVBitStreamFilter;
#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);
/**
* 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
* into the supplied pointer.

View File

@ -85,4 +85,17 @@ typedef struct MediaCodecBuffer AVMediaCodecBuffer;
*/
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 */

View File

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

View File

@ -845,6 +845,8 @@ typedef struct AVStreamInternal AVStreamInternal;
#define AV_DISPOSITION_CAPTIONS 0x10000
#define AV_DISPOSITION_DESCRIPTIONS 0x20000
#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.
@ -1101,6 +1103,13 @@ typedef struct AVStream {
*/
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_duration;
@ -1258,6 +1267,7 @@ typedef struct AVProgram {
int program_num;
int pmt_pid;
int pcr_pid;
int pmt_version;
/*****************************************************************
* 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.
*/
#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_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
@ -1925,6 +1937,13 @@ typedef struct AVFormatContext {
* - decoding: set by user
*/
int max_streams;
/**
* Skip duration calcuation in estimate_timings_from_pts.
* - encoding: unused
* - decoding: set by user
*/
int skip_estimate_duration_from_pts;
} AVFormatContext;
#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);
int64_t (*seek)(void *opaque, int64_t offset, int whence);
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 max_packet_size;
unsigned long checksum;
@ -566,8 +566,8 @@ static av_always_inline int64_t avio_tell(AVIOContext *s)
int64_t avio_size(AVIOContext *s);
/**
* feof() equivalent for AVIOContext.
* @return non zero if and only if end of file
* Similar to feof() but also returns nonzero on read errors.
* @return non zero if and only if at end of file or a read error happened when reading.
*/
int avio_feof(AVIOContext *s);

View File

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

View File

@ -66,7 +66,7 @@
#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
*/

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);
/**
* 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)
{
if (a&(~0xFF)) return (-a)>>31;
if (a&(~0xFF)) return (~a)>>31;
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)
{
if (a&(~0xFFFF)) return (-a)>>31;
if (a&(~0xFFFF)) return (~a)>>31;
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)
{
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;
}

View File

@ -54,6 +54,7 @@ typedef enum {
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_24_IEEE,
AV_CRC_8_EBU,
AV_CRC_MAX, /*< Not part of public API! Do not use outside libavutil. */
}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! */
#ifndef AVUTIL_FFVERSION_H
#define AVUTIL_FFVERSION_H
#define FFMPEG_VERSION "3.4.git"
#define FFMPEG_VERSION "N-93231-g96d79ff5b5"
#endif /* AVUTIL_FFVERSION_H */

View File

@ -33,6 +33,8 @@
* allocated buffer or map it with mmap() when available.
* In case of success set *bufptr to the read or mmapped buffer, and
* *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().
*
* @param log_offset loglevel offset used for logging

View File

@ -141,6 +141,44 @@ enum AVFrameSideDataType {
* metadata key entry "name".
*/
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 {
@ -168,6 +206,35 @@ typedef struct AVFrameSideData {
AVBufferRef *buf;
} 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.
*
@ -364,7 +431,6 @@ typedef struct AVFrame {
* that time,
* 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
* @deprecated in favor of pkt_pts
*/
int64_t reordered_opaque;
@ -529,6 +595,7 @@ typedef struct AVFrame {
attribute_deprecated
int qscale_type;
attribute_deprecated
AVBufferRef *qp_table_buf;
#endif
/**
@ -800,6 +867,22 @@ AVFrameSideData *av_frame_new_side_data(AVFrame *frame,
enum AVFrameSideDataType type,
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
* is no side data with such type in this frame.

View File

@ -29,6 +29,8 @@
#include <stdint.h>
#include "version.h"
/**
* @defgroup lavu_hash Hash Functions
* @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] 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);
#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.

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 {
CUcontext cuda_ctx;
CUstream stream;
AVCUDADeviceContextInternal *internal;
} AVCUDADeviceContext;

View File

@ -58,6 +58,9 @@ typedef struct AVDRMObjectDescriptor {
size_t size;
/**
* 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;
} 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)
#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
* 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
* 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.
@ -363,10 +363,10 @@ av_alloc_size(2, 3) int av_reallocp_array(void *ptr, size_t nmemb, size_t size);
* @endcode
*
* @param[in,out] ptr Already allocated buffer, or `NULL`
* @param[in,out] size Pointer to current size of buffer `ptr`. `*size` is
* changed to `min_size` in case of success or 0 in
* case of failure
* @param[in] min_size New size of buffer `ptr`
* @param[in,out] size Pointer to the size of buffer `ptr`. `*size` is
* updated to the new allocated size, in particular 0
* in case of failure.
* @param[in] min_size Desired minimal size of buffer `ptr`
* @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
* 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.
* `*ptr` will be overwritten with pointer to new
* buffer on success or `NULL` on failure
* @param[in,out] size Pointer to current size of buffer `*ptr`. `*size` is
* changed to `min_size` in case of success or 0 in
* case of failure
* @param[in] min_size New size of buffer `*ptr`
* @param[in,out] size Pointer to the size of buffer `*ptr`. `*size` is
* updated to the new allocated size, in particular 0
* in case of failure.
* @param[in] min_size Desired minimal size of buffer `*ptr`
* @see av_realloc()
* @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.
* `*ptr` will be overwritten with pointer to new
* buffer on success or `NULL` on failure
* @param[in,out] size Pointer to current size of buffer `*ptr`. `*size` is
* changed to `min_size` in case of success or 0 in
* case of failure
* @param[in] min_size New size of buffer `*ptr`
* @param[in,out] size Pointer to the size of buffer `*ptr`. `*size` is
* updated to the new allocated size, in particular 0
* in case of failure.
* @param[in] min_size Desired minimal size of buffer `*ptr`
* @see av_fast_malloc()
*/
void av_fast_mallocz(void *ptr, unsigned int *size, size_t min_size);

View File

@ -29,6 +29,8 @@
#include <stdint.h>
#include "version.h"
/**
* @defgroup lavu_murmur3 Murmur3
* @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] 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);
#else
void av_murmur3_update(struct AVMurMur3 *c, const uint8_t *src, size_t len);
#endif
/**
* 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_DICT,
AV_OPT_TYPE_UINT64,
AV_OPT_TYPE_CONST = 128,
AV_OPT_TYPE_IMAGE_SIZE = MKBETAG('S','I','Z','E'), ///< offset must point to two consecutive integers
AV_OPT_TYPE_PIXEL_FMT = MKBETAG('P','F','M','T'),
AV_OPT_TYPE_SAMPLE_FMT = MKBETAG('S','F','M','T'),
AV_OPT_TYPE_VIDEO_RATE = MKBETAG('V','R','A','T'), ///< offset must point to AVRational
AV_OPT_TYPE_DURATION = MKBETAG('D','U','R',' '),
AV_OPT_TYPE_COLOR = MKBETAG('C','O','L','R'),
AV_OPT_TYPE_CHANNEL_LAYOUT = MKBETAG('C','H','L','A'),
AV_OPT_TYPE_BOOL = MKBETAG('B','O','O','L'),
AV_OPT_TYPE_CONST,
AV_OPT_TYPE_IMAGE_SIZE, ///< offset must point to two consecutive integers
AV_OPT_TYPE_PIXEL_FMT,
AV_OPT_TYPE_SAMPLE_FMT,
AV_OPT_TYPE_VIDEO_RATE, ///< offset must point to AVRational
AV_OPT_TYPE_DURATION,
AV_OPT_TYPE_COLOR,
AV_OPT_TYPE_CHANNEL_LAYOUT,
AV_OPT_TYPE_BOOL,
};
/**
@ -287,7 +287,9 @@ typedef struct AVOption {
* This flag only makes sense when AV_OPT_FLAG_EXPORT is also set.
*/
#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_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
/**

View File

@ -154,17 +154,21 @@ typedef struct AVPixFmtDescriptor {
* in some cases be simpler. Or the data can be interpreted purely based on
* the pixel format without using the palette.
* 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)
/**
* 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
* carry alpha as part of the palette. Details are explained in the
* AVPixelFormat enum, and are also encoded in the corresponding
* AVPixFmtDescriptor.
*
* The alpha is always straight, never pre-multiplied.
* support alpha in some way, including AV_PIX_FMT_PAL8. The alpha is always
* straight, never pre-multiplied.
*
* 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.
@ -339,7 +343,13 @@ char *av_get_pix_fmt_string(char *buf, int buf_size,
* format writes the values corresponding to the palette
* 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.
* @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],
const int linesize[4], const AVPixFmtDescriptor *desc,
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 w the width of the line to write, that is the number of
* 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],
const int linesize[4], const AVPixFmtDescriptor *desc,
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
* 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
* 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
@ -330,6 +334,17 @@ enum AVPixelFormat {
*/
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
};
@ -349,6 +364,7 @@ enum AVPixelFormat {
#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_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_YA16 AV_PIX_FMT_NE(YA16BE, YA16LE)
#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_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_YUVA422P9 AV_PIX_FMT_NE(YUVA422P9BE , YUVA422P9LE)
#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_YUVA422P10 AV_PIX_FMT_NE(YUVA422P10BE, YUVA422P10LE)
#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_YUVA422P16 AV_PIX_FMT_NE(YUVA422P16BE, YUVA422P16LE)
#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 len input data length
*/
#if FF_API_CRYPTO_SIZE_T
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.

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 (*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
*

View File

@ -79,7 +79,7 @@
*/
#define LIBAVUTIL_VERSION_MAJOR 56
#define LIBAVUTIL_VERSION_MINOR 7
#define LIBAVUTIL_VERSION_MINOR 26
#define LIBAVUTIL_VERSION_MICRO 100
#define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
@ -126,6 +126,9 @@
#ifndef FF_API_FRAME_GET_SET
#define FF_API_FRAME_GET_SET (LIBAVUTIL_VERSION_MAJOR < 57)
#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 <utils.h>
#include <libyuv.h>
#include <android/bitmap.h>
#include <cstdint>
#include <limits>
#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" {
#include <libavformat/avformat.h>
#include <libavutil/eval.h>
#include <libswscale/swscale.h>
static const std::string av_make_error_str(int errnum) {
char errbuf[AV_ERROR_MAX_STRING_SIZE];
@ -18,6 +24,11 @@ static const std::string av_make_error_str(int errnum) {
#undef av_err2str
#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 {
@ -38,12 +49,47 @@ typedef struct VideoInfo {
delete [] src;
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);
video_stream_idx = -1;
video_stream = nullptr;
}
AVFormatContext *fmt_ctx = nullptr;
char *src = nullptr;
int video_stream_idx = -1;
@ -53,14 +99,22 @@ typedef struct VideoInfo {
bool has_decoded_frames = false;
AVPacket pkt;
AVPacket orig_pkt;
};
bool stopped = false;
bool seeking = false;
jobject makeGlobarRef(JNIEnv *env, jobject object) {
if (object) {
return env->NewGlobalRef(object);
}
return 0;
}
uint8_t __attribute__ ((aligned (16))) *dst_data[1];
int32_t dst_linesize[1];
struct SwsContext *sws_ctx = nullptr;
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 ret, stream_index;
@ -119,7 +173,82 @@ int decode_packet(VideoInfo *info, int *got_frame) {
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();
char const *srcString = env->GetStringUTFChars(src, 0);
@ -130,12 +259,40 @@ jlong Java_org_telegram_ui_Components_AnimatedFileDrawable_createDecoder(JNIEnv
if (srcString != 0) {
env->ReleaseStringUTFChars(src, srcString);
}
int ret;
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 (streamFileSize != 0) {
info->file_size = streamFileSize;
info->stream = env->NewGlobalRef(stream);
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) {
@ -164,11 +321,12 @@ jlong Java_org_telegram_ui_Components_AnimatedFileDrawable_createDecoder(JNIEnv
av_init_packet(&info->pkt);
info->pkt.data = NULL;
info->pkt.size = 0;
jint *dataArr = env->GetIntArrayElements(data, 0);
if (dataArr != nullptr) {
dataArr[0] = info->video_dec_ctx->width;
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);
if (rotate_tag && *rotate_tag->value && strcmp(rotate_tag->value, "0")) {
char *tail;
@ -179,6 +337,8 @@ jlong Java_org_telegram_ui_Components_AnimatedFileDrawable_createDecoder(JNIEnv
} else {
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);
}
@ -192,19 +352,123 @@ void Java_org_telegram_ui_Components_AnimatedFileDrawable_destroyDecoder(JNIEnv
return;
}
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;
}
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) {
return 0;
}
//int64_t time = ConnectionsManager::getInstance(0).getCurrentTimeMonotonicMillis();
VideoInfo *info = (VideoInfo *) (intptr_t) ptr;
int ret = 0;
int got_frame = 0;
int32_t triesCount = 6;
while (triesCount != 0) {
while (!info->stopped && triesCount != 0) {
if (info->pkt.size == 0) {
ret = av_read_frame(info->fmt_ctx, &info->pkt);
//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;
}
}
if (info->pkt.size > 0) {
ret = decode_packet(info, &got_frame);
if (ret < 0) {
@ -225,7 +489,7 @@ jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv *
info->pkt.data += ret;
info->pkt.size -= ret;
}
if (info->pkt.size == 0) {
av_packet_unref(&info->orig_pkt);
}
@ -239,8 +503,7 @@ jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv *
}
if (got_frame == 0) {
if (info->has_decoded_frames) {
//LOGD("file end reached %s", info->src);
if ((ret = avformat_seek_file(info->fmt_ctx, -1, std::numeric_limits<int64_t>::min(), 0, std::numeric_limits<int64_t>::max(), 0)) < 0) {
if ((ret = av_seek_frame(info->fmt_ctx, info->video_stream_idx, 0, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME)) < 0) {
LOGE("can't seek to begin of file %s, %s", info->src, av_err2str(ret));
return 0;
} else {
@ -249,7 +512,7 @@ jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv *
}
}
}
if (ret < 0) {
if (ret < 0 || info->seeking) {
return 0;
}
if (got_frame) {
@ -261,7 +524,11 @@ jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv *
if (dataArr != nullptr) {
wantedWidth = dataArr[0];
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);
} else {
AndroidBitmapInfo bitmapInfo;
@ -269,26 +536,64 @@ jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv *
wantedWidth = bitmapInfo.width;
wantedHeight = bitmapInfo.height;
}
void *pixels;
if (AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0) {
if (info->frame->format == AV_PIX_FMT_YUV420P || info->frame->format == AV_PIX_FMT_YUVJ420P) {
//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 (wantedWidth == info->frame->width && wantedHeight == info->frame->height || wantedWidth == info->frame->height && wantedHeight == info->frame->width) {
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);
if (wantedWidth == info->frame->width && wantedHeight == info->frame->height || wantedWidth == info->frame->height && wantedHeight == info->frame->width) {
if (info->sws_ctx == nullptr) {
if (info->frame->format > AV_PIX_FMT_NONE && info->frame->format < AV_PIX_FMT_NB) {
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);
}
}
info->has_decoded_frames = true;
av_frame_unref(info->frame);
//LOGD("frame time %lld ms", ConnectionsManager::getInstance(0).getCurrentTimeMonotonicMillis() - time);
return 1;
}
triesCount--;
if (!info->has_decoded_frames) {
triesCount--;
}
}
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 <libwebp/webp/decode.h>
#include <libwebp/webp/encode.h>
#include "utils.h"
#include "c_utils.h"
#include "image.h"
jclass jclass_NullPointerException;
@ -28,34 +28,34 @@ jclass createGlobarRef(JNIEnv *env, jclass class) {
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"));
if (jclass_NullPointerException == 0) {
return -1;
return JNI_FALSE;
}
jclass_RuntimeException = createGlobarRef(env, (*env)->FindClass(env, "java/lang/RuntimeException"));
if (jclass_RuntimeException == 0) {
return -1;
return JNI_FALSE;
}
jclass_Options = createGlobarRef(env, (*env)->FindClass(env, "android/graphics/BitmapFactory$Options"));
if (jclass_Options == 0) {
return -1;
return JNI_FALSE;
}
jclass_Options_inJustDecodeBounds = (*env)->GetFieldID(env, jclass_Options, "inJustDecodeBounds", "Z");
if (jclass_Options_inJustDecodeBounds == 0) {
return -1;
return JNI_FALSE;
}
jclass_Options_outHeight = (*env)->GetFieldID(env, jclass_Options, "outHeight", "I");
if (jclass_Options_outHeight == 0) {
return -1;
return JNI_FALSE;
}
jclass_Options_outWidth = (*env)->GetFieldID(env, jclass_Options, "outWidth", "I");
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) {
@ -545,7 +545,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_calcCDT(JNIEnv *env, jclass
uint32_t totalSegments = PGPhotoEnhanceSegments * 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;
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;
for (uint32_t j = hMin; j < PGPhotoEnhanceHistogramBins; ++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];
@ -709,8 +709,6 @@ JNIEXPORT jboolean Java_org_telegram_messenger_Utilities_loadWebpImage(JNIEnv *e
}
#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); }
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 h=info.height;
int stride=info.stride;
unsigned char* pixels=0;
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++){
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[1] = pixels[offset + 1];
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);
}
int offset = (yw + vmin[x])*4;
int offset = (y*stride + vmin[x]*4);
sir[0] = pixels[offset];
sir[1] = pixels[offset + 1];
sir[2] = pixels[offset + 2];
@ -878,10 +877,9 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env
yp += w;
}
}
yi = x;
stackpointer = radius;
for (y = 0;y < h;y++) {
int offset = yi*4;
int offset = stride*y+x*4;
pixels[offset] = dv[rsum];
pixels[offset + 1] = dv[gsum];
pixels[offset + 2] = dv[bsum];
@ -897,7 +895,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env
boutsum -= sir[2];
if (x == 0){
vmin[y] = MIN(y + r1, hm)*w;
vmin[y] = (MIN(y + r1, hm))*w;
}
p = x + vmin[y];

View File

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

View File

@ -13,6 +13,7 @@
#include "libtgvoip/client/android/tg_voip_jni.h"
int registerNativeTgNetFunctions(JavaVM *vm, JNIEnv *env);
int videoOnJNILoad(JavaVM *vm, JNIEnv *env);
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = 0;
@ -22,7 +23,11 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
return -1;
}
if (imageOnJNILoad(vm, reserved, env) == -1) {
if (imageOnJNILoad(vm, env) != JNI_TRUE) {
return -1;
}
if (videoOnJNILoad(vm, env) != JNI_TRUE) {
return -1;
}
@ -163,19 +168,6 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_aesCbcEncryption(JNIEnv *en
(*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 value = 0;
DIR *dir;
@ -209,7 +201,7 @@ int64_t listdir(const char *fileName, int32_t mode, int32_t docType, int64_t tim
} else {
stat(buff, &attrib);
if (mode == 0) {
value += attrib.st_size;
value += 512 * attrib.st_blocks;
} else if (mode == 1) {
if (attrib.st_atim.tv_sec != 0) {
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()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION "3.26.0"
#define SQLITE_VERSION_NUMBER 3026000
#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9"
#define SQLITE_VERSION "3.27.2"
#define SQLITE_VERSION_NUMBER 3027002
#define SQLITE_SOURCE_ID "2019-02-25 16:06:06 bd49a8271d650fa89e446b42e513b595a717b9212c91dd384aab871fc1d0f6d7"
/*
** 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 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]]
** 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
@ -1131,6 +1140,7 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33
#define SQLITE_FCNTL_LOCK_TIMEOUT 34
#define SQLITE_FCNTL_DATA_VERSION 35
#define SQLITE_FCNTL_SIZE_LIMIT 36
/* deprecated names */
#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.
** This option is only available if SQLite is compiled with the
** [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>
*/
#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_SMALL_MALLOC 27 /* boolean */
#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */
/*
** 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
** 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
** connection D. Any changes by other database connections are ignored.
** 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
** is only capable of millisecond resolution so the six least significant
** digits in the time are meaningless. Future versions of SQLite
** might provide greater resolution on the profiler callback. The
** sqlite3_profile() function is considered experimental and is
** subject to change in future versions of SQLite.
** might provide greater resolution on the profiler callback. Invoking
** either [sqlite3_trace()] or [sqlite3_trace_v2()] will cancel the
** profile callback.
*/
SQLITE_API SQLITE_DEPRECATED void *sqlite3_trace(sqlite3*,
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
** VFS method, then the behavior of this routine is undefined and probably
** undesirable.
**
** See the [URI filename] documentation for additional information.
*/
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);
@ -3629,18 +3653,23 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** deplete the limited store of lookaside memory. Future versions of
** SQLite may act on this hint differently.
**
** [[SQLITE_PREPARE_NORMALIZE]] ^(<dt>SQLITE_PREPARE_NORMALIZE</dt>
** <dd>The SQLITE_PREPARE_NORMALIZE flag indicates that a normalized
** representation of the SQL statement should be calculated and then
** associated with the prepared statement, which can be obtained via
** the [sqlite3_normalized_sql()] interface.)^ The semantics used to
** normalize a SQL statement are unspecified and subject to change.
** At a minimum, literal values will be replaced with suitable
** placeholders.
** [[SQLITE_PREPARE_NORMALIZE]] <dt>SQLITE_PREPARE_NORMALIZE</dt>
** <dd>The SQLITE_PREPARE_NORMALIZE flag is a no-op. This flag used
** to be required for any prepared statement that wanted to use the
** [sqlite3_normalized_sql()] interface. However, the
** [sqlite3_normalized_sql()] interface is now available to all
** prepared statements, regardless of whether or not they use this
** flag.
**
** [[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>
*/
#define SQLITE_PREPARE_PERSISTENT 0x01
#define SQLITE_PREPARE_NORMALIZE 0x02
#define SQLITE_PREPARE_NO_VTAB 0x04
/*
** 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
** 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
** 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
** [sqlite3session_indirect()] for a description of direct and indirect
** 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
** 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
** with the offsets=0 option specified. In this case *piOff is always
** set to -1.
**
** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM)
** if an error occurs.
** first token of the phrase. 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
** "detail=none" or "detail=column" option.
@ -11524,11 +11549,11 @@ struct Fts5ExtensionApi {
** the tokenizer substitutes "first" for "1st" and the query works
** as expected.
**
** <li> By adding multiple synonyms for a single term to the FTS index.
** In this case, when tokenizing query text, the tokenizer may
** provide multiple synonyms for a single term within the document.
** FTS5 then queries the index for each synonym individually. For
** example, faced with the query:
** <li> By querying the index for all synonyms of each query term
** separately. In this case, when tokenizing query text, the
** tokenizer may provide multiple synonyms for a single term
** within the document. FTS5 then queries the index for each
** synonym individually. For example, faced with the query:
**
** <codeblock>
** ... MATCH 'first place'</codeblock>
@ -11552,7 +11577,7 @@ struct Fts5ExtensionApi {
** "place".
**
** 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
** 'first + place' or '1st + place', as there are entries in the
** 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" />
<application
android:name=".ApplicationLoader"
android:name="org.telegram.messenger.${applicationClassName}"
android:allowBackup="false"
android:hardwareAccelerated="@bool/useHardwareAcceleration"
android:icon="@drawable/ic_launcher"
@ -364,6 +364,11 @@
android:exported="false"
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" />
<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" />

View File

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

View File

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

View File

@ -2,3 +2,4 @@ change SimpleExoPlayer.java.java
change VideoListener.java
change AspectRatioFrameLayout.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
public void release() {
public void release(boolean async) {
Log.i(TAG, "Release " + Integer.toHexString(System.identityHashCode(this)) + " ["
+ ExoPlayerLibraryInfo.VERSION_SLASHY + "] [" + Util.DEVICE_DEBUG_INFO + "] ["
+ ExoPlayerLibraryInfo.registeredModules() + "]");

View File

@ -921,8 +921,8 @@ public final class Format implements Parcelable {
this.height = height;
this.frameRate = frameRate;
this.rotationDegrees = rotationDegrees == Format.NO_VALUE ? 0 : rotationDegrees;
this.pixelWidthHeightRatio =
pixelWidthHeightRatio == Format.NO_VALUE ? 1 : pixelWidthHeightRatio;
this.pixelWidthHeightRatio = 1;
//pixelWidthHeightRatio == Format.NO_VALUE ? 1 : pixelWidthHeightRatio; TODO remove after ffmpeg support
this.projectionData = projectionData;
this.stereoMode = stereoMode;
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
* player must not be used after calling this method.
*/
void release();
void release(boolean async);
/**
* 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.VideoRendererEventListener;
import com.google.android.exoplayer2.video.spherical.CameraMotionListener;
import org.telegram.messenger.Utilities;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -986,9 +989,13 @@ public class SimpleExoPlayer extends BasePlayer
}
@Override
public void release() {
public void release(boolean async) {
audioFocusManager.handleStop();
player.release();
if (async) {
Utilities.globalQueue.postRunnable(() -> player.release(async));
} else {
player.release(async);
}
removeSurfaceCallbacks();
if (surface != null) {
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
* 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;

View File

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

View File

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

View File

@ -1853,14 +1853,33 @@ public class AndroidUtilities {
}
public static String formatFileSize(long size) {
return formatFileSize(size, false);
}
public static String formatFileSize(long size, boolean removeZero) {
if (size < 1024) {
return String.format("%d B", size);
} 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) {
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 {
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;
}
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 ty, sy;
boolean xLarger = false;
if (rotation == 90 || rotation == 270) {
sx = dst.height() / src.width();
sy = dst.width() / src.height();
@ -2142,17 +2162,16 @@ public class AndroidUtilities {
sx = dst.width() / src.width();
sy = dst.height() / src.height();
}
if (align != Matrix.ScaleToFit.FILL) {
if (sx > sy) {
sx = sy;
} else {
sy = sx;
}
if (sx < sy) {
sx = sy;
xLarger = true;
} else {
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) {
matrix.preRotate(90);
matrix.preTranslate(0, -dst.width());
@ -2164,8 +2183,31 @@ public class AndroidUtilities {
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.preTranslate(tx, ty);
if (translate) {
matrix.preTranslate(tx, ty);
}
}
public static boolean handleProxyIntent(Activity activity, Intent intent) {
@ -2538,4 +2580,10 @@ public class AndroidUtilities {
}
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() {
@Override
public void onReceive(Context context, Intent intent) {
currentNetworkInfo = connectivityManager.getActiveNetworkInfo();
try {
currentNetworkInfo = connectivityManager.getActiveNetworkInfo();
} catch (Throwable ignore) {
}
boolean isSlow = isConnectionSlow();
for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) {
ConnectionsManager.getInstance(a).checkConnection();
@ -151,11 +156,24 @@ public class ApplicationLoader extends Application {
WearDataLayerListenerService.updateWatchConnectionState();
}
public ApplicationLoader() {
super();
}
@Override
public void onCreate() {
try {
applicationContext = getApplicationContext();
} catch (Throwable ignore) {
}
super.onCreate();
applicationContext = getApplicationContext();
if (applicationContext == null) {
applicationContext = getApplicationContext();
}
NativeLoader.initNativeLibs(ApplicationLoader.applicationContext);
ConnectionsManager.native_setJava(false);
new ForegroundDetector(this);

View File

@ -18,8 +18,8 @@ public class BuildVars {
public static boolean LOGS_ENABLED = false;
public static boolean USE_CLOUD_STRINGS = true;
public static boolean CHECK_UPDATES = false;
public static int BUILD_VERSION = 1497;
public static String BUILD_VERSION_STRING = "5.3.0";
public static int BUILD_VERSION = 1517;
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 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";

View File

@ -2008,8 +2008,8 @@ public class ContactsController {
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();
int value = contentResolver.delete(rawContactUri, ContactsContract.RawContacts.SYNC2 + " = " + user.id, null);
} catch (Exception e) {
FileLog.e(e);
} catch (Exception ignore) {
}
}
@ -2049,8 +2049,8 @@ public class ContactsController {
if (result != null && result.length > 0 && result[0].uri != null) {
res = Long.parseLong(result[0].uri.getLastPathSegment());
}
} catch (Exception e) {
FileLog.e(e);
} catch (Exception ignore) {
}
synchronized (observerLock) {
ignoreChanges = false;

View File

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

View File

@ -17,6 +17,7 @@ import android.net.ConnectivityManager;
import android.util.LongSparseArray;
import android.util.SparseArray;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.TLRPC;
import java.lang.ref.WeakReference;
@ -33,27 +34,25 @@ public class DownloadController implements NotificationCenter.NotificationCenter
int getObserverTag();
}
public static final int AUTODOWNLOAD_MASK_PHOTO = 1;
public static final int AUTODOWNLOAD_MASK_AUDIO = 2;
public static final int AUTODOWNLOAD_MASK_VIDEO = 4;
public static final int AUTODOWNLOAD_MASK_DOCUMENT = 8;
public static final int AUTODOWNLOAD_MASK_MUSIC = 16;
public static final int AUTODOWNLOAD_MASK_GIF = 32;
public static final int AUTODOWNLOAD_MASK_VIDEOMESSAGE = 64;
public boolean globalAutodownloadEnabled;
public int mobileDataDownloadMask[] = new int[4];
public int wifiDownloadMask[] = new int[4];
public int roamingDownloadMask[] = new int[4];
public int mobileMaxFileSize[] = new int[7];
public int wifiMaxFileSize[] = new int[7];
public int roamingMaxFileSize[] = new int[7];
public static final int AUTODOWNLOAD_TYPE_PHOTO = 1;
public static final int AUTODOWNLOAD_TYPE_AUDIO = 2;
public static final int AUTODOWNLOAD_TYPE_VIDEO = 4;
public static final int AUTODOWNLOAD_TYPE_DOCUMENT = 8;
public static final int PRESET_NUM_CONTACT = 0;
public static final int PRESET_NUM_PM = 1;
public static final int PRESET_NUM_GROUP = 2;
public static final int PRESET_NUM_CHANNEL = 3;
public static final int PRESET_SIZE_NUM_PHOTO = 0;
public static final int PRESET_SIZE_NUM_VIDEO = 1;
public static final int PRESET_SIZE_NUM_DOCUMENT = 2;
public static final int PRESET_SIZE_NUM_AUDIO = 3;
private int lastCheckMask = 0;
private ArrayList<DownloadObject> photoDownloadQueue = new ArrayList<>();
private ArrayList<DownloadObject> audioDownloadQueue = new ArrayList<>();
private ArrayList<DownloadObject> videoMessageDownloadQueue = 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 HashMap<String, DownloadObject> downloadQueueKeys = new HashMap<>();
@ -65,8 +64,137 @@ public class DownloadController implements NotificationCenter.NotificationCenter
private ArrayList<FileDownloadProgressListener> deleteLaterArray = new ArrayList<>();
private int lastTag = 0;
private boolean loadingAutoDownloadConfig;
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 static volatile DownloadController Instance[] = new DownloadController[UserConfig.MAX_ACCOUNT_COUNT];
@ -86,32 +214,63 @@ public class DownloadController implements NotificationCenter.NotificationCenter
public DownloadController(int instance) {
currentAccount = instance;
SharedPreferences preferences = MessagesController.getMainSettings(currentAccount);
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_MASK_PHOTO | AUTODOWNLOAD_MASK_AUDIO | AUTODOWNLOAD_MASK_MUSIC | AUTODOWNLOAD_MASK_GIF | AUTODOWNLOAD_MASK_VIDEOMESSAGE);
wifiDownloadMask[a] = preferences.getInt("wifiDownloadMask" + (a == 0 ? "" : a), AUTODOWNLOAD_MASK_PHOTO | AUTODOWNLOAD_MASK_AUDIO | AUTODOWNLOAD_MASK_MUSIC | AUTODOWNLOAD_MASK_GIF | AUTODOWNLOAD_MASK_VIDEOMESSAGE);
roamingDownloadMask[a] = preferences.getInt("roamingDownloadMask" + (a == 0 ? "" : a), 0);
} else {
mobileDataDownloadMask[a] = mobileDataDownloadMask[0];
wifiDownloadMask[a] = wifiDownloadMask[0];
roamingDownloadMask[a] = roamingDownloadMask[0];
lowPreset = new Preset(preferences.getString("preset0", "1_1_1_1_1048576_512000_512000_524288_0_0_1_1"));
mediumPreset = new Preset(preferences.getString("preset1", "13_13_13_13_1048576_10485760_1048576_524288_1_1_1_0"));
highPreset = new Preset(preferences.getString("preset2", "13_13_13_13_1048576_15728640_3145728_524288_1_1_1_0"));
boolean newConfig;
if (newConfig = preferences.contains("newConfig") || !UserConfig.getInstance(currentAccount).isClientActivated()) {
mobilePreset = new Preset(preferences.getString("mobilePreset", mediumPreset.toString()));
wifiPreset = new Preset(preferences.getString("wifiPreset", highPreset.toString()));
roamingPreset = new Preset(preferences.getString("roamingPreset", lowPreset.toString()));
currentMobilePreset = preferences.getInt("currentMobilePreset", 3);
currentWifiPreset = preferences.getInt("currentWifiPreset", 3);
currentRoamingPreset = preferences.getInt("currentRoamingPreset", 3);
if (!newConfig) {
preferences.edit().putBoolean("newConfig", true).commit();
}
}
for (int a = 0; a < 7; a++) {
int sdefault;
if (a == 1) {
sdefault = 2 * 1024 * 1024;
} else if (a == 6) {
sdefault = 5 * 1024 * 1024;
} else {
sdefault = 10 * 1024 * 1024;
} else {
int mobileDataDownloadMask[] = new int[4];
int wifiDownloadMask[] = new int[4];
int roamingDownloadMask[] = new int[4];
int mobileMaxFileSize[] = new int[7];
int wifiMaxFileSize[] = new int[7];
int roamingMaxFileSize[] = new int[7];
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);
roamingMaxFileSize[a] = preferences.getInt("roamingMaxDownloadSize" + a, sdefault);
mobileMaxFileSize[2] = preferences.getInt("mobileMaxDownloadSize" + 2, mediumPreset.sizes[PRESET_SIZE_NUM_VIDEO]);
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(() -> {
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.httpFileDidLoad);
NotificationCenter.getInstance(currentAccount).addObserver(DownloadController.this, NotificationCenter.httpFileDidFailedLoad);
loadAutoDownloadConfig(false);
});
BroadcastReceiver networkStateReceiver = new BroadcastReceiver() {
@ -136,72 +296,145 @@ public class DownloadController implements NotificationCenter.NotificationCenter
}
}
public static int maskToIndex(int mask) {
if (mask == AUTODOWNLOAD_MASK_PHOTO) {
return 0;
} 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;
public void loadAutoDownloadConfig(boolean force) {
if (loadingAutoDownloadConfig || !force && Math.abs(System.currentTimeMillis() - UserConfig.getInstance(currentAccount).autoDownloadConfigLoadTime) < 24 * 60 * 60 * 1000) {
return;
}
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() {
photoDownloadQueue.clear();
audioDownloadQueue.clear();
videoMessageDownloadQueue.clear();
documentDownloadQueue.clear();
videoDownloadQueue.clear();
musicDownloadQueue.clear();
gifDownloadQueue.clear();
downloadQueueKeys.clear();
typingTimes.clear();
}
public int getAutodownloadMask() {
if (!globalAutodownloadEnabled) {
return 0;
}
int result = 0;
int masksArray[];
if (ApplicationLoader.isConnectedToWiFi()) {
masksArray = wifiDownloadMask;
if (!wifiPreset.enabled) {
return 0;
}
masksArray = getCurrentWiFiPreset().mask;
} else if (ApplicationLoader.isRoaming()) {
masksArray = roamingDownloadMask;
if (!roamingPreset.enabled) {
return 0;
}
masksArray = getCurrentRoamingPreset().mask;
} 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;
if ((masksArray[a] & AUTODOWNLOAD_MASK_PHOTO) != 0) {
mask |= AUTODOWNLOAD_MASK_PHOTO;
if ((masksArray[a] & AUTODOWNLOAD_TYPE_PHOTO) != 0) {
mask |= AUTODOWNLOAD_TYPE_PHOTO;
}
if ((masksArray[a] & AUTODOWNLOAD_MASK_AUDIO) != 0) {
mask |= AUTODOWNLOAD_MASK_AUDIO;
if ((masksArray[a] & AUTODOWNLOAD_TYPE_AUDIO) != 0) {
mask |= AUTODOWNLOAD_TYPE_AUDIO;
}
if ((masksArray[a] & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0) {
mask |= AUTODOWNLOAD_MASK_VIDEOMESSAGE;
if ((masksArray[a] & AUTODOWNLOAD_TYPE_VIDEO) != 0) {
mask |= AUTODOWNLOAD_TYPE_VIDEO;
}
if ((masksArray[a] & AUTODOWNLOAD_MASK_VIDEO) != 0) {
mask |= AUTODOWNLOAD_MASK_VIDEO;
}
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;
if ((masksArray[a] & AUTODOWNLOAD_TYPE_DOCUMENT) != 0) {
mask |= AUTODOWNLOAD_TYPE_DOCUMENT;
}
result |= mask << (a * 8);
}
@ -209,31 +442,22 @@ public class DownloadController implements NotificationCenter.NotificationCenter
}
protected int getAutodownloadMaskAll() {
if (!globalAutodownloadEnabled) {
if (!mobilePreset.enabled && !roamingPreset.enabled && !wifiPreset.enabled) {
return 0;
}
int mask = 0;
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) {
mask |= AUTODOWNLOAD_MASK_PHOTO;
if ((getCurrentMobilePreset().mask[a] & AUTODOWNLOAD_TYPE_PHOTO) != 0 || (getCurrentWiFiPreset().mask[a] & AUTODOWNLOAD_TYPE_PHOTO) != 0 || (getCurrentRoamingPreset().mask[a] & AUTODOWNLOAD_TYPE_PHOTO) != 0) {
mask |= AUTODOWNLOAD_TYPE_PHOTO;
}
if ((mobileDataDownloadMask[a] & AUTODOWNLOAD_MASK_AUDIO) != 0 || (wifiDownloadMask[a] & AUTODOWNLOAD_MASK_AUDIO) != 0 || (roamingDownloadMask[a] & AUTODOWNLOAD_MASK_AUDIO) != 0) {
mask |= AUTODOWNLOAD_MASK_AUDIO;
if ((getCurrentMobilePreset().mask[a] & AUTODOWNLOAD_TYPE_AUDIO) != 0 || (getCurrentWiFiPreset().mask[a] & AUTODOWNLOAD_TYPE_AUDIO) != 0 || (getCurrentRoamingPreset().mask[a] & AUTODOWNLOAD_TYPE_AUDIO) != 0) {
mask |= AUTODOWNLOAD_TYPE_AUDIO;
}
if ((mobileDataDownloadMask[a] & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0 || (wifiDownloadMask[a] & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0 || (roamingDownloadMask[a] & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0) {
mask |= AUTODOWNLOAD_MASK_VIDEOMESSAGE;
if ((getCurrentMobilePreset().mask[a] & AUTODOWNLOAD_TYPE_VIDEO) != 0 || (getCurrentWiFiPreset().mask[a] & AUTODOWNLOAD_TYPE_VIDEO) != 0 || (getCurrentRoamingPreset().mask[a] & AUTODOWNLOAD_TYPE_VIDEO) != 0) {
mask |= AUTODOWNLOAD_TYPE_VIDEO;
}
if ((mobileDataDownloadMask[a] & AUTODOWNLOAD_MASK_VIDEO) != 0 || (wifiDownloadMask[a] & AUTODOWNLOAD_MASK_VIDEO) != 0 || (roamingDownloadMask[a] & AUTODOWNLOAD_MASK_VIDEO) != 0) {
mask |= AUTODOWNLOAD_MASK_VIDEO;
}
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;
if ((getCurrentMobilePreset().mask[a] & AUTODOWNLOAD_TYPE_DOCUMENT) != 0 || (getCurrentWiFiPreset().mask[a] & AUTODOWNLOAD_TYPE_DOCUMENT) != 0 || (getCurrentRoamingPreset().mask[a] & AUTODOWNLOAD_TYPE_DOCUMENT) != 0) {
mask |= AUTODOWNLOAD_TYPE_DOCUMENT;
}
}
return mask;
@ -245,9 +469,9 @@ public class DownloadController implements NotificationCenter.NotificationCenter
return;
}
lastCheckMask = currentMask;
if ((currentMask & AUTODOWNLOAD_MASK_PHOTO) != 0) {
if ((currentMask & AUTODOWNLOAD_TYPE_PHOTO) != 0) {
if (photoDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_PHOTO);
newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_PHOTO);
}
} else {
for (int a = 0; a < photoDownloadQueue.size(); a++) {
@ -260,9 +484,9 @@ public class DownloadController implements NotificationCenter.NotificationCenter
}
photoDownloadQueue.clear();
}
if ((currentMask & AUTODOWNLOAD_MASK_AUDIO) != 0) {
if ((currentMask & AUTODOWNLOAD_TYPE_AUDIO) != 0) {
if (audioDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_AUDIO);
newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_AUDIO);
}
} else {
for (int a = 0; a < audioDownloadQueue.size(); a++) {
@ -271,20 +495,9 @@ public class DownloadController implements NotificationCenter.NotificationCenter
}
audioDownloadQueue.clear();
}
if ((currentMask & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 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 ((currentMask & AUTODOWNLOAD_TYPE_DOCUMENT) != 0) {
if (documentDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_DOCUMENT);
newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_DOCUMENT);
}
} else {
for (int a = 0; a < documentDownloadQueue.size(); a++) {
@ -294,9 +507,9 @@ public class DownloadController implements NotificationCenter.NotificationCenter
}
documentDownloadQueue.clear();
}
if ((currentMask & AUTODOWNLOAD_MASK_VIDEO) != 0) {
if ((currentMask & AUTODOWNLOAD_TYPE_VIDEO) != 0) {
if (videoDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_VIDEO);
newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_VIDEO);
}
} else {
for (int a = 0; a < videoDownloadQueue.size(); a++) {
@ -305,82 +518,40 @@ public class DownloadController implements NotificationCenter.NotificationCenter
}
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();
if (mask == 0) {
MessagesStorage.getInstance(currentAccount).clearDownloadQueue(0);
} else {
if ((mask & AUTODOWNLOAD_MASK_PHOTO) == 0) {
MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_MASK_PHOTO);
if ((mask & AUTODOWNLOAD_TYPE_PHOTO) == 0) {
MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_TYPE_PHOTO);
}
if ((mask & AUTODOWNLOAD_MASK_AUDIO) == 0) {
MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_MASK_AUDIO);
if ((mask & AUTODOWNLOAD_TYPE_AUDIO) == 0) {
MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_TYPE_AUDIO);
}
if ((mask & AUTODOWNLOAD_MASK_VIDEOMESSAGE) == 0) {
MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_MASK_VIDEOMESSAGE);
if ((mask & AUTODOWNLOAD_TYPE_VIDEO) == 0) {
MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_TYPE_VIDEO);
}
if ((mask & AUTODOWNLOAD_MASK_VIDEO) == 0) {
MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_MASK_VIDEO);
}
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);
if ((mask & AUTODOWNLOAD_TYPE_DOCUMENT) == 0) {
MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_TYPE_DOCUMENT);
}
}
}
public boolean canDownloadMedia(MessageObject messageObject) {
return canDownloadMedia(messageObject.messageOwner);
return canDownloadMedia(messageObject.messageOwner) == 1;
}
public boolean canDownloadMedia(TLRPC.Message message) {
if (!globalAutodownloadEnabled) {
return false;
}
public int canDownloadMedia(TLRPC.Message message) {
int type;
if (MessageObject.isPhoto(message) || MessageObject.isStickerMessage(message)) {
type = AUTODOWNLOAD_MASK_PHOTO;
boolean isVideo;
if ((isVideo = MessageObject.isVideoMessage(message)) || MessageObject.isGifMessage(message) || MessageObject.isRoundVideoMessage(message)) {
type = AUTODOWNLOAD_TYPE_VIDEO;
} else if (MessageObject.isVoiceMessage(message)) {
type = AUTODOWNLOAD_MASK_AUDIO;
} else if (MessageObject.isRoundVideoMessage(message)) {
type = AUTODOWNLOAD_MASK_VIDEOMESSAGE;
} 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;
type = AUTODOWNLOAD_TYPE_AUDIO;
} else if (MessageObject.isPhoto(message) || MessageObject.isStickerMessage(message)) {
type = AUTODOWNLOAD_TYPE_PHOTO;
} else {
type = AUTODOWNLOAD_MASK_DOCUMENT;
type = AUTODOWNLOAD_TYPE_DOCUMENT;
}
int mask;
int index;
@ -394,10 +565,18 @@ public class DownloadController implements NotificationCenter.NotificationCenter
index = 1;
}
} 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 {
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 {
index = 3;
}
@ -405,63 +584,132 @@ public class DownloadController implements NotificationCenter.NotificationCenter
} else {
index = 1;
}
Preset preset;
if (ApplicationLoader.isConnectedToWiFi()) {
mask = wifiDownloadMask[index];
maxSize = wifiMaxFileSize[maskToIndex(type)];
if (!wifiPreset.enabled) {
return 0;
}
preset = getCurrentWiFiPreset();
} else if (ApplicationLoader.isRoaming()) {
mask = roamingDownloadMask[index];
maxSize = roamingMaxFileSize[maskToIndex(type)];
if (!roamingPreset.enabled) {
return 0;
}
preset = getCurrentRoamingPreset();
} else {
mask = mobileDataDownloadMask[index];
maxSize = mobileMaxFileSize[maskToIndex(type)];
if (!mobilePreset.enabled) {
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() {
if (!globalAutodownloadEnabled) {
return 0;
}
if (ApplicationLoader.isConnectedToWiFi()) {
if (!wifiPreset.enabled) {
return 0;
}
int mask = 0;
for (int a = 0; a < 4; a++) {
mask |= wifiDownloadMask[a];
mask |= getCurrentWiFiPreset().mask[a];
}
return mask;
} else if (ApplicationLoader.isRoaming()) {
if (!roamingPreset.enabled) {
return 0;
}
int mask = 0;
for (int a = 0; a < 4; a++) {
mask |= roamingDownloadMask[a];
mask |= getCurrentRoamingPreset().mask[a];
}
return mask;
} else {
if (!mobilePreset.enabled) {
return 0;
}
int mask = 0;
for (int a = 0; a < 4; a++) {
mask |= mobileDataDownloadMask[a];
mask |= getCurrentMobilePreset().mask[a];
}
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) {
if (objects.isEmpty()) {
return;
}
ArrayList<DownloadObject> queue = null;
if (type == AUTODOWNLOAD_MASK_PHOTO) {
if (type == AUTODOWNLOAD_TYPE_PHOTO) {
queue = photoDownloadQueue;
} else if (type == AUTODOWNLOAD_MASK_AUDIO) {
} else if (type == AUTODOWNLOAD_TYPE_AUDIO) {
queue = audioDownloadQueue;
} else if (type == AUTODOWNLOAD_MASK_VIDEOMESSAGE) {
queue = videoMessageDownloadQueue;
} else if (type == AUTODOWNLOAD_MASK_VIDEO) {
} else if (type == AUTODOWNLOAD_TYPE_VIDEO) {
queue = videoDownloadQueue;
} else if (type == AUTODOWNLOAD_MASK_DOCUMENT) {
} else if (type == AUTODOWNLOAD_TYPE_DOCUMENT) {
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++) {
DownloadObject downloadObject = objects.get(a);
@ -494,26 +742,17 @@ public class DownloadController implements NotificationCenter.NotificationCenter
protected void newDownloadObjectsAvailable(int downloadMask) {
int mask = getCurrentDownloadMask();
if ((mask & AUTODOWNLOAD_MASK_PHOTO) != 0 && (downloadMask & AUTODOWNLOAD_MASK_PHOTO) != 0 && photoDownloadQueue.isEmpty()) {
MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_MASK_PHOTO);
if ((mask & AUTODOWNLOAD_TYPE_PHOTO) != 0 && (downloadMask & AUTODOWNLOAD_TYPE_PHOTO) != 0 && photoDownloadQueue.isEmpty()) {
MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_TYPE_PHOTO);
}
if ((mask & AUTODOWNLOAD_MASK_AUDIO) != 0 && (downloadMask & AUTODOWNLOAD_MASK_AUDIO) != 0 && audioDownloadQueue.isEmpty()) {
MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_MASK_AUDIO);
if ((mask & AUTODOWNLOAD_TYPE_AUDIO) != 0 && (downloadMask & AUTODOWNLOAD_TYPE_AUDIO) != 0 && audioDownloadQueue.isEmpty()) {
MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_TYPE_AUDIO);
}
if ((mask & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0 && (downloadMask & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0 && videoMessageDownloadQueue.isEmpty()) {
MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_MASK_VIDEOMESSAGE);
if ((mask & AUTODOWNLOAD_TYPE_VIDEO) != 0 && (downloadMask & AUTODOWNLOAD_TYPE_VIDEO) != 0 && videoDownloadQueue.isEmpty()) {
MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_TYPE_VIDEO);
}
if ((mask & AUTODOWNLOAD_MASK_VIDEO) != 0 && (downloadMask & AUTODOWNLOAD_MASK_VIDEO) != 0 && videoDownloadQueue.isEmpty()) {
MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_MASK_VIDEO);
}
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);
if ((mask & AUTODOWNLOAD_TYPE_DOCUMENT) != 0 && (downloadMask & AUTODOWNLOAD_TYPE_DOCUMENT) != 0 && documentDownloadQueue.isEmpty()) {
MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_TYPE_DOCUMENT);
}
}
@ -524,40 +763,25 @@ public class DownloadController implements NotificationCenter.NotificationCenter
if (state == 0 || state == 2) {
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);
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);
if (audioDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_AUDIO);
newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_AUDIO);
}
} else if (downloadObject.type == AUTODOWNLOAD_MASK_VIDEOMESSAGE) {
videoMessageDownloadQueue.remove(downloadObject);
if (videoMessageDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_VIDEOMESSAGE);
}
} else if (downloadObject.type == AUTODOWNLOAD_MASK_VIDEO) {
} else if (downloadObject.type == AUTODOWNLOAD_TYPE_VIDEO) {
videoDownloadQueue.remove(downloadObject);
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);
if (documentDownloadQueue.isEmpty()) {
newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_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);
newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_DOCUMENT);
}
}
}

View File

@ -9,6 +9,7 @@
package org.telegram.messenger;
import android.util.SparseArray;
import android.util.SparseIntArray;
import org.telegram.tgnet.ConnectionsManager;
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 stateDownloading = 1;
@ -58,6 +71,27 @@ public class FileLoadOperation {
private final static int bigFileSizeFrom = 1024 * 1024;
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 volatile ArrayList<Range> notLoadedBytesRangesCopy;
private ArrayList<Range> notRequestedBytesRanges;
@ -177,7 +211,7 @@ public class FileLoadOperation {
webLocation = webDocument.location;
totalBytesCount = webDocument.size;
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/")) {
currentType = ConnectionsManager.FileTypePhoto;
} else if (webDocument.mime_type.equals("audio/ogg")) {
@ -212,6 +246,12 @@ public class FileLoadOperation {
}
initialDatacenterId = datacenterId = documentLocation.dc_id;
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;
if (key != null) {
@ -230,27 +270,13 @@ public class FileLoadOperation {
}
if ("audio/ogg".equals(documentLocation.mime_type)) {
currentType = ConnectionsManager.FileTypeAudio;
} else if ("video/mp4".equals(documentLocation.mime_type)) {
} else if (FileLoader.isVideoMimeType(documentLocation.mime_type)) {
currentType = ConnectionsManager.FileTypeVideo;
} else {
currentType = ConnectionsManager.FileTypeFile;
}
if (ext.length() <= 1) {
if (documentLocation.mime_type != null) {
switch (documentLocation.mime_type) {
case "video/mp4":
ext = ".mp4";
break;
case "audio/ogg":
ext = ".ogg";
break;
default:
ext = "";
break;
}
} else {
ext = "";
}
ext = FileLoader.getExtensionByMimeType(documentLocation.mime_type);
}
} catch (Exception e) {
FileLog.e(e);
@ -292,7 +318,7 @@ public class FileLoadOperation {
}
public boolean wasStarted() {
return started;
return started && !paused;
}
public int getCurrentType() {
@ -364,9 +390,6 @@ public class FileLoadOperation {
filePartsStream.writeInt(count);
for (int a = 0; a < count; 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.end);
}
@ -454,8 +477,8 @@ public class FileLoadOperation {
});
try {
countDownLatch.await();
} catch (Exception e) {
FileLog.e(e);
} catch (Exception ignore) {
}
return result[0];
}
@ -477,7 +500,7 @@ public class FileLoadOperation {
});
}
private void copytNotLoadedRanges() {
private void copyNotLoadedRanges() {
if (notLoadedBytesRanges == null) {
return;
}
@ -488,14 +511,14 @@ public class FileLoadOperation {
if (state != stateDownloading) {
return;
}
Utilities.stageQueue.postRunnable(() -> paused = true);
paused = true;
}
public boolean start() {
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) {
currentDownloadChunkSize = totalBytesCount >= bigFileSizeFrom ? downloadChunkSizeBig : downloadChunkSize;
currentMaxDownloadRequests = totalBytesCount >= bigFileSizeFrom ? maxDownloadRequestsBig : maxDownloadRequests;
@ -511,8 +534,13 @@ public class FileLoadOperation {
streamStartOffset = streamOffset / currentDownloadChunkSize * currentDownloadChunkSize;
streamListeners.add(stream);
if (alreadyStarted) {
//clearOperaion(null);
if (preloadedBytesRanges != null && getDownloadedLengthFromOffsetInternal(notLoadedBytesRanges, streamStartOffset, 1) == 0) {
if (preloadedBytesRanges.get(streamStartOffset) != null) {
nextPartWasPreloaded = true;
}
}
startDownloadRequest();
nextPartWasPreloaded = false;
}
});
} else if (wasPaused && alreadyStarted) {
@ -536,6 +564,7 @@ public class FileLoadOperation {
String fileNameFinal;
String fileNameTemp;
String fileNameParts = null;
String fileNamePreload = null;
String fileNameIv = null;
if (webLocation != null) {
String md5 = Utilities.MD5(webFile.url);
@ -574,6 +603,7 @@ public class FileLoadOperation {
if (notLoadedBytesRanges != null) {
fileNameParts = location.volume_id + "_" + location.local_id + ".pt";
}
fileNamePreload = location.volume_id + "_" + location.local_id + ".preload";
}
} else {
if (datacenterId == 0 || location.id == 0) {
@ -595,6 +625,7 @@ public class FileLoadOperation {
if (notLoadedBytesRanges != null) {
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) {
cacheFileParts = new File(tempPath, fileNameParts);
try {
@ -691,10 +798,15 @@ public class FileLoadOperation {
range = notLoadedBytesRanges.get(a);
downloadedBytes -= (range.end - range.start);
}
requestedBytesCount = downloadedBytes;
}
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) {
@ -714,8 +826,8 @@ public class FileLoadOperation {
requestedBytesCount = downloadedBytes = 0;
}
}
if (downloadedBytes != 0 && totalBytesCount > 0) {
copytNotLoadedRanges();
if (!isPreloadVideoOperation && downloadedBytes != 0 && totalBytesCount > 0) {
copyNotLoadedRanges();
delegate.didChangedLoadProgress(FileLoadOperation.this, Math.min(1.0f, (float) downloadedBytes / (float) totalBytesCount));
}
try {
@ -732,7 +844,7 @@ public class FileLoadOperation {
}
started = true;
Utilities.stageQueue.postRunnable(() -> {
if (totalBytesCount != 0 && downloadedBytes == totalBytesCount) {
if (totalBytesCount != 0 && (isPreloadVideoOperation && preloaded[0] || downloadedBytes == totalBytesCount)) {
try {
onFinishLoadingFile(false);
} catch (Exception e) {
@ -757,6 +869,39 @@ public class FileLoadOperation {
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() {
Utilities.stageQueue.postRunnable(() -> {
if (state == stateFinished || state == stateFailed) {
@ -788,6 +933,19 @@ public class FileLoadOperation {
} catch (Exception 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 {
if (fileReadStream != null) {
try {
@ -847,50 +1005,61 @@ public class FileLoadOperation {
}
state = stateFinished;
cleanup();
if (cacheIvTemp != null) {
cacheIvTemp.delete();
cacheIvTemp = null;
}
if (cacheFileParts != null) {
cacheFileParts.delete();
cacheFileParts = null;
}
if (cacheFileTemp != null) {
boolean renameResult = cacheFileTemp.renameTo(cacheFileFinal);
if (!renameResult) {
if (BuildVars.LOGS_ENABLED) {
FileLog.e("unable to rename temp = " + cacheFileTemp + " to final = " + cacheFileFinal + " retry = " + renameRetryCount);
}
renameRetryCount++;
if (renameRetryCount < 3) {
state = stateDownloading;
Utilities.stageQueue.postRunnable(() -> {
try {
onFinishLoadingFile(increment);
} catch (Exception e) {
onFail(false, 0);
}
}, 200);
return;
}
cacheFileFinal = cacheFileTemp;
if (isPreloadVideoOperation) {
preloadFinished = true;
if (BuildVars.DEBUG_VERSION) {
FileLog.d("finished preloading file to " + cacheFileTemp + " loaded " + totalPreloadedBytes + " of " + totalBytesCount);
}
} else {
if (cacheIvTemp != null) {
cacheIvTemp.delete();
cacheIvTemp = null;
}
if (cacheFileParts != null) {
cacheFileParts.delete();
cacheFileParts = null;
}
if (cacheFilePreload != null) {
cacheFilePreload.delete();
cacheFilePreload = null;
}
if (cacheFileTemp != null) {
boolean renameResult = cacheFileTemp.renameTo(cacheFileFinal);
if (!renameResult) {
if (BuildVars.LOGS_ENABLED) {
FileLog.e("unable to rename temp = " + cacheFileTemp + " to final = " + cacheFileFinal + " retry = " + renameRetryCount);
}
renameRetryCount++;
if (renameRetryCount < 3) {
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);
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) {
@ -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) {
if (requestingCdnOffsets) {
return;
@ -999,85 +1204,134 @@ public class FileLoadOperation {
Utilities.aesCtrDecryption(bytes.buffer, cdnKey, cdnIv, 0, bytes.limit());
}
downloadedBytes += currentBytesSize;
boolean finishedDownloading;
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 (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 (isPreloadVideoOperation) {
preloadStream.writeInt(requestInfo.offset);
preloadStream.writeInt(currentBytesSize);
preloadStreamFileOffset += 8;
FileChannel channel = preloadStream.getChannel();
channel.write(bytes.buffer);
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);
}
}
FileChannel channel = fileOutputStream.getChannel();
channel.write(bytes.buffer);
addPart(notLoadedBytesRanges, requestInfo.offset, requestInfo.offset + currentBytesSize, true);
if (isCdn) {
int cdnCheckPart = requestInfo.offset / cdnChunkCheckSize;
if (preloadedBytesRanges == null) {
preloadedBytesRanges = new SparseArray<>();
}
preloadedBytesRanges.put(requestInfo.offset, new PreloadRange(preloadStreamFileOffset, requestInfo.offset, currentBytesSize));
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;
totalPreloadedBytes += currentBytesSize;
preloadStreamFileOffset += currentBytesSize;
if (moovFound == 0) {
int offset = findNextPreloadDownloadOffset(nextAtomOffset, requestInfo.offset, bytes);
if (offset < 0) {
offset *= -1;
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) {
int fileOffset = cdnCheckPart * cdnChunkCheckSize;
int availableSize = getDownloadedLengthFromOffsetInternal(notLoadedBytesRanges, fileOffset, cdnChunkCheckSize);
if (availableSize != 0 && (availableSize == cdnChunkCheckSize || totalBytesCount > 0 && availableSize == totalBytesCount - fileOffset || totalBytesCount <= 0 && finishedDownloading)) {
TLRPC.TL_fileHash hash = cdnHashes.get(fileOffset);
if (fileReadStream == null) {
cdnCheckBytes = new byte[cdnChunkCheckSize];
fileReadStream = new RandomAccessFile(cacheFileTemp, "r");
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) {
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);
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());
}
}
if (!checked) {
int fileOffset = cdnCheckPart * cdnChunkCheckSize;
int availableSize = getDownloadedLengthFromOffsetInternal(notLoadedBytesRanges, fileOffset, cdnChunkCheckSize);
if (availableSize != 0 && (availableSize == cdnChunkCheckSize || totalBytesCount > 0 && availableSize == totalBytesCount - fileOffset || totalBytesCount <= 0 && finishedDownloading)) {
TLRPC.TL_fileHash hash = cdnHashes.get(fileOffset);
if (fileReadStream == null) {
cdnCheckBytes = new byte[cdnChunkCheckSize];
fileReadStream = new RandomAccessFile(cacheFileTemp, "r");
}
onFail(false, 0);
cacheFileTemp.delete();
return false;
fileReadStream.seek(fileOffset);
fileReadStream.readFully(cdnCheckBytes, 0, availableSize);
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) {
fiv.seek(0);
fiv.write(iv);
}
if (totalBytesCount > 0 && state == stateDownloading) {
copytNotLoadedRanges();
delegate.didChangedLoadProgress(FileLoadOperation.this, Math.min(1.0f, (float) downloadedBytes / (float) totalBytesCount));
if (fiv != null) {
fiv.seek(0);
fiv.write(iv);
}
if (totalBytesCount > 0 && state == stateDownloading) {
copyNotLoadedRanges();
delegate.didChangedLoadProgress(FileLoadOperation.this, Math.min(1.0f, (float) downloadedBytes / (float) totalBytesCount));
}
}
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;
for (int a = 0; a < requestInfos.size(); a++) {
RequestInfo info = requestInfos.get(a);
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) {
continue;
}
@ -1180,7 +1438,11 @@ public class FileLoadOperation {
requestInfos.clear();
for (int a = 0; a < delayedRequestInfos.size(); 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) {
info.response.disableFree = false;
info.response.freeResources();
@ -1195,7 +1457,9 @@ public class FileLoadOperation {
}
delayedRequestInfos.clear();
requestsCount = 0;
if (notLoadedBytesRanges == null) {
if (!preloadChanged && isPreloadVideoOperation) {
requestedBytesCount = totalPreloadedBytes;
} else if (notLoadedBytesRanges == null) {
requestedBytesCount = downloadedBytes = minOffset;
}
}
@ -1204,7 +1468,7 @@ public class FileLoadOperation {
if (requestingReference) {
return;
}
clearOperaion(requestInfo);
clearOperaion(requestInfo, false);
requestingReference = true;
if (parentObject instanceof MessageObject) {
MessageObject messageObject = (MessageObject) parentObject;
@ -1216,45 +1480,86 @@ public class FileLoadOperation {
}
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;
}
int count = 1;
if (totalBytesCount > 0) {
if (!nextPartWasPreloaded && (!isPreloadVideoOperation || moovFound != 0) && totalBytesCount > 0) {
count = Math.max(0, currentMaxDownloadRequests - requestInfos.size());
}
for (int a = 0; a < count; a++) {
int downloadOffset;
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;
if (isPreloadVideoOperation) {
if (moovFound != 0 && preloadNotRequestedBytesCount <= 0) {
return;
}
if (nextPreloadDownloadOffset == -1) {
downloadOffset = 0;
boolean found = false;
int tries = preloadMaxBytes / currentDownloadChunkSize + 2;
while (tries != 0) {
if (requestedPreloadedBytesRanges.get(downloadOffset, 0) == 0) {
found = true;
break;
}
if (streamStartOffset < range.start && range.start < minStreamStart) {
minStreamStart = range.start;
downloadOffset += currentDownloadChunkSize;
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 {
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 {
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);
}
@ -1294,6 +1599,28 @@ public class FileLoadOperation {
final RequestInfo requestInfo = new RequestInfo();
requestInfos.add(requestInfo);
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) -> {
if (!requestInfos.contains(requestInfo)) {
return;
@ -1305,7 +1632,7 @@ public class FileLoadOperation {
} else if (request instanceof TLRPC.TL_upload_getCdnFile) {
if (error.text.equals("FILE_TOKEN_INVALID")) {
isCdn = false;
clearOperaion(requestInfo);
clearOperaion(requestInfo, false);
startDownloadRequest();
return;
}
@ -1337,12 +1664,12 @@ public class FileLoadOperation {
cdnIv = res.encryption_iv;
cdnKey = res.encryption_key;
cdnToken = res.file_token;
clearOperaion(requestInfo);
clearOperaion(requestInfo, false);
startDownloadRequest();
}
} else if (response instanceof TLRPC.TL_upload_cdnFileReuploadNeeded) {
if (!reuploadingCdn) {
clearOperaion(requestInfo);
clearOperaion(requestInfo, false);
reuploadingCdn = true;
TLRPC.TL_upload_cdnFileReuploadNeeded res = (TLRPC.TL_upload_cdnFileReuploadNeeded) response;
TLRPC.TL_upload_reuploadCdnFile req = new TLRPC.TL_upload_reuploadCdnFile();
@ -1365,7 +1692,7 @@ public class FileLoadOperation {
} else {
if (error1.text.equals("FILE_TOKEN_INVALID") || error1.text.equals("REQUEST_TOKEN_INVALID")) {
isCdn = false;
clearOperaion(requestInfo);
clearOperaion(requestInfo, false);
startDownloadRequest();
} else {
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 HashMap<String, Long> uploadSizes = new HashMap<>();
private HashMap<String, Boolean> loadingVideos = new HashMap<>();
private static SparseArray<File> mediaDirs = null;
private FileLoaderDelegate delegate = null;
@ -118,6 +120,61 @@ public class FileLoader {
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) {
fileLoaderQueue.postRunnable(() -> {
FileUploadOperation operation;
@ -440,22 +497,22 @@ public class FileLoader {
private void pauseCurrentFileLoadOperations(FileLoadOperation newOperation) {
for (int a = 0; a < activeFileLoadOperation.size(); a++) {
FileLoadOperation operation = activeFileLoadOperation.get(a);
if (operation == newOperation) {
if (operation == newOperation || operation.getDatacenterId() != newOperation.getDatacenterId()) {
continue;
}
activeFileLoadOperation.remove(operation);
a--;
operation.pause();
int datacenterId = operation.getDatacenterId();
LinkedList<FileLoadOperation> loadOperationQueue = getLoadOperationQueue(datacenterId);
loadOperationQueue.add(0, operation);
if (operation.wasStarted()) {
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;
if (location != null) {
fileName = getAttachFileName(location, locationExt);
@ -469,14 +526,17 @@ public class FileLoader {
if (fileName == null || fileName.contains("" + Integer.MIN_VALUE)) {
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);
}
FileLoadOperation operation;
operation = loadOperationPaths.get(fileName);
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();
LinkedList<FileLoadOperation> audioLoadOperationQueue = getAudioLoadOperationQueue(datacenterId);
@ -494,9 +554,9 @@ public class FileLoader {
}
if (downloadQueue != null) {
int index = downloadQueue.indexOf(operation);
if (index > 0) {
if (index >= 0) {
downloadQueue.remove(index);
if (streamOffset != 0) {
if (stream != null) {
if (downloadQueue == audioLoadOperationQueue) {
if (operation.start(stream, streamOffset)) {
currentAudioLoadOperationsCount.put(datacenterId, currentAudioLoadOperationsCount.get(datacenterId) + 1);
@ -564,21 +624,29 @@ public class FileLoader {
type = MEDIA_DIR_DOCUMENT;
}
}
if (cacheType == 0) {
if (cacheType == 0 || cacheType == 10) {
storeDir = getDirectory(type);
} else if (cacheType == 2) {
operation.setEncryptFile(true);
}
operation.setPaths(currentAccount, storeDir, tempDir);
if (cacheType == 10) {
operation.setIsPreloadVideoOperation(true);
}
final String finalFileName = fileName;
final int finalType = type;
FileLoadOperation.FileLoadOperationDelegate fileLoadOperationDelegate = new FileLoadOperation.FileLoadOperationDelegate() {
@Override
public void didFinishLoadingFile(FileLoadOperation operation, File finalFile) {
loadOperationPathsUI.remove(finalFileName);
if (delegate != null) {
delegate.fileDidLoaded(finalFileName, finalFile, finalType);
if (!operation.isPreloadVideoOperation() && operation.isPreloadFinished()) {
return;
}
if (!operation.isPreloadVideoOperation()) {
loadOperationPathsUI.remove(finalFileName);
if (delegate != null) {
delegate.fileDidLoaded(finalFileName, finalFile, finalType);
}
}
checkDownloadQueue(operation.getDatacenterId(), document, webDocument, location, finalFileName);
}
@ -612,7 +680,7 @@ public class FileLoader {
if (type == MEDIA_DIR_AUDIO) {
int maxCount = priority > 0 ? 3 : 1;
int count = currentAudioLoadOperationsCount.get(datacenterId);
if (streamOffset != 0 || count < maxCount) {
if (stream != null || count < maxCount) {
if (operation.start(stream, streamOffset)) {
currentAudioLoadOperationsCount.put(datacenterId, count + 1);
}
@ -622,7 +690,7 @@ public class FileLoader {
} else if (location != null || MessageObject.isImageWebDocument(webDocument)) {
int maxCount = priority > 0 ? 6 : 2;
int count = currentPhotoLoadOperationsCount.get(datacenterId);
if (streamOffset != 0 || count < maxCount) {
if (stream != null || count < maxCount) {
if (operation.start(stream, streamOffset)) {
currentPhotoLoadOperationsCount.put(datacenterId, count + 1);
}
@ -632,7 +700,7 @@ public class FileLoader {
} else {
int maxCount = priority > 0 ? 3 : 1;
int count = currentLoadOperationsCount.get(datacenterId);
if (streamOffset != 0 || count < maxCount) {
if (stream != null || count < maxCount) {
if (operation.start(stream, streamOffset)) {
currentLoadOperationsCount.put(datacenterId, count + 1);
activeFileLoadOperation.add(operation);
@ -675,13 +743,13 @@ public class FileLoader {
} else {
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);
}
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 FileLoadOperation[] result = new FileLoadOperation[1];
fileLoaderQueue.postRunnable(() -> {
@ -826,7 +894,7 @@ public class FileLoader {
} else if (message.media instanceof TLRPC.TL_messageMediaInvoice) {
TLRPC.WebDocument document = ((TLRPC.TL_messageMediaInvoice) message.media).photo;
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 : "";
}
public static String getExtensionByMime(String mime) {
public static String getMimeTypePart(String mime) {
int index;
if ((index = mime.lastIndexOf('/')) != -1) {
return mime.substring(index + 1);
@ -1017,6 +1085,20 @@ public class FileLoader {
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() {
return ApplicationLoader.applicationContext.getCacheDir();
}
@ -1056,21 +1138,7 @@ public class FileLoader {
}
}
if (docExt.length() <= 1) {
if (document.mime_type != null) {
switch (document.mime_type) {
case "video/mp4":
docExt = ".mp4";
break;
case "audio/ogg":
docExt = ".ogg";
break;
default:
docExt = "";
break;
}
} else {
docExt = "";
}
docExt = getExtensionByMimeType(document.mime_type);
}
if (docExt.length() > 1) {
return document.dc_id + "_" + document.id + docExt;
@ -1085,7 +1153,7 @@ public class FileLoader {
return secureFile.dc_id + "_" + secureFile.id + ".jpg";
} else if (attach instanceof WebFile) {
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) {
TLRPC.PhotoSize photo = (TLRPC.PhotoSize) attach;
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) {
MessageObject messageObject = (MessageObject) parentObject;
int channelId = messageObject.getChannelId();
return "message" + messageObject.getId() + "_" + channelId;
return "message" + messageObject.getRealId() + "_" + channelId;
} else if (parentObject instanceof TLRPC.Message) {
TLRPC.Message message = (TLRPC.Message) parentObject;
int channelId = message.to_id != null ? message.to_id.channel_id : 0;
@ -200,7 +200,7 @@ public class FileRefController {
}
if (parentObject instanceof MessageObject) {
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;
}
}
@ -266,11 +266,11 @@ public class FileRefController {
if (channelId != 0) {
TLRPC.TL_channels_getMessages req = new TLRPC.TL_channels_getMessages();
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));
} else {
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));
}
} else if (parentObject instanceof TLRPC.TL_wallPaper) {
@ -872,6 +872,6 @@ public class FileRefController {
}
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.util.concurrent.CountDownLatch;
public class FileStreamLoadOperation extends BaseDataSource {
public class FileStreamLoadOperation extends BaseDataSource implements FileLoadOperationStream {
private FileLoadOperation loadOperation;
@ -76,8 +76,10 @@ public class FileStreamLoadOperation extends BaseDataSource {
}
opened = true;
transferStarted(dataSpec);
file = new RandomAccessFile(loadOperation.getCurrentFile(), "r");
file.seek(currentOffset);
if (loadOperation != null) {
file = new RandomAccessFile(loadOperation.getCurrentFile(), "r");
file.seek(currentOffset);
}
return bytesRemaining;
}
@ -96,9 +98,8 @@ public class FileStreamLoadOperation extends BaseDataSource {
while (availableLength == 0) {
availableLength = loadOperation.getDownloadedLengthFromOffset(currentOffset, readLength);
if (availableLength == 0) {
if (loadOperation.isPaused()) {
FileLoader.getInstance(currentAccount).loadStreamFile(this, document, parentObject, currentOffset);
}
FileLog.d("not found bytes " + offset);
FileLoader.getInstance(currentAccount).loadStreamFile(this, document, parentObject, currentOffset);
countDownLatch = new CountDownLatch(1);
countDownLatch.await();
}
@ -142,7 +143,8 @@ public class FileStreamLoadOperation extends BaseDataSource {
}
}
protected void newDataAvailable() {
@Override
public void newDataAvailable() {
if (countDownLatch != null) {
countDownLatch.countDown();
}

View File

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

View File

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

View File

@ -366,6 +366,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
private float seekToProgressPending;
private long lastProgress = 0;
private MessageObject playingMessageObject;
private MessageObject goingToShowMessageObject;
private Timer progressTimer = null;
private final Object progressTimerSync = new Object();
private ArrayList<MessageObject> playlist = new ArrayList<>();
@ -376,6 +377,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
private boolean playMusicAgain;
private AudioInfo audioInfo;
private VideoPlayer videoPlayer;
private boolean playerWasReady;
private TextureView currentTextureView;
private PipRoundVideoView pipRoundVideoView;
private int pipSwitchingState;
@ -389,6 +391,16 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
private float currentAspectRatioFrameLayoutRatio;
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 TLRPC.TL_document recordingAudio;
private int recordingCurrentAccount;
@ -869,11 +881,11 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (videoPlayer != null) {
duration = videoPlayer.getDuration();
progress = videoPlayer.getCurrentPosition();
bufferedValue = videoPlayer.getBufferedPosition() / (float) duration;
value = duration >= 0 ? progress / (float) duration : 0.0f;
if (progress < 0) {
if (duration == C.TIME_UNSET || progress == C.TIME_UNSET || progress < 0 || duration <= 0) {
return;
}
bufferedValue = videoPlayer.getBufferedPosition() / (float) duration;
value = duration >= 0 ? progress / (float) duration : 0.0f;
if (value >= 1) {
return;
}
@ -1420,7 +1432,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (!paused) {
videoPlayer.play();
} else {
videoPlayer.pause();
pauseMessage(playingMessageObject);
}
} else {
boolean post = audioPlayer != null;
@ -1519,13 +1531,13 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
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) {
try {
audioPlayer.releasePlayer();
audioPlayer.releasePlayer(true);
} catch (Exception e) {
FileLog.e(e);
}
@ -1534,14 +1546,31 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
currentAspectRatioFrameLayout = null;
currentTextureViewContainer = null;
currentAspectRatioFrameLayoutReady = false;
isDrawingWasReady = false;
currentTextureView = null;
videoPlayer.releasePlayer();
videoPlayer = null;
goingToShowMessageObject = 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 {
baseActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} catch (Exception e) {
FileLog.e(e);
}
if (playingMessageObject != null && !transferPlayerToPhotoViewer) {
AndroidUtilities.cancelRunOnUIThread(setLoadingRunnable);
FileLoader.getInstance(playingMessageObject.currentAccount).removeLoadingVideo(playingMessageObject.getDocument(), true, false);
}
}
stopProgressTimer();
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) {
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) {
try {
audioPlayer.releasePlayer();
audioPlayer.releasePlayer(true);
} catch (Exception e) {
FileLog.e(e);
}
@ -1764,13 +1801,15 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
currentTextureViewContainer = null;
currentAspectRatioFrameLayoutReady = false;
currentTextureView = null;
videoPlayer.releasePlayer();
videoPlayer.releasePlayer(true);
videoPlayer = null;
try {
baseActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} catch (Exception e) {
FileLog.e(e);
}
AndroidUtilities.cancelRunOnUIThread(setLoadingRunnable);
FileLoader.getInstance(playingMessageObject.currentAccount).removeLoadingVideo(playingMessageObject.getDocument(), true, false);
}
stopProgressTimer();
lastProgress = 0;
@ -1848,7 +1887,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
private void checkIsNextMusicFileDownloaded(int currentAccount) {
if ((DownloadController.getInstance(currentAccount).getCurrentDownloadMask() & DownloadController.AUTODOWNLOAD_MASK_MUSIC) == 0) {
if (!DownloadController.getInstance(currentAccount).canDownloadNextTrack()) {
return;
}
ArrayList<MessageObject> currentPlayList = SharedConfig.shuffleMusic ? shuffledPlaylist : playlist;
@ -1872,9 +1911,6 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
MessageObject nextAudio = currentPlayList.get(nextIndex);
if (!DownloadController.getInstance(currentAccount).canDownloadMedia(nextAudio)) {
return;
}
File file = null;
if (!TextUtils.isEmpty(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) {
return;
}
@ -1988,9 +2024,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (currentAspectRatioFrameLayout != null) {
currentAspectRatioFrameLayout.setAspectRatio(currentAspectRatioFrameLayoutRatio, currentAspectRatioFrameLayoutRotation);
}
if (currentTextureViewContainer.getVisibility() != View.VISIBLE) {
currentTextureViewContainer.setVisibility(View.VISIBLE);
}
//if (currentTextureViewContainer.getVisibility() != 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);
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) {
FileLoader.getInstance(messageObject.currentAccount).loadFile(messageObject.getDocument(), messageObject, 0, 0);
downloadingCurrentMessage = true;
@ -2120,7 +2156,14 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
isDrawingWasReady = 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();
shuffledPlaylist.clear();
videoPlayer = new VideoPlayer();
@ -2144,12 +2187,29 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
}
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;
if (currentTextureViewContainer != null && currentTextureViewContainer.getVisibility() != View.VISIBLE) {
currentTextureViewContainer.setVisibility(View.VISIBLE);
} else if (playbackState == ExoPlayer.STATE_BUFFERING) {
if (playWhenReady && playingMessageObject != null && playingMessageObject.isVideo()) {
if (playerWasReady) {
setLoadingRunnable.run();
} else {
AndroidUtilities.runOnUIThread(setLoadingRunnable, 1000);
}
}
} 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()) {
isDrawingWasReady = true;
currentAspectRatioFrameLayout.setDrawingReady(true);
if (currentTextureViewContainer != null && currentTextureViewContainer.getVisibility() != View.VISIBLE) {
currentTextureViewContainer.setVisibility(View.VISIBLE);
}
currentTextureViewContainer.setTag(1);
//if (currentTextureViewContainer != null && currentTextureViewContainer.getVisibility() != View.VISIBLE) {
// currentTextureViewContainer.setVisibility(View.VISIBLE);
//}
}
}
@ -2223,6 +2284,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
pipSwitchingState = 0;
return true;
} else if (PhotoViewer.hasInstance() && PhotoViewer.getInstance().isInjectingVideoPlayer()) {
PhotoViewer.getInstance().injectVideoPlayerSurface(surfaceTexture);
return true;
}
return false;
}
@ -2248,12 +2312,39 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} else if (currentTextureView != null) {
videoPlayer.setTextureView(currentTextureView);
}
videoPlayer.preparePlayer(Uri.fromFile(cacheFile), "other");
videoPlayer.setStreamType(useFrontSpeaker ? AudioManager.STREAM_VOICE_CALL : AudioManager.STREAM_MUSIC);
if (currentPlaybackSpeed > 1.0f) {
videoPlayer.setPlaybackSpeed(currentPlaybackSpeed);
if (exists) {
if (!messageObject.mediaExists && cacheFile != file) {
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 {
if (pipRoundVideoView != null) {
pipRoundVideoView.close(true);
@ -2268,7 +2359,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (!playlist.isEmpty() && playlist.size() > 1) {
playNextMessageWithoutOrder(true);
} 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)) {
int seekTo = (int) (audioPlayer.getDuration() * seekToProgressPending);
@ -2343,7 +2434,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
FileLog.e(e);
NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.messagePlayingPlayStateChanged, playingMessageObject != null ? playingMessageObject.getId() : 0);
if (audioPlayer != null) {
audioPlayer.releasePlayer();
audioPlayer.releasePlayer(true);
audioPlayer = null;
isPaused = false;
playingMessageObject = null;
@ -2367,11 +2458,15 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (videoPlayer != null) {
try {
if (playingMessageObject.audioProgress != 0) {
long duration = audioPlayer.getDuration();
long duration = videoPlayer.getDuration();
if (duration == C.TIME_UNSET) {
duration = (long) playingMessageObject.getDuration() * 1000;
}
int seekTo = (int) (duration * playingMessageObject.audioProgress);
if (playingMessageObject.audioProgressMs != 0) {
seekTo = playingMessageObject.audioProgressMs;
playingMessageObject.audioProgressMs = 0;
}
videoPlayer.seekTo(seekTo);
}
} catch (Exception e2) {
@ -2380,6 +2475,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.messagePlayingProgressDidChanged, playingMessageObject.getId(), 0);
FileLog.e(e2);
}
videoPlayer.play();
} else if (audioPlayer != null) {
try {
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);
try {
/*if (Build.VERSION.SDK_INT >= 26) {
@ -2416,48 +2512,6 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
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() {
return audioInfo;
}
@ -2529,7 +2583,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
return true;
}
public boolean isRoundVideoDrawingReady() {
public boolean isVideoDrawingReady() {
return currentAspectRatioFrameLayout != null && currentAspectRatioFrameLayout.isDrawingReady();
}
@ -2551,6 +2605,10 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
return false;
}
public boolean isPlayingMessageAndReadyToDraw(MessageObject messageObject) {
return isDrawingWasReady && isPlayingMessage(messageObject);
}
public boolean isMessagePaused() {
return isPaused || downloadingCurrentMessage;
}
@ -3370,7 +3428,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
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;
if (firstWrite) {
videoConvertFirstWrite = false;
@ -3389,7 +3447,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (firstWrite) {
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) {
info.offset = 0;
info.flags = extractor.getSampleFlags();
if (mediaMuxer.writeSampleData(muxerTrackIndex, buffer, info, false)) {
didWriteData(messageObject, file, false, false);
long availableSize = mediaMuxer.writeSampleData(muxerTrackIndex, buffer, info, false);
if (availableSize != 0) {
didWriteData(messageObject, file, false, availableSize, false);
}
} else {
eof = true;
@ -3600,7 +3659,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
boolean isPreviousOk = preferences.getBoolean("isPreviousOk", true);
preferences.edit().putBoolean("isPreviousOk", false).commit();
if (!inputFile.canRead() || !isPreviousOk) {
didWriteData(messageObject, cacheFile, true, true);
didWriteData(messageObject, cacheFile, true, 0, true);
preferences.edit().putBoolean("isPreviousOk", true).commit();
return false;
}
@ -3814,8 +3873,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (info.size > 0 && (endTime < 0 || info.presentationTimeUs < endTime)) {
info.offset = 0;
info.flags = extractor.getSampleFlags();
if (mediaMuxer.writeSampleData(audioTrackIndex, audioBuffer, info, false)) {
didWriteData(messageObject, cacheFile, false, false);
long availableSize = mediaMuxer.writeSampleData(audioTrackIndex, audioBuffer, info, false);
if (availableSize != 0) {
didWriteData(messageObject, cacheFile, false, availableSize, false);
}
}
} else if (index == -1) {
@ -3860,8 +3920,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
if (info.size > 1) {
if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
if (mediaMuxer.writeSampleData(videoTrackIndex, encodedData, info, true)) {
didWriteData(messageObject, cacheFile, false, false);
long availableSize = mediaMuxer.writeSampleData(videoTrackIndex, encodedData, info, true);
if (availableSize != 0) {
didWriteData(messageObject, cacheFile, false, availableSize, false);
}
} else if (videoTrackIndex == -5) {
byte[] csd = new byte[info.size];
@ -4031,11 +4092,11 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
} else {
preferences.edit().putBoolean("isPreviousOk", true).commit();
didWriteData(messageObject, cacheFile, true, true);
didWriteData(messageObject, cacheFile, true, 0, true);
return false;
}
preferences.edit().putBoolean("isPreviousOk", true).commit();
didWriteData(messageObject, cacheFile, true, error);
didWriteData(messageObject, cacheFile, true, 0, error);
return true;
}
}

View File

@ -81,6 +81,7 @@ public class MessageObject {
public String monthKey;
public boolean deleted;
public float audioProgress;
public int audioProgressMs;
public float bufferedProgress;
public float gifState;
public int audioProgressSec;
@ -100,6 +101,8 @@ public class MessageObject {
public boolean useCustomPhoto;
public StringBuilder botButtonsLayout;
public boolean hadAnimationNotReadyLoading;
public boolean cancelEditing;
public CharSequence editingMessage;
@ -422,6 +425,8 @@ public class MessageObject {
float maxAspectRatio = maxSizeWidth / maxSizeHeight;
averageAspectRatio = averageAspectRatio / count;
float minH = AndroidUtilities.dp(100) / maxSizeHeight;
if (!forceCalc && (count == 2 || count == 3 || count == 4)) {
if (count == 2) {
GroupedMessagePosition position1 = posArray.get(0);
@ -484,6 +489,9 @@ public class MessageObject {
int width = maxSizeWidth / 2;
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);
position3.set(1, 1, 1, 1, width, secondHeight, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM);
maxX = 1;
@ -509,6 +517,9 @@ public class MessageObject {
}
h = Math.min(maxSizeHeight - h0, h);
h /= maxSizeHeight;
if (h < minH) {
h = minH;
}
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);
position4.set(2, 2, 1, 1, w2, h, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM);
@ -655,7 +666,7 @@ public class MessageObject {
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++;
}
posToFix.pw += spanLeft;
@ -2107,6 +2118,7 @@ public class MessageObject {
message.media.document = getDocumentWithId(webPage, pageBlockVideo.video_id);
}
message.message = "";
message.realId = getId();
message.id = Utilities.random.nextInt();
message.date = messageOwner.date;
message.to_id = messageOwner.to_id;
@ -2381,7 +2393,7 @@ public class MessageObject {
}
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 height = 0;
boolean round = false;
@ -2401,7 +2413,7 @@ public class MessageObject {
}
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 height = 0;
boolean animated = false;
@ -2422,7 +2434,7 @@ public class MessageObject {
}
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 height = 0;
boolean animated = false;
@ -3390,7 +3402,7 @@ public class MessageObject {
}
int selfUserId = UserConfig.getInstance(currentAccount).getClientUserId();
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;
}
@ -3453,9 +3465,19 @@ public class MessageObject {
return messageOwner.id;
}
public int getRealId() {
return messageOwner.realId != 0 ? messageOwner.realId : messageOwner.id;
}
public static int getMessageSize(TLRPC.Message message) {
if (message.media != null && message.media.document != null) {
return message.media.document.size;
TLRPC.Document document;
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;
}
@ -3570,9 +3592,9 @@ public class MessageObject {
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 false;
}
@ -3733,9 +3755,9 @@ public class MessageObject {
if (isAnimated && (width > 1280 || height > 1280)) {
isAnimated = false;
}
/*if (!isVideo && "video/x-matroska".equals(document.mime_type)) {
if (SharedConfig.streamMkv && !isVideo && "video/x-matroska".equals(document.mime_type)) {
isVideo = true;
}*/
}
return isVideo && !isAnimated;
}
return false;
@ -3744,6 +3766,8 @@ public class MessageObject {
public TLRPC.Document getDocument() {
if (messageOwner.media instanceof TLRPC.TL_messageMediaWebPage) {
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;
}
@ -3780,7 +3804,7 @@ public class MessageObject {
public static boolean isPhoto(TLRPC.Message message) {
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;
}
@ -4068,15 +4092,13 @@ public class MessageObject {
}
public int getDuration() {
TLRPC.Document document;
if (type == 0) {
document = messageOwner.media.webpage.document;
} else {
document = messageOwner.media.document;
}
TLRPC.Document document = getDocument();
if (document == null) {
return 0;
}
if (audioPlayerDuration > 0) {
return audioPlayerDuration;
}
for (int a = 0; a < document.attributes.size(); a++) {
TLRPC.DocumentAttribute attribute = document.attributes.get(a);
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.CountDownLatch;
import android.support.v4.app.NotificationManagerCompat;
public class MessagesController implements NotificationCenter.NotificationCenterDelegate {
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> dialogsForward = 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> dialogsUsersOnly = new ArrayList<>();
public ArrayList<TLRPC.TL_dialog> dialogsGroupsOnly = new ArrayList<>();
public int unreadUnmutedDialogs;
public int nextDialogsCacheOffset;
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 boolean isLeftProxyChannel;
private long proxyDialogId;
private String proxyDialogAddress;
private boolean checkingTosUpdate;
private int nextTosCheckTime;
@ -414,6 +418,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter
canRevokePmInbox = mainPreferences.getBoolean("canRevokePmInbox", canRevokePmInbox);
preloadFeaturedStickers = mainPreferences.getBoolean("preloadFeaturedStickers", false);
proxyDialogId = mainPreferences.getLong("proxy_dialog", 0);
proxyDialogAddress = mainPreferences.getString("proxyDialogAddress", null);
nextTosCheckTime = notificationsPreferences.getInt("nextTosCheckTime", 0);
venueSearchBot = mainPreferences.getString("venueSearchBot", "foursquare");
gifSearchBot = mainPreferences.getString("gifSearchBot", "gif");
@ -426,6 +431,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter
public void updateConfig(final TLRPC.TL_config config) {
AndroidUtilities.runOnUIThread(() -> {
DownloadController.getInstance(currentAccount).loadAutoDownloadConfig(false);
LocaleController.getInstance().loadRemoteLanguages(currentAccount);
maxMegagroupCount = config.megagroup_size_max;
maxGroupCount = config.chat_size_max;
@ -790,7 +796,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter
editor = emojiPreferences.edit();
editor.putLong("lastGifLoadTime", 0).putLong("lastStickersLoadTime", 0).putLong("lastStickersLoadTimeMask", 0).putLong("lastStickersLoadTimeFavs", 0).commit();
editor = mainPreferences.edit();
editor.remove("gifhint").remove("dcDomainName").remove("webFileDatacenterId").commit();
editor.remove("gifhint").remove("soundHint").remove("dcDomainName").remove("webFileDatacenterId").commit();
reloadingWebpages.clear();
reloadingWebpagesPending.clear();
@ -808,8 +814,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter
pollsToCheckSize = 0;
dialogsServerOnly.clear();
dialogsForward.clear();
dialogsGroupsOnly.clear();
dialogsCanAddUsers.clear();
dialogsChannelsOnly.clear();
dialogsGroupsOnly.clear();
dialogsUsersOnly.clear();
dialogMessagesByIds.clear();
dialogMessagesByRandomIds.clear();
@ -2507,8 +2514,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter
needShortPollOnlines.delete(-(int) did);
});
}
dialogsGroupsOnly.remove(dialog);
dialogsCanAddUsers.remove(dialog);
dialogsChannelsOnly.remove(dialog);
dialogsGroupsOnly.remove(dialog);
dialogsUsersOnly.remove(dialog);
dialogsForward.remove(dialog);
dialogs_dict.remove(did);
@ -3003,10 +3011,18 @@ public class MessagesController implements NotificationCenter.NotificationCenter
if (!reset && nextProxyInfoCheckTime > ConnectionsManager.getInstance(currentAccount).getCurrentTime() || checkingProxyInfo) {
return;
}
if (checkingProxyInfoRequestId != 0) {
ConnectionsManager.getInstance(currentAccount).cancelRequest(checkingProxyInfoRequestId, true);
checkingProxyInfoRequestId = 0;
}
SharedPreferences preferences = getGlobalMainSettings();
boolean enabled = preferences.getBoolean("proxy_enabled", false);
String proxyAddress = preferences.getString("proxy_ip", "");
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)) {
checkingProxyInfo = true;
TLRPC.TL_help_getProxyData req = new TLRPC.TL_help_getProxyData();
@ -3049,7 +3065,8 @@ public class MessagesController implements NotificationCenter.NotificationCenter
}
}
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;
if (!noDialog) {
AndroidUtilities.runOnUIThread(() -> {
@ -3180,7 +3197,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter
}
if (noDialog) {
proxyDialogId = 0;
getGlobalMainSettings().edit().putLong("proxy_dialog", proxyDialogId).commit();
getGlobalMainSettings().edit().putLong("proxy_dialog", proxyDialogId).remove("proxyDialogAddress").commit();
checkingProxyInfoRequestId = 0;
checkingProxyInfo = false;
AndroidUtilities.runOnUIThread(() -> {
@ -3200,13 +3217,19 @@ public class MessagesController implements NotificationCenter.NotificationCenter
}
});
} else {
removeCurrent = 2;
}
if (removeCurrent != 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;
checkingProxyInfo = false;
if (checkingProxyInfoRequestId != 0) {
ConnectionsManager.getInstance(currentAccount).cancelRequest(checkingProxyInfoRequestId, true);
checkingProxyInfoRequestId = 0;
if (removeCurrent == 2) {
checkingProxyInfo = false;
if (checkingProxyInfoRequestId != 0) {
ConnectionsManager.getInstance(currentAccount).cancelRequest(checkingProxyInfoRequestId, true);
checkingProxyInfoRequestId = 0;
}
}
AndroidUtilities.runOnUIThread(() -> {
if (proxyDialog != null) {
@ -6462,18 +6485,18 @@ public class MessagesController implements NotificationCenter.NotificationCenter
getChannelDifference(channelId, 0, 0, null);
}
public static boolean isSupportId(int id) {
return id / 1000 == 777 || id == 333000 ||
id == 4240000 || id == 4240000 || id == 4244000 ||
id == 4245000 || id == 4246000 || id == 410000 ||
id == 420000 || id == 431000 || id == 431415000 ||
id == 434000 || id == 4243000 || id == 439000 ||
id == 449000 || id == 450000 || id == 452000 ||
id == 454000 || id == 4254000 || id == 455000 ||
id == 460000 || id == 470000 || id == 479000 ||
id == 796000 || id == 482000 || id == 490000 ||
id == 496000 || id == 497000 || id == 498000 ||
id == 4298000;
public static boolean isSupportUser(TLRPC.User user) {
return user != null && (user.support || user.id / 1000 == 777 || user.id == 333000 ||
user.id == 4240000 || user.id == 4240000 || user.id == 4244000 ||
user.id == 4245000 || user.id == 4246000 || user.id == 410000 ||
user.id == 420000 || user.id == 431000 || user.id == 431415000 ||
user.id == 434000 || user.id == 4243000 || user.id == 439000 ||
user.id == 449000 || user.id == 450000 || user.id == 452000 ||
user.id == 454000 || user.id == 4254000 || user.id == 455000 ||
user.id == 460000 || user.id == 470000 || user.id == 479000 ||
user.id == 796000 || user.id == 482000 || user.id == 490000 ||
user.id == 496000 || user.id == 497000 || user.id == 498000 ||
user.id == 4298000);
}
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;
}
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);
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) {
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("account", currentAccount);
try {
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ApplicationLoader.applicationContext.startForegroundService(intent);
}else{
} else {
ApplicationLoader.applicationContext.startService(intent);
}
} catch (Throwable e) {
@ -9738,8 +9766,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter
if (dialog != null) {
dialogs.remove(dialog);
dialogsServerOnly.remove(dialog);
dialogsGroupsOnly.remove(dialog);
dialogsCanAddUsers.remove(dialog);
dialogsChannelsOnly.remove(dialog);
dialogsGroupsOnly.remove(dialog);
dialogsUsersOnly.remove(dialog);
dialogsForward.remove(dialog);
dialogs_dict.remove(dialog.id);
@ -9858,8 +9887,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter
public void sortDialogs(SparseArray<TLRPC.Chat> chatsDict) {
dialogsServerOnly.clear();
dialogsGroupsOnly.clear();
dialogsCanAddUsers.clear();
dialogsChannelsOnly.clear();
dialogsGroupsOnly.clear();
dialogsUsersOnly.clear();
dialogsForward.clear();
unreadUnmutedDialogs = 0;
@ -9888,10 +9918,10 @@ public class MessagesController implements NotificationCenter.NotificationCenter
if (DialogObject.isChannel(d)) {
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)) {
dialogsGroupsOnly.add(d);
dialogsCanAddUsers.add(d);
}
if (chat != null && chat.megagroup) {
dialogsGroupsOnly.add(d);
} else {
dialogsChannelsOnly.add(d);
}
@ -9904,6 +9934,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter
continue;
}
}
dialogsCanAddUsers.add(d);
dialogsGroupsOnly.add(d);
} else if (lower_id > 0) {
dialogsUsersOnly.add(d);

View File

@ -5500,47 +5500,41 @@ public class MessagesStorage {
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) {
int type = 0;
long id = 0;
TLRPC.MessageMedia object = null;
if (MessageObject.isVoiceMessage(message)) {
id = message.media.document.id;
type = DownloadController.AUTODOWNLOAD_MASK_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;
type = DownloadController.AUTODOWNLOAD_TYPE_AUDIO;
object = new TLRPC.TL_messageMediaDocument();
object.document = message.media.document;
object.flags |= 1;
} else if (MessageObject.isStickerMessage(message)) {
id = message.media.document.id;
type = DownloadController.AUTODOWNLOAD_MASK_PHOTO;
type = DownloadController.AUTODOWNLOAD_TYPE_PHOTO;
object = new TLRPC.TL_messageMediaDocument();
object.document = message.media.document;
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());
if (photoSize != null) {
id = message.media.photo.id;
type = DownloadController.AUTODOWNLOAD_MASK_PHOTO;
type = DownloadController.AUTODOWNLOAD_TYPE_PHOTO;
object = new TLRPC.TL_messageMediaPhoto();
object.photo = message.media.photo;
object.flags |= 1;
}
} else if (MessageObject.isVideoMessage(message)) {
} else if (MessageObject.isVideoMessage(message) || MessageObject.isRoundVideoMessage(message)) {
id = message.media.document.id;
type = DownloadController.AUTODOWNLOAD_MASK_VIDEO;
type = DownloadController.AUTODOWNLOAD_TYPE_VIDEO;
object = new TLRPC.TL_messageMediaDocument();
object.document = message.media.document;
object.flags |= 1;
} else if (message.media instanceof TLRPC.TL_messageMediaDocument && !MessageObject.isMusicMessage(message) && !MessageObject.isGifDocument(message.media.document)) {
id = message.media.document.id;
type = DownloadController.AUTODOWNLOAD_MASK_DOCUMENT;
type = DownloadController.AUTODOWNLOAD_TYPE_DOCUMENT;
object = new TLRPC.TL_messageMediaDocument();
object.document = message.media.document;
object.flags |= 1;

View File

@ -24,7 +24,7 @@ import java.util.zip.ZipFile;
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_SO_NAME = "lib" + LIB_NAME + ".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 didUpdatePollResults = totalEvents++;
public static final int chatOnlineCountDidLoad = totalEvents++;
public static final int videoLoadingStateChanged = totalEvents++;
public static final int httpFileDidLoad = totalEvents++;
public static final int httpFileDidFailedLoad = totalEvents++;
@ -110,6 +111,7 @@ public class NotificationCenter {
public static final int messagePlayingPlayStateChanged = totalEvents++;
public static final int messagePlayingDidStart = totalEvents++;
public static final int messagePlayingDidSeek = totalEvents++;
public static final int messagePlayingGoingToStop = totalEvents++;
public static final int recordProgressChanged = totalEvents++;
public static final int recordStarted = 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) {
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);
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);
}
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) {
document.thumbs.add(thumb);
document.flags |= 1;
@ -5174,7 +5174,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
thumb = ThumbnailUtils.createVideoThumbnail(path, MediaStore.Video.Thumbnails.MINI_KIND);
}
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) {
try {
int side = isEncrypted ? 90 : 320;
int side = isEncrypted || info.ttl != 0 ? 90 : 320;
Bitmap bitmap;
if (thumbFile.getAbsolutePath().endsWith("mp4")) {
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);
}
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) {
document.thumbs.add(thumb);
document.flags |= 1;
@ -5492,8 +5492,8 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
if (thumb == null) {
thumb = ThumbnailUtils.createVideoThumbnail(info.path, MediaStore.Video.Thumbnails.MINI_KIND);
}
int side = isEncrypted ? 90 : 320;
TLRPC.PhotoSize size = ImageLoader.scaleAndSaveImage(thumb, side, side, 55, isEncrypted);
int side = isEncrypted || info.ttl != 0 ? 90 : 320;
TLRPC.PhotoSize size = ImageLoader.scaleAndSaveImage(thumb, side, side, side > 90 ? 70 : 55, isEncrypted);
if (thumb != null && size != null) {
thumb = null;
}
@ -5707,7 +5707,11 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
if (AndroidUtilities.isTablet()) {
maxPhotoWidth = photoWidth = (int) (AndroidUtilities.getMinTabletSide() * 0.7f);
} 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);
@ -5988,8 +5992,36 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
for (int a = 0; a < sizes.length; a++) {
sampleSizes += sizes[a];
}
videoDuration = (float) mediaHeaderBox.getDuration() / (float) mediaHeaderBox.getTimescale();
trackBitrate = (int) (sampleSizes * 8 / videoDuration);
if (videoDuration == 0) {
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) {
FileLog.e(e);
}
@ -6149,6 +6181,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
videoEditedInfo.estimatedSize = (int) (audioFramesSize + videoFramesSize);
videoEditedInfo.estimatedSize += videoEditedInfo.estimatedSize / (32 * 1024) * 16;
}
if (videoEditedInfo.estimatedSize == 0) {
videoEditedInfo.estimatedSize = 1;
}
return videoEditedInfo;
}
@ -6200,8 +6235,8 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
if (thumb == null) {
thumb = ThumbnailUtils.createVideoThumbnail(videoPath, MediaStore.Video.Thumbnails.MINI_KIND);
}
int side = isEncrypted ? 90 : 320;
TLRPC.PhotoSize size = ImageLoader.scaleAndSaveImage(thumb, side, side, 55, isEncrypted);
int side = isEncrypted || ttl != 0 ? 90 : 320;
TLRPC.PhotoSize size = ImageLoader.scaleAndSaveImage(thumb, side, side, side > 90 ? 70 : 55, isEncrypted);
if (thumb != null && size != null) {
if (isRound) {
if (isEncrypted) {

View File

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

View File

@ -66,6 +66,7 @@ public class UserConfig {
public int pendingAppUpdateBuildVersion;
public long pendingAppUpdateInstallTime;
public long lastUpdateCheckTime;
public long autoDownloadConfigLoadTime;
public volatile byte[] savedPasswordHash;
public volatile byte[] savedSaltedPassword;
@ -145,6 +146,7 @@ public class UserConfig {
editor.putBoolean("hasSecureData", hasSecureData);
editor.putBoolean("notificationsSettingsLoaded3", notificationsSettingsLoaded);
editor.putBoolean("notificationsSignUpSettingsLoaded", notificationsSignUpSettingsLoaded);
editor.putLong("autoDownloadConfigLoadTime", autoDownloadConfigLoadTime);
editor.putInt("3migrateOffsetId", migrateOffsetId);
if (migrateOffsetId != -1) {
@ -292,6 +294,7 @@ public class UserConfig {
hasSecureData = preferences.getBoolean("hasSecureData", false);
notificationsSettingsLoaded = preferences.getBoolean("notificationsSettingsLoaded3", false);
notificationsSignUpSettingsLoaded = preferences.getBoolean("notificationsSignUpSettingsLoaded", false);
autoDownloadConfigLoadTime = preferences.getLong("autoDownloadConfigLoadTime", 0);
try {
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 globalQueue = new DispatchQueue("globalQueue");
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();
@ -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);
}
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) {
if (md5 == null) {
return null;

View File

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

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