本文介紹如何在通話中監測網路、音頻、視頻的品質。
功能介紹
在通話過程中,RTC SDK會觸發與通話和直播品質相關的回調。從這些回調中,可以瞭解使用者的互動體驗,進行問題排查和使用者體驗的最佳化。此外,當使用者網路狀態發生變化時,SDK也會觸發相關回調,上報最新的狀態。
範例程式碼
Android端通話中品質監測:Android/ARTCExample/BasicUsage/src/main/java/com/aliyun/artc/api/basicusage/StreamMonitoring/StreamMonitoringActivity.java。
iOS端通話中品質監測:iOS/ARTCExample/BasicUsage/StreamMonitoring/StreamMonitoringVC.swift。
前提條件
在設定視頻配置之前,請確保達成以下條件:
功能實現
本文檔以 Android 平台為例,不同平台介面命名可能不同,具體請參考程式碼範例。
上下行網路品質報告
當網路品質發生變化時,使用者可以通過onNetworkQualityChanged回調擷取上下行網路品質資訊。
Linux 平台暫時不支援該功能。
uid:使用者識別碼,如果為空白表示本機使用者上下行網路狀態。
upQuality:上行網路狀態。
downQuality:下行網路狀態。
網路品質類型AliRtcNetworkQuality如下:
枚舉值 | 描述 |
AliRtcNetworkExcellent(0) | 網路極好。 |
AliRtcNetworkGood(1) | 網路好。 |
AliRtcNetworkPoor(2) | 網路不好。 |
AliRtcNetworkBad(3) | 網路差。 |
AliRtcNetworkVeryBad(4) | 網路極差。 |
AliRtcNetworkDisconnected(5) | 網路斷開。 |
AliRtcNetworkUnknow(6) | 網路品質未知。 |
統計資訊報告
onAliRtcStats回調每2秒觸發一次,用於即時反饋通話過程中的各種統計資料,包括資料轉送統計資料、CPU使用、通話時間長度、丟包率等。詳細指標介紹請參考AliRtcStats。
音頻品質報告
音頻推流資料統計
onRtcLocalAudioStats回調主要用於即時反饋本地音頻發送端的統計資訊。該回調每 2s 觸發一次,開發人員可通過此介面監控音頻品質、網路狀況和推流狀態等資料。返回的部分指標資訊如下:
sentSamplerate:採樣率(單位:Hz),例如44100、48000。表示當前音訊框架的採樣頻率,影響音質清晰度。
numChannel:聲道數,如1(單聲道)、2(立體聲)。決定音頻空間感和傳輸開銷。
sentBitrate:發送碼率(單位:kbps),表示當前音頻編碼後每秒發送的資料量。碼率越高音質越好,但頻寬佔用越大。
更多統計指標及其介紹請參考AliRtcLocalAudioStats。
音頻訂閱資料統計
onRtcRemoteAudioStats回調主要用於即時反饋遠端使用者的音頻流的統計資訊。該回調每隔 2s 觸發一次,提供關於音頻品質、網路狀況和播放效能的詳細資料。下面為部分指標資訊:
quality:
0:Unknow,品質未知。
1:Excellent,品質極好。
2:Good,品質好,碼率略低。
3:Poor,使用者主觀感受有瑕疵,但不影響溝通。
4:Bad,品質差,溝通不順暢。
5:Very Bad,品質極差,基本不能溝通。
6:NetworkDisconnected,網路斷開,完全無法溝通。
audioTotalFrozenRate:自加入頻道音頻播放卡頓率。
network_transport_delay:發送端到接收端的網路延時(ms)。
更多指標及詳細介紹請參考AliRtcRemoteAudioStats。
視頻品質報告
視頻推流資料統計
onRtcLocalVideoStats回調主要用於即時反饋本地視頻發送端的統計資訊。該回調每 2s 觸發一次,開發人員可通過此介面監控視頻編碼品質、網路狀況、推流狀態等資料。返回的部分指標資訊如下:
actualEncodeBitrate:實際編碼碼率,表示實際編碼過程中使用的碼率。
sentBitrate:發送碼率(單位:kbps),表示實際通過網路發送的視頻碼率。
captureFps、encodeFps、sentFps:視頻採集、編碼、發送幀率。
avgQp:每秒平均QP(Quantization Parameter),反映視頻編碼品質。通常 QP 值越小,壓縮率越低。
更多統計指標及其介紹請參考AliRtcLocalVideoStats。
視頻訂閱資料統計
onRtcRemoteVideoStats回調主要用於即時反饋遠端使用者的視頻流的統計資訊。該回調每隔 2s 觸發一次,提供關於視頻品質、網路狀況和播放效能的詳細資料。下面為部分指標資訊:
width、height:視頻解析度。
decodeFps、renderFps:視頻解碼、渲染的幀率。
videoTotalFrozenRate:視頻播放的累計卡頓率。
更多指標及詳細介紹請參考AliRtcRemoteVideoStats。
Android
// 上下行網路品質
private AliRtcEngineEventListener mRtcEngineEventListener = new AliRtcEngineEventListener() {
// 網路品質變化
@Override
public void onNetworkQualityChanged(String uid, AliRtcEngine.AliRtcNetworkQuality upQuality, AliRtcEngine.AliRtcNetworkQuality downQuality){
super.onNetworkQualityChanged(uid, upQuality, downQuality);
// TODO: 添加商務邏輯
}
}
// 統計資料回調
private AliRtcEngineNotify mRtcEngineNotify = new AliRtcEngineNotify() {
@Override
public void onAliRtcStats(AliRtcEngine.AliRtcStats stats){
super.onAliRtcStats(stats);
mRtcStats = stats;
updateStatsDisplay();
}
@Override
public void onRtcLocalAudioStats(AliRtcEngine.AliRtcLocalAudioStats aliRtcStats){
super.onRtcLocalAudioStats(aliRtcStats);
mLocalAudioStats = aliRtcStats;
}
@Override
public void onRtcRemoteAudioStats(AliRtcEngine.AliRtcRemoteAudioStats aliRtcStats){
super.onRtcRemoteAudioStats(aliRtcStats);
mRemoteAudioStats = aliRtcStats;
}
@Override
public void onRtcLocalVideoStats(AliRtcEngine.AliRtcLocalVideoStats aliRtcLocalVideoStats){
super.onRtcLocalVideoStats(aliRtcLocalVideoStats);
mLocalVideoStats = aliRtcLocalVideoStats;
}
@Override
public void onRtcRemoteVideoStats(AliRtcEngine.AliRtcRemoteVideoStats aliRtcRemoteVideoStats){
super.onRtcRemoteVideoStats(aliRtcRemoteVideoStats);
mRemoteVideoStats = aliRtcRemoteVideoStats;
// 更新顯示所有統計資訊
updateStatsDisplay();
}
}iOS
extension StreamMonitoringMainVC: AliRtcEngineDelegate {
// MARK: Network Quality
func onNetworkQualityChanged(_ uid: String, up upQuality: AliRtcNetworkQuality, downNetworkQuality downQuality: AliRtcNetworkQuality) {
}
// MARK: Stream Monitoring Callback
func onRtcStats(_ stats: AliRtcStats) {
}
func onRtcLocalAudioStats(_ localAudioStats: AliRtcLocalAudioStats) {
}
func onRtcRemoteAudioStats(_ remoteAudioStats: AliRtcRemoteAudioStats) {
}
func onRtcLocalVideoStats(_ localVideoStats: AliRtcLocalVideoStats) {
}
func onRtcRemoteVideoStats(_ remoteVideoStats: AliRtcRemoteVideoStats) {
}
}Mac
- (void)onNetworkQualityChanged:(NSString *)uid upNetworkQuality:(AliRtcNetworkQuality)upQuality downNetworkQuality:(AliRtcNetworkQuality)downQuality{
NSLog(@"onNetworkQualityChanged callback!");
}
- (void)onRtcStats:(AliRtcStats)stats{
/* */
NSLog(@"onRtcStats callback!");
}
- (void)onRtcLocalAudioStats:(AliRtcLocalAudioStats *)localAudioStats {
NSLog(@"onRtcLocalAudioStats callback!");
}
- (void)onRtcRemoteAudioStats:(AliRtcRemoteAudioStats *)remoteAudioStats {
NSLog(@"onRtcRemoteAudioStats callback!");
}
- (void)onRtcLocalVideoStats:(AliRtcLocalVideoStats *)localVideoStats{
NSLog(@"onRtcLocalVideoStats callback!");
}
- (void)onRtcRemoteVideoStats:(AliRtcRemoteVideoStats *)remoteVideoStats{
NSLog(@"onRtcRemoteVideoStats callback!");
}
Windows
/* 本地視頻狀態 */
void CTutorialDlg::OnLocalVideoStats(const AliEngineLocalVideoStats& localVideoStats)
{
if (m_pRtcStatsDlg != nullptr) {
m_pRtcStatsDlg->setLocalVideoStats(localVideoStats);
}
}
/* 本地音頻狀態 */
void CTutorialDlg::OnLocalAudioStats(const AliEngineLocalAudioStats& localVideoStats)
{
if (nullptr != m_pRtcStatsDlg)
{
m_pRtcStatsDlg->setLocalAudioStats(localVideoStats);
}
}
/* 網路狀態 */
void CTutorialDlg::OnNetworkQualityChanged(const char *uid,
AliEngineNetworkQuality upQuality,
AliEngineNetworkQuality downQuality)
{
// 處理網路品質變化
}
/* 遠端音頻狀態 */
void CTutorialDlg::OnRemoteAudioStats(const AliEngineRemoteAudioStats& remoteAudioStats)
{
CString strText, strTemp;
switch (remoteAudioStats.track)
{
case AliEngineAudioTrackMic:
strText = _T("AudioTrackMic");
break;
default:
strText = _T("AudioTrackNo");
break;
}
strText += _T(": { ");
strTemp.Format(_T("quality: %d,"), remoteAudioStats.quality);
strText += strTemp;
strTemp.Format(_T("audio_loss_rate: %d,"), remoteAudioStats.audioLossRate);
strText += strTemp;
strTemp.Format(_T("rcvd_bitrate: %d,"), remoteAudioStats.rcvdBitrate);
strText += strTemp;
strTemp.Format(_T("total_frozen_times: %d,"), remoteAudioStats.totalFrozenTimes);
strText += strTemp;
strTemp.Format(_T("network_transport_delay: %d,"), remoteAudioStats.networkTransportDelay);
strText += strTemp;
strTemp.Format(_T("jitter_buffer_delay: %d }\n"), remoteAudioStats.jitterBufferDelay);
strText += strTemp;
CString strid = AliStringToCString(remoteAudioStats.userId);
/* 寫到狀態map中,UI通過定時器讀取顯示 */
m_MuteUserAudioStats.Lock();
m_UserAudioStatsMap[strid] = strText;
m_MuteUserAudioStats.Unlock();
}
/* 遠端視頻狀態 */
void CTutorialDlg::OnRemoteVideoStats(const AliEngineRemoteVideoStats& remoteVideoStats)
{
CString strText, strTemp, strStats;
CString strid = AliStringToCString(remoteVideoStats.userId);
auto iter = m_UserVideoStatsMap.find(strid);
if (iter != m_UserVideoStatsMap.end())
{
strStats = iter->second;
}
switch (remoteVideoStats.track)
{
case AliEngineVideoTrackCamera:
strText = _T("TrackCamera");
break;
case AliEngineVideoTrackScreen:
strText = _T("TrackScreen");
break;
case AliEngineVideoTrackBoth:
strText = _T("TrackBoth");
break;
default:
strText = _T("TrackNo");
break;
}
delRepeatTrackStatic(strStats, strText);
strText += _T(": { ");
strTemp.Format(_T("Width: %d,"), remoteVideoStats.width);
strText += strTemp;
strTemp.Format(_T("Height: %d,"), remoteVideoStats.height);
strText += strTemp;
strTemp.Format(_T("Decode_fps: %d,"), remoteVideoStats.decodeFps);
strText += strTemp;
strTemp.Format(_T("Render_fps: %d,"), remoteVideoStats.renderFps);
strText += strTemp;
strTemp.Format(_T("Frozen_times: %d }\n"), remoteVideoStats.frozenTimes);
strText += strTemp;
strStats += strText;
/* 寫到狀態map中,UI通過定時器讀取顯示 */
m_MutexUserVideoStats.Lock();
m_UserVideoStatsMap[strid] = strStats;
m_MutexUserVideoStats.Unlock();
}
/* 引擎全域狀態 */
void CTutorialDlg::OnStats(const AliEngineStats& stats)
{
AliEngineStats* pStats = new AliEngineStats;
if (pStats == nullptr)
{
return;
}
/* 全域狀態也可以通過訊息發送給UI模組顯示 */
*pStats = stats;
PostMessage(MM_UPDATE_STATS, (WPARAM)pStats, 0);
}Linux
實現EngineEventHandlerInterface類中回調介面。
/**
* @brief 當前會話統計資訊回調
* @param stats 會話統計資訊
* @note SDK每兩秒觸發一次此統計資訊回調
*/
virtual void OnStats(const AliRTCSdk::Linux::AliEngineStats& stats) {}
/**
* @brief 本地視頻統計資訊
* @param localVideoStats 本地視頻統計資訊
* @note SDK每兩秒觸發一次此統計資訊回調
*/
virtual void OnLocalVideoStats(const AliRTCSdk::Linux::AliEngineLocalVideoStats& localVideoStats) {}
/**
* @brief 遠端視頻統計資訊
* @param remoteVideoStats 遠端視頻統計資訊
* @note SDK每兩秒觸發一次此統計資訊回調
*/
virtual void OnRemoteVideoStats(const AliRTCSdk::Linux::AliEngineRemoteVideoStats& remoteVideoStats) {}
/**
* @brief 本地音頻統計資訊
* @param localAudioStats 本地視頻統計資訊
* @note SDK每兩秒觸發一次此統計資訊回調
*/
virtual void OnLocalAudioStats(const AliRTCSdk::Linux::AliEngineLocalAudioStats& localAudioStats) {}
/**
* @brief 遠端音頻統計資訊
* @param remoteAudioStats 遠端視頻統計資訊
* @note SDK每兩秒觸發一次此統計資訊回調
*/
virtual void OnRemoteAudioStats(const AliRTCSdk::Linux::AliEngineRemoteAudioStats& remoteAudioStats) {}