@@ -442,6 +442,58 @@ public final class MediaChannel {
442442 }
443443 }
444444
445+ /// libwebrtc の統計情報を取得します。
446+ /// 非同期取得中に切断された場合でも安全になるよう、コールバック内で
447+ /// self の生存確認、state == .connected の再確認、peerChannel.nativeChannel が同一インスタンスかどうか、をチェックしています。
448+ ///
449+ /// - parameter handler: 統計情報取得後に呼ばれるクロージャー
450+ public func getStats( handler: @escaping ( Result < Statistics , Error > ) -> Void ) {
451+ guard state == . connected else {
452+ let message = " MediaChannel is not connected (state: \( state) ) "
453+ Logger . debug ( type: . mediaChannel, message: message)
454+ handler ( . failure( SoraError . peerChannelError ( reason: message) ) )
455+ return
456+ }
457+
458+ guard let peerConnection = peerChannel. nativeChannel else {
459+ let message =
460+ " RTCPeerConnection is unavailable (state: \( state) , nativeChannel: nil) "
461+ Logger . debug ( type: . mediaChannel, message: message)
462+ handler ( . failure( SoraError . peerChannelError ( reason: message) ) )
463+ return
464+ }
465+
466+ // peerConnection.statistics クロージャはlibwebrtc 側のスレッドから遅れて呼ばれ、内部で MediaChannel をキャプチャします。
467+ // ここで self を強参照すると、MediaChannel が切断・解放されたあとでもクロージャが解放されず、deinit が遅れたり循環参照が発生する恐れがあります。
468+ // そのため [weak self] でキャプチャし、呼び出し時点で MediaChannel がまだ有効かどうかをチェックしています。
469+ // self が解放済みなら MediaChannel is unavailable エラーを返すことで安全に処理を抜けます。
470+ peerConnection. statistics { [ weak self] report in
471+ guard let self else {
472+ handler ( . failure( SoraError . peerChannelError ( reason: " MediaChannel is unavailable " ) ) )
473+ return
474+ }
475+
476+ guard self . state == . connected else {
477+ let message = " MediaChannel is not connected (state: \( self . state) ) "
478+ Logger . debug ( type: . mediaChannel, message: message)
479+ handler ( . failure( SoraError . peerChannelError ( reason: message) ) )
480+ return
481+ }
482+
483+ guard let currentPeerConnection = self . peerChannel. nativeChannel,
484+ currentPeerConnection === peerConnection
485+ else {
486+ let message =
487+ " RTCPeerConnection is unavailable (state: \( self . state) , nativeChannel changed) "
488+ Logger . debug ( type: . mediaChannel, message: message)
489+ handler ( . failure( SoraError . peerChannelError ( reason: message) ) )
490+ return
491+ }
492+
493+ handler ( . success( Statistics ( contentsOf: report) ) )
494+ }
495+ }
496+
445497 /// DataChannel を利用してメッセージを送信します
446498 public func sendMessage( label: String , data: Data ) -> Error ? {
447499 guard peerChannel. switchedToDataChannel else {
0 commit comments