|
@ -17,7 +17,7 @@ configurations.all {
|
|||
dependencies {
|
||||
implementation 'androidx.core:core:1.6.0'
|
||||
implementation 'androidx.palette:palette:1.0.0'
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.2'
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.3'
|
||||
implementation 'androidx.dynamicanimation:dynamicanimation:1.0.0'
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
implementation "androidx.sharetarget:sharetarget:1.1.0"
|
||||
|
@ -29,7 +29,7 @@ dependencies {
|
|||
implementation 'com.google.firebase:firebase-datatransport:18.0.1'
|
||||
implementation 'com.google.firebase:firebase-appindexing:20.0.0'
|
||||
implementation 'com.google.android.gms:play-services-maps:17.0.1'
|
||||
implementation 'com.google.android.gms:play-services-auth:19.0.0'
|
||||
implementation 'com.google.android.gms:play-services-auth:19.2.0'
|
||||
implementation 'com.google.android.gms:play-services-vision:16.2.0'
|
||||
implementation 'com.google.android.gms:play-services-wearable:17.1.0'
|
||||
implementation 'com.google.android.gms:play-services-location:18.0.0'
|
||||
|
@ -299,7 +299,7 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
defaultConfig.versionCode = 2387
|
||||
defaultConfig.versionCode = 2390
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.all { output ->
|
||||
|
@ -318,7 +318,7 @@ android {
|
|||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 29
|
||||
versionName "7.9.1"
|
||||
versionName "7.9.3"
|
||||
|
||||
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
|
||||
|
||||
|
|
|
@ -495,6 +495,15 @@ void ConnectionSocket::openConnectionInternal(bool ipv6) {
|
|||
if (setsockopt(socketFd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(int))) {
|
||||
if (LOGS_ENABLED) DEBUG_E("connection(%p) set TCP_NODELAY failed", this);
|
||||
}
|
||||
#ifdef DEBUG_VERSION
|
||||
int size = 4 * 1024 * 1024;
|
||||
if (setsockopt(socketFd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(int))) {
|
||||
if (LOGS_ENABLED) DEBUG_E("connection(%p) set SO_SNDBUF failed", this);
|
||||
}
|
||||
if (setsockopt(socketFd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(int))) {
|
||||
if (LOGS_ENABLED) DEBUG_E("connection(%p) set SO_RCVBUF failed", this);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fcntl(socketFd, F_SETFL, O_NONBLOCK) == -1) {
|
||||
if (LOGS_ENABLED) DEBUG_E("connection(%p) set O_NONBLOCK failed", this);
|
||||
|
|
|
@ -924,13 +924,13 @@ JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_setupOutg
|
|||
std::shared_ptr<tgcalls::VideoCaptureInterface> capturer;
|
||||
if (type == 0 || type == 1) {
|
||||
if (instance->_videoCapture == nullptr) {
|
||||
instance->_videoCapture = tgcalls::VideoCaptureInterface::Create(StaticThreads::getThreads(), type == 1 ? "front" : "back", false, instance->_platformContext);
|
||||
instance->_videoCapture = tgcalls::VideoCaptureInterface::Create(StaticThreads::getThreads(), type == 1 ? "front" : "back", false, std::make_shared<AndroidContext>(env, nullptr, false));
|
||||
}
|
||||
capturer = instance->_videoCapture;
|
||||
instance->useScreencast = false;
|
||||
} else {
|
||||
if (instance->_screenVideoCapture == nullptr) {
|
||||
instance->_screenVideoCapture = tgcalls::VideoCaptureInterface::Create(StaticThreads::getThreads(), "screen", true, instance->_platformContext);
|
||||
instance->_screenVideoCapture = tgcalls::VideoCaptureInterface::Create(StaticThreads::getThreads(), "screen", true, std::make_shared<AndroidContext>(env, nullptr, true));
|
||||
}
|
||||
capturer = instance->_screenVideoCapture;
|
||||
instance->useScreencast = true;
|
||||
|
|
|
@ -636,28 +636,45 @@ void MediaManager::setSendVideo(std::shared_ptr<VideoCaptureInterface> videoCapt
|
|||
const auto wasReceiving = computeIsReceivingVideo();
|
||||
|
||||
if (_videoCapture) {
|
||||
GetVideoCaptureAssumingSameThread(_videoCapture.get())->setStateUpdated(nullptr);
|
||||
GetVideoCaptureAssumingSameThread(_videoCapture.get())->setStateUpdated(nullptr);
|
||||
}
|
||||
_videoCapture = videoCapture;
|
||||
if (_videoCapture) {
|
||||
if (_videoCapture) {
|
||||
_videoCapture->setPreferredAspectRatio(_preferredAspectRatio);
|
||||
_isScreenCapture = _videoCapture->isScreenCapture();
|
||||
|
||||
const auto thread = _thread;
|
||||
const auto weak = std::weak_ptr<MediaManager>(shared_from_this());
|
||||
GetVideoCaptureAssumingSameThread(_videoCapture.get())->setStateUpdated([=](VideoState state) {
|
||||
thread->PostTask(RTC_FROM_HERE, [=] {
|
||||
if (const auto strong = weak.lock()) {
|
||||
strong->setOutgoingVideoState(state);
|
||||
}
|
||||
});
|
||||
});
|
||||
const auto thread = _thread;
|
||||
const auto weak = std::weak_ptr<MediaManager>(shared_from_this());
|
||||
GetVideoCaptureAssumingSameThread(_videoCapture.get())->setStateUpdated([=](VideoState state) {
|
||||
thread->PostTask(RTC_FROM_HERE, [=] {
|
||||
if (const auto strong = weak.lock()) {
|
||||
strong->setOutgoingVideoState(state);
|
||||
}
|
||||
});
|
||||
});
|
||||
setOutgoingVideoState(VideoState::Active);
|
||||
} else {
|
||||
_isScreenCapture = false;
|
||||
|
||||
setOutgoingVideoState(VideoState::Inactive);
|
||||
resetSendingVideo();
|
||||
}
|
||||
|
||||
if (_enableFlexfec) {
|
||||
_videoChannel->RemoveSendStream(_ssrcVideo.outgoing);
|
||||
_videoChannel->RemoveSendStream(_ssrcVideo.fecOutgoing);
|
||||
} else {
|
||||
_videoChannel->RemoveSendStream(_ssrcVideo.outgoing);
|
||||
}
|
||||
|
||||
if (_enableFlexfec) {
|
||||
cricket::StreamParams videoSendStreamParams;
|
||||
cricket::SsrcGroup videoSendSsrcGroup(cricket::kFecFrSsrcGroupSemantics, {_ssrcVideo.outgoing, _ssrcVideo.fecOutgoing});
|
||||
videoSendStreamParams.ssrcs = {_ssrcVideo.outgoing};
|
||||
videoSendStreamParams.ssrc_groups.push_back(videoSendSsrcGroup);
|
||||
videoSendStreamParams.cname = "cname";
|
||||
_videoChannel->AddSendStream(videoSendStreamParams);
|
||||
} else {
|
||||
_videoChannel->AddSendStream(cricket::StreamParams::CreateLegacy(_ssrcVideo.outgoing));
|
||||
}
|
||||
|
||||
checkIsSendingVideoChanged(wasSending);
|
||||
|
@ -727,30 +744,6 @@ void MediaManager::configureSendingVideoIfNeeded() {
|
|||
adjustBitratePreferences(true);
|
||||
}
|
||||
|
||||
void MediaManager::resetSendingVideo() {
|
||||
if (!_didConfigureVideo) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_enableFlexfec) {
|
||||
_videoChannel->RemoveSendStream(_ssrcVideo.outgoing);
|
||||
_videoChannel->RemoveSendStream(_ssrcVideo.fecOutgoing);
|
||||
} else {
|
||||
_videoChannel->RemoveSendStream(_ssrcVideo.outgoing);
|
||||
}
|
||||
|
||||
if (_enableFlexfec) {
|
||||
cricket::StreamParams videoSendStreamParams;
|
||||
cricket::SsrcGroup videoSendSsrcGroup(cricket::kFecFrSsrcGroupSemantics, {_ssrcVideo.outgoing, _ssrcVideo.fecOutgoing});
|
||||
videoSendStreamParams.ssrcs = {_ssrcVideo.outgoing};
|
||||
videoSendStreamParams.ssrc_groups.push_back(videoSendSsrcGroup);
|
||||
videoSendStreamParams.cname = "cname";
|
||||
_videoChannel->AddSendStream(videoSendStreamParams);
|
||||
} else {
|
||||
_videoChannel->AddSendStream(cricket::StreamParams::CreateLegacy(_ssrcVideo.outgoing));
|
||||
}
|
||||
}
|
||||
|
||||
void MediaManager::checkIsSendingVideoChanged(bool wasSending) {
|
||||
const auto sending = computeIsSendingVideo();
|
||||
if (sending == wasSending) {
|
||||
|
|
|
@ -102,7 +102,6 @@ private:
|
|||
|
||||
bool computeIsSendingVideo() const;
|
||||
void configureSendingVideoIfNeeded();
|
||||
void resetSendingVideo();
|
||||
void checkIsSendingVideoChanged(bool wasSending);
|
||||
bool videoCodecsNegotiated() const;
|
||||
|
||||
|
|
|
@ -70,6 +70,21 @@ namespace tgcalls {
|
|||
|
||||
namespace {
|
||||
|
||||
template <typename Out>
|
||||
void splitString(const std::string &s, char delim, Out result) {
|
||||
std::istringstream iss(s);
|
||||
std::string item;
|
||||
while (std::getline(iss, item, delim)) {
|
||||
*result++ = item;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> splitString(const std::string &s, char delim) {
|
||||
std::vector<std::string> elems;
|
||||
splitString(s, delim, std::back_inserter(elems));
|
||||
return elems;
|
||||
}
|
||||
|
||||
static int stringToInt(std::string const &string) {
|
||||
std::stringstream stringStream(string);
|
||||
int value = 0;
|
||||
|
@ -1130,6 +1145,14 @@ public:
|
|||
_requestedMaxQuality = quality;
|
||||
}
|
||||
|
||||
void setStats(absl::optional<GroupInstanceStats::IncomingVideoStats> stats) {
|
||||
_stats = stats;
|
||||
}
|
||||
|
||||
absl::optional<GroupInstanceStats::IncomingVideoStats> getStats() {
|
||||
return _stats;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Threads> _threads;
|
||||
uint32_t _mainVideoSsrc = 0;
|
||||
|
@ -1145,6 +1168,8 @@ private:
|
|||
|
||||
VideoChannelDescription::Quality _requestedMinQuality = VideoChannelDescription::Quality::Thumbnail;
|
||||
VideoChannelDescription::Quality _requestedMaxQuality = VideoChannelDescription::Quality::Thumbnail;
|
||||
|
||||
absl::optional<GroupInstanceStats::IncomingVideoStats> _stats;
|
||||
};
|
||||
|
||||
class MissingSsrcPacketBuffer {
|
||||
|
@ -1776,27 +1801,32 @@ public:
|
|||
GroupLevelsUpdate levelsUpdate;
|
||||
levelsUpdate.updates.reserve(strong->_audioLevels.size() + 1);
|
||||
for (auto &it : strong->_audioLevels) {
|
||||
if (it.second.value.level > 0.001f && it.second.timestamp > timestamp - maxSampleTimeout) {
|
||||
uint32_t effectiveSsrc = it.first.actualSsrc;
|
||||
if (std::find_if(levelsUpdate.updates.begin(), levelsUpdate.updates.end(), [&](GroupLevelUpdate const &item) {
|
||||
return item.ssrc == effectiveSsrc;
|
||||
}) != levelsUpdate.updates.end()) {
|
||||
continue;
|
||||
}
|
||||
levelsUpdate.updates.push_back(GroupLevelUpdate{
|
||||
effectiveSsrc,
|
||||
it.second.value,
|
||||
});
|
||||
if (it.second.value.level > 0.001f) {
|
||||
auto audioChannel = strong->_incomingAudioChannels.find(it.first);
|
||||
if (audioChannel != strong->_incomingAudioChannels.end()) {
|
||||
audioChannel->second->updateActivity();
|
||||
}
|
||||
}
|
||||
|
||||
it.second.value.level *= 0.5f;
|
||||
it.second.value.voice = false;
|
||||
if (it.second.value.level < 0.001f) {
|
||||
continue;
|
||||
}
|
||||
if (it.second.timestamp <= timestamp - maxSampleTimeout) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t effectiveSsrc = it.first.actualSsrc;
|
||||
if (std::find_if(levelsUpdate.updates.begin(), levelsUpdate.updates.end(), [&](GroupLevelUpdate const &item) {
|
||||
return item.ssrc == effectiveSsrc;
|
||||
}) != levelsUpdate.updates.end()) {
|
||||
continue;
|
||||
}
|
||||
levelsUpdate.updates.push_back(GroupLevelUpdate{
|
||||
effectiveSsrc,
|
||||
it.second.value,
|
||||
});
|
||||
if (it.second.value.level > 0.001f) {
|
||||
auto audioChannel = strong->_incomingAudioChannels.find(it.first);
|
||||
if (audioChannel != strong->_incomingAudioChannels.end()) {
|
||||
audioChannel->second->updateActivity();
|
||||
}
|
||||
}
|
||||
|
||||
it.second.value.level *= 0.5f;
|
||||
it.second.value.voice = false;
|
||||
}
|
||||
|
||||
auto myAudioLevel = strong->_myAudioLevel;
|
||||
|
@ -2463,6 +2493,58 @@ public:
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (messageType == "DebugMessage") {
|
||||
const auto message = json.object_items().find("message");
|
||||
if (message != json.object_items().end() && message->second.is_string()) {
|
||||
std::vector<std::string> parts = splitString(message->second.string_value(), '\n');
|
||||
for (const auto &part : parts) {
|
||||
std::string cleanString = part;
|
||||
std::size_t index = cleanString.find("=");
|
||||
if (index == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
cleanString.erase(cleanString.begin(), cleanString.begin() + index + 1);
|
||||
|
||||
index = cleanString.find("target=");
|
||||
if (index == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string endpointId = cleanString.substr(0, index);
|
||||
cleanString.erase(cleanString.begin(), cleanString.begin() + index + 7);
|
||||
|
||||
index = cleanString.find("p/");
|
||||
if (index == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string targetQuality = cleanString.substr(0, index);
|
||||
cleanString.erase(cleanString.begin(), cleanString.begin() + index + 2);
|
||||
|
||||
index = cleanString.find("ideal=");
|
||||
if (index == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cleanString.erase(cleanString.begin(), cleanString.begin() + index + 6);
|
||||
|
||||
index = cleanString.find("p/");
|
||||
if (index == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string availableQuality = cleanString.substr(0, index);
|
||||
|
||||
for (const auto &it : _incomingVideoChannels) {
|
||||
if (it.second->endpointId() == endpointId) {
|
||||
GroupInstanceStats::IncomingVideoStats incomingVideoStats;
|
||||
incomingVideoStats.receivingQuality = stringToInt(targetQuality);
|
||||
incomingVideoStats.availableQuality = stringToInt(availableQuality);
|
||||
it.second->setStats(incomingVideoStats);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2997,11 +3079,21 @@ public:
|
|||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
InternalGroupLevelValue updated;
|
||||
updated.value.level = update.level;
|
||||
updated.value.voice = update.hasSpeech;
|
||||
updated.timestamp = rtc::TimeMillis();
|
||||
strong->_audioLevels.insert(std::make_pair(ChannelId(ssrc), std::move(updated)));
|
||||
|
||||
auto it = strong->_audioLevels.find(ChannelId(ssrc));
|
||||
if (it != strong->_audioLevels.end()) {
|
||||
it->second.value.level = fmax(it->second.value.level, update.level);
|
||||
if (update.hasSpeech) {
|
||||
it->second.value.voice = true;
|
||||
}
|
||||
it->second.timestamp = rtc::TimeMillis();
|
||||
} else {
|
||||
InternalGroupLevelValue updated;
|
||||
updated.value.level = update.level;
|
||||
updated.value.voice = update.hasSpeech;
|
||||
updated.timestamp = rtc::TimeMillis();
|
||||
strong->_audioLevels.insert(std::make_pair(ChannelId(ssrc), std::move(updated)));
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -3205,6 +3297,19 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void getStats(std::function<void(GroupInstanceStats)> completion) {
|
||||
GroupInstanceStats result;
|
||||
|
||||
for (const auto &it : _incomingVideoChannels) {
|
||||
const auto videoStats = it.second->getStats();
|
||||
if (videoStats) {
|
||||
result.incomingVideoStats.push_back(std::make_pair(it.second->endpointId(), videoStats.value()));
|
||||
}
|
||||
}
|
||||
|
||||
completion(result);
|
||||
}
|
||||
|
||||
private:
|
||||
rtc::scoped_refptr<WrappedAudioDeviceModule> createAudioDeviceModule() {
|
||||
const auto create = [&](webrtc::AudioDeviceModule::AudioLayer layer) {
|
||||
|
@ -3465,6 +3570,12 @@ void GroupInstanceCustomImpl::setRequestedVideoChannels(std::vector<VideoChannel
|
|||
});
|
||||
}
|
||||
|
||||
void GroupInstanceCustomImpl::getStats(std::function<void(GroupInstanceStats)> completion) {
|
||||
_internal->perform(RTC_FROM_HERE, [completion = std::move(completion)](GroupInstanceCustomInternal *internal) mutable {
|
||||
internal->getStats(completion);
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<GroupInstanceInterface::AudioDevice> GroupInstanceInterface::getAudioDevices(AudioDevice::Type type) {
|
||||
auto result = std::vector<AudioDevice>();
|
||||
#ifdef WEBRTC_LINUX //Not needed for ios, and some crl::sync stuff is needed for windows
|
||||
|
|
|
@ -43,6 +43,8 @@ public:
|
|||
void setVolume(uint32_t ssrc, double volume);
|
||||
void setRequestedVideoChannels(std::vector<VideoChannelDescription> &&requestedVideoChannels);
|
||||
|
||||
void getStats(std::function<void(GroupInstanceStats)> completion);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Threads> _threads;
|
||||
std::unique_ptr<ThreadLocalObject<GroupInstanceCustomInternal>> _internal;
|
||||
|
|
|
@ -128,6 +128,15 @@ struct VideoChannelDescription {
|
|||
Quality maxQuality = Quality::Thumbnail;
|
||||
};
|
||||
|
||||
struct GroupInstanceStats {
|
||||
struct IncomingVideoStats {
|
||||
int receivingQuality = 0;
|
||||
int availableQuality = 0;
|
||||
};
|
||||
|
||||
std::vector<std::pair<std::string, IncomingVideoStats>> incomingVideoStats;
|
||||
};
|
||||
|
||||
struct GroupInstanceDescriptor {
|
||||
std::shared_ptr<Threads> threads;
|
||||
GroupConfig config;
|
||||
|
@ -185,6 +194,8 @@ public:
|
|||
virtual void setVolume(uint32_t ssrc, double volume) = 0;
|
||||
virtual void setRequestedVideoChannels(std::vector<VideoChannelDescription> &&requestedVideoChannels) = 0;
|
||||
|
||||
virtual void getStats(std::function<void(GroupInstanceStats)> completion) = 0;
|
||||
|
||||
struct AudioDevice {
|
||||
enum class Type {Input, Output};
|
||||
std::string name;
|
||||
|
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 1,009 B After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 1,008 B |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.1 KiB |