/* * Copyright (c) 2010-2021 Belledonne Communications SARL. * * This file is part of linphone-android * (see https://www.linphone.org). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import linphonesw class CallStatisticsData { var call : Call var audioStats:[StatItemData] = [] var videoStats:[StatItemData] = [] let isVideoEnabled = MutableLiveData() let statsUpdated = MutableLiveData(true) private var callDelegate : CallDelegateStub? init (call:Call) { self.call = call callDelegate = CallDelegateStub( onStatsUpdated : { (call: Call, stats: CallStats) -> Void in self.isVideoEnabled.value = call.currentParams?.videoEnabled self.updateCallStats(stats: stats) self.statsUpdated.value = true } ) call.addDelegate(delegate: callDelegate!) initCallStats() isVideoEnabled.value = call.currentParams?.videoEnabled call.audioStats.map { updateCallStats(stats: $0) } call.videoStats.map { updateCallStats(stats: $0) } } private func initCallStats() { audioStats.append(StatItemData(type: StatType.CAPTURE)) audioStats.append(StatItemData(type: StatType.PLAYBACK)) audioStats.append(StatItemData(type: StatType.PAYLOAD)) audioStats.append(StatItemData(type: StatType.ENCODER)) audioStats.append(StatItemData(type: StatType.DECODER)) audioStats.append(StatItemData(type: StatType.DOWNLOAD_BW)) audioStats.append(StatItemData(type: StatType.UPLOAD_BW)) audioStats.append(StatItemData(type: StatType.ICE)) audioStats.append(StatItemData(type: StatType.IP_FAM)) audioStats.append(StatItemData(type: StatType.SENDER_LOSS)) audioStats.append(StatItemData(type: StatType.RECEIVER_LOSS)) audioStats.append(StatItemData(type: StatType.JITTER)) videoStats.append(StatItemData(type: StatType.CAPTURE)) videoStats.append(StatItemData(type: StatType.PLAYBACK)) videoStats.append(StatItemData(type: StatType.PAYLOAD)) videoStats.append(StatItemData(type: StatType.ENCODER)) videoStats.append(StatItemData(type: StatType.DECODER)) videoStats.append(StatItemData(type: StatType.DOWNLOAD_BW)) videoStats.append(StatItemData(type: StatType.UPLOAD_BW)) videoStats.append(StatItemData(type: StatType.ESTIMATED_AVAILABLE_DOWNLOAD_BW)) videoStats.append(StatItemData(type: StatType.ICE)) videoStats.append(StatItemData(type: StatType.IP_FAM)) videoStats.append(StatItemData(type: StatType.SENDER_LOSS)) videoStats.append(StatItemData(type: StatType.RECEIVER_LOSS)) videoStats.append(StatItemData(type: StatType.SENT_RESOLUTION)) videoStats.append(StatItemData(type: StatType.RECEIVED_RESOLUTION)) videoStats.append(StatItemData(type: StatType.SENT_FPS)) videoStats.append(StatItemData(type: StatType.RECEIVED_FPS)) } private func updateCallStats(stats: CallStats) { if (stats.type == StreamType.Audio) { audioStats.forEach{ $0.update(call: call, stats: stats)} } else if (stats.type == StreamType.Video) { videoStats.forEach{ $0.update(call: call, stats: stats)} } } } enum StatType { case CAPTURE case PLAYBACK case PAYLOAD case ENCODER case DECODER case DOWNLOAD_BW case UPLOAD_BW case ICE case IP_FAM case SENDER_LOSS case RECEIVER_LOSS case JITTER case SENT_RESOLUTION case RECEIVED_RESOLUTION case SENT_FPS case RECEIVED_FPS case ESTIMATED_AVAILABLE_DOWNLOAD_BW } struct StatItemData { var type:StatType let value = MutableLiveData() func update(call: Call, stats: CallStats) { guard let payloadType = stats.type == StreamType.Audio ? call.currentParams?.usedAudioPayloadType : call.currentParams?.usedVideoPayloadType, let core = call.core else { value.value = "n/a" return } switch(type) { case StatType.CAPTURE: value.value = stats.type == StreamType.Audio ? core.captureDevice : core.videoDevice case StatType.PLAYBACK: value.value = stats.type == StreamType.Audio ? core.playbackDevice : core.videoDisplayFilter case StatType.PAYLOAD: value.value = "\(payloadType.mimeType)/\(payloadType.clockRate / 1000) kHz" case StatType.ENCODER: value.value = payloadType.description case StatType.DECODER: value.value = payloadType.description case StatType.DOWNLOAD_BW: value.value = "\(stats.downloadBandwidth) kbits/s" case StatType.UPLOAD_BW: value.value = "\(stats.uploadBandwidth) kbits/s" case StatType.ICE: value.value = stats.iceState.toString() case StatType.IP_FAM: value.value = stats.ipFamilyOfRemote == AddressFamily.Inet6 ? "IPv6" : "IPv4" case StatType.SENDER_LOSS: value.value = String(format: "%.2f%",stats.senderLossRate) case StatType.RECEIVER_LOSS: value.value = String(format: "%.2f%",stats.receiverLossRate) case StatType.JITTER: value.value = String(format: "%.2f ms",stats.jitterBufferSizeMs) case StatType.SENT_RESOLUTION: value.value = call.currentParams?.sentVideoDefinition?.name case StatType.RECEIVED_RESOLUTION: value.value = call.currentParams?.receivedVideoDefinition?.name case StatType.SENT_FPS: value.value = "\(call.currentParams?.sentFramerate ?? 0.0)" case StatType.RECEIVED_FPS: value.value = "\(call.currentParams?.receivedFramerate ?? 0.0)" case StatType.ESTIMATED_AVAILABLE_DOWNLOAD_BW: value.value = "\(stats.estimatedDownloadBandwidth) kbit/s" } } func getTypeTitle() -> String { switch (type) { case .CAPTURE: return VoipTexts.call_stats_capture_filter case .PLAYBACK: return VoipTexts.call_stats_player_filter case .PAYLOAD: return VoipTexts.call_stats_codec case .ENCODER: return VoipTexts.call_stats_encoder_name case .DECODER: return VoipTexts.call_stats_decoder_name case .DOWNLOAD_BW: return VoipTexts.call_stats_download case .UPLOAD_BW: return VoipTexts.call_stats_upload case .ICE: return VoipTexts.call_stats_ice case .IP_FAM: return VoipTexts.call_stats_ip case .SENDER_LOSS: return VoipTexts.call_stats_sender_loss_rate case .RECEIVER_LOSS: return VoipTexts.call_stats_receiver_loss_rate case .JITTER: return VoipTexts.call_stats_jitter_buffer case .SENT_RESOLUTION: return VoipTexts.call_stats_video_resolution_sent case .RECEIVED_RESOLUTION: return VoipTexts.call_stats_video_resolution_received case .SENT_FPS: return VoipTexts.call_stats_video_fps_sent case .RECEIVED_FPS: return VoipTexts.call_stats_video_fps_received case .ESTIMATED_AVAILABLE_DOWNLOAD_BW: return VoipTexts.call_stats_estimated_download } } }