Skip to content

Commit

Permalink
Merge pull request #129 from GetStream/feature/uni-64-add-debug-info-…
Browse files Browse the repository at this point in the history
…around-mediastream

Feature/uni 64 add debug info around mediastream
  • Loading branch information
sierpinskid authored Dec 31, 2024
2 parents 85837f8 + 044c86c commit cd93507
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 36 deletions.
88 changes: 62 additions & 26 deletions Packages/StreamVideo/Runtime/Core/LowLevelClient/RtcSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ internal sealed class RtcSession : IMediaInputProvider, IDisposable
public const ulong FullPublishVideoBitrate = 1_200_000;
public const ulong HalfPublishVideoBitrate = MaxPublishVideoBitrate / 2;
public const ulong QuarterPublishVideoBitrate = MaxPublishVideoBitrate / 4;

// StreamTodo: control this via compiler flag
public const bool LogWebRTCStats = false;

public CallingState CallState
{
Expand Down Expand Up @@ -96,7 +99,7 @@ public AudioSource AudioInput
{
return;
}

var prev = _audioInput;
_audioInput = value;

Expand All @@ -121,7 +124,7 @@ public WebCamTexture VideoInput
{
return;
}

var prev = _videoInput;
_videoInput = value;

Expand Down Expand Up @@ -207,7 +210,7 @@ public void Update()
public async Task SendWebRtcStats(SendStatsRequest request)
{
var response = await RpcCallAsync(request, GeneratedAPI.SendStats,
nameof(GeneratedAPI.SendStats));
nameof(GeneratedAPI.SendStats), postLog: LogWebRTCStats);

if (ActiveCall == null)
{
Expand Down Expand Up @@ -382,7 +385,7 @@ private void ClearSession()
* - we retry when:
* -- error isn't permanent, SFU didn't change, the mute/publish state didn't change
* -- we cap at 30 retries to prevent endless loops
*/
*/
private void QueueTracksSubscriptionRequest()
{
if (_trackSubscriptionRequested)
Expand Down Expand Up @@ -486,10 +489,12 @@ private VideoResolution GetRequestedVideoResolution(IStreamVideoCallParticipant
if (participant == null || participant.SessionId == null)
{
#if STREAM_DEBUG_ENABLED
_logs.Warning($"Participant or SessionId was null: {participant}, SeessionId: {participant?.SessionId}");
_logs.Warning(
$"Participant or SessionId was null: {participant}, SeessionId: {participant?.SessionId}");
#endif
return _config.Video.DefaultParticipantVideoResolution;
}

if (_videoResolutionByParticipantSessionId.TryGetValue(participant.SessionId, out var resolution))
{
return resolution;
Expand Down Expand Up @@ -755,7 +760,7 @@ private void OnSfuPublisherAnswer(PublisherAnswer publisherAnswer)
//StreamTodo: implement retry strategy like in Android SDK
//If possible, take into account if we the update is still valid e.g.
private async Task<TResponse> RpcCallAsync<TRequest, TResponse>(TRequest request,
Func<HttpClient, TRequest, Task<TResponse>> rpcCallAsync, string debugRequestName, bool preLog = false)
Func<HttpClient, TRequest, Task<TResponse>> rpcCallAsync, string debugRequestName, bool preLog = false, bool postLog = true)
{
//StreamTodo: use rpcCallAsync.GetMethodInfo().Name; instead debugRequestName

Expand All @@ -770,22 +775,25 @@ private async Task<TResponse> RpcCallAsync<TRequest, TResponse>(TRequest request
var response = await rpcCallAsync(_httpClient, request);

#if STREAM_DEBUG_ENABLED
var serializedResponse = _serializer.Serialize(response);

//StreamTodo: move to debug helper class
var sb = new System.Text.StringBuilder();

var errorProperty = typeof(TResponse).GetProperty("Error");
var error = (StreamVideo.v1.Sfu.Models.Error)errorProperty.GetValue(response);
var errorLog = error != null ? $"<color=red>{error.Message}</color>" : "";
var errorStatus = error != null ? "<color=red>FAILED</color>" : "<color=green>SUCCESS</color>";
sb.AppendLine($"[RPC Request] {errorStatus} {debugRequestName} | {errorLog}");
sb.AppendLine(serializedRequest);
sb.AppendLine();
sb.AppendLine("Response:");
sb.AppendLine(serializedResponse);

_logs.Warning(sb.ToString());
if (postLog)
{
var serializedResponse = _serializer.Serialize(response);

//StreamTodo: move to debug helper class
var sb = new System.Text.StringBuilder();

var errorProperty = typeof(TResponse).GetProperty("Error");
var error = (StreamVideo.v1.Sfu.Models.Error)errorProperty.GetValue(response);
var errorLog = error != null ? $"<color=red>{error.Message}</color>" : "";
var errorStatus = error != null ? "<color=red>FAILED</color>" : "<color=green>SUCCESS</color>";
sb.AppendLine($"[RPC Request] {errorStatus} {debugRequestName} | {errorLog}");
sb.AppendLine(serializedRequest);
sb.AppendLine();
sb.AppendLine("Response:");
sb.AppendLine(serializedResponse);

_logs.Warning(sb.ToString());
}
#endif

return response;
Expand Down Expand Up @@ -832,6 +840,8 @@ private async void OnPublisherNegotiationNeeded()
var offer = await Publisher.CreateOfferAsync();
Publisher.ThrowDisposedDuringOperationIfNull();

//StreamTOodo: check if SDP is null or empty (this would throw an exception during setting)

//StreamTodo: ignored the _config.Audio.EnableRed because with current webRTC version this modification causes a crash
//We're also forcing the red codec in the StreamPeerConnection but atm this results in "InvalidModification"
//This is most likely issue with the webRTC lib
Expand Down Expand Up @@ -926,16 +936,34 @@ private IEnumerable<TrackInfo> GetPublisherTracks(string sdp)
//StreamTodo: figure out TrackType, because we rely on transceiver track type mapping we don't support atm screen video/audio share tracks
//This implementation is based on the Android SDK, perhaps we shouldn't rely on GetTransceivers() but maintain our own TrackType => Transceiver mapping


foreach (var t in transceivers)
{
var trackId = t.Sender.Track.Kind == TrackKind.Video ? ExtractVideoTrackId(sdp) : t.Sender.Track.Id;

if (t.Mid == null)
{
// StreamTodo: figure out why this is happening and if there should be any re-try logic. This is part of the SDP negotiation
// Perhaps we need to recreate transceiver.
// This is happening only sometimes
#if STREAM_DEBUG_ENABLED
var sb = new System.Text.StringBuilder();
sb.AppendLine("Error: Track MID was NULL. Transceiver dump:");
sb.AppendLine(DebugObjectPrinter.PrintObject(t));
sb.AppendLine("SDP:");
sb.AppendLine(sdp);
_logs.Error(sb.ToString());
#endif

t.Stop();

continue;
}

var trackInfo = new TrackInfo
{
TrackId = trackId,
TrackType = t.Sender.Track.Kind.ToInternalEnum(),
Mid = t.Mid
Mid = t.Mid // Will throw if NULL due to protbuf precondition
};

if (t.Sender.Track.Kind == TrackKind.Video)
Expand Down Expand Up @@ -979,6 +1007,10 @@ private string ReplaceVp8PayloadType(string sdpOffer)
private IEnumerable<VideoLayer> GetPublisherVideoLayers(IEnumerable<RTCRtpEncodingParameters> encodings,
PublisherVideoSettings videoSettings)
{
#if STREAM_DEBUG_ENABLED
var sb = new System.Text.StringBuilder();
sb.AppendLine("GetPublisherVideoLayers:");
#endif
foreach (var encoding in encodings)
{
var scaleBy = encoding.scaleResolutionDownBy ?? 1.0;
Expand All @@ -989,8 +1021,8 @@ private IEnumerable<VideoLayer> GetPublisherVideoLayers(IEnumerable<RTCRtpEncodi
var quality = EncodingsToVideoQuality(encoding);

#if STREAM_DEBUG_ENABLED
_logs.Warning(
$"Video layer - rid: {encoding.rid} quality: {quality}, scaleBy: {scaleBy}, width: {width}, height: {height}, bitrate: {encoding.maxBitrate}");
sb.AppendLine(
$"- rid: {encoding.rid} quality: {quality}, scaleBy: {scaleBy}, width: {width}, height: {height}, bitrate: {encoding.maxBitrate}");
#endif

yield return new VideoLayer
Expand All @@ -1006,6 +1038,10 @@ private IEnumerable<VideoLayer> GetPublisherVideoLayers(IEnumerable<RTCRtpEncodi
Quality = quality,
};
}

#if STREAM_DEBUG_ENABLED
_logs.Warning(sb.ToString());
#endif
}

private static VideoQuality EncodingsToVideoQuality(RTCRtpEncodingParameters encodings)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -558,11 +558,7 @@ private void ForceCodec(RTCRtpTransceiver transceiver, string codecKey, TrackKin
=> c.mimeType.IndexOf(codecKey, StringComparison.OrdinalIgnoreCase) != -1);

#if STREAM_DEBUG_ENABLED
foreach (var codec in capabilities.codecs)
{
_logs.Info($"Available codec of kind `{kind}`: {codec.mimeType}");
}

_logs.Info($"Available codec of kind `{kind}`: " + string.Join(", ", capabilities.codecs.Select(c => c.mimeType)));
#endif

if (!forcedCodecs.Any())
Expand All @@ -583,7 +579,7 @@ private void ForceCodec(RTCRtpTransceiver transceiver, string codecKey, TrackKin
var error = transceiver.SetCodecPreferences(forcedCodecs.ToArray());
if (error != RTCErrorType.None)
{
_logs.Error($"Failed to set codecs for kind kind `{kind}` due to error: {error}");
_logs.Error($"Failed to set codecs for kind `{kind}` due to error: {error}");
}
}
}
Expand Down
11 changes: 7 additions & 4 deletions Packages/StreamVideo/Runtime/Core/Stats/WebRtcStatsSender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,13 @@ private async Task CollectAndSend()
};

#if STREAM_DEBUG_ENABLED
_logs.Info("-----------WebRTC STATS DUMP -> 1. publisher, 2. subscriber------");
_logs.Info(publisherStatsJson);
_logs.Info(subscriberStatsJson);
_logs.Info("-----------END WebRTC STATS DUMP END------");
if (RtcSession.LogWebRTCStats)
{
_logs.Info("-----------WebRTC STATS DUMP -> 1. publisher, 2. subscriber------");
_logs.Info(publisherStatsJson);
_logs.Info(subscriberStatsJson);
_logs.Info("-----------END WebRTC STATS DUMP END------");
}
#endif

await _rtcSession.SendWebRtcStats(request);
Expand Down
90 changes: 90 additions & 0 deletions Packages/StreamVideo/Runtime/Core/Utils/DebugObjectPrinter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

namespace StreamVideo.Core.Utils
{
#if STREAM_DEBUG_ENABLED
internal class DebugObjectPrinter
{
public static string PrintObject(object obj, int maxDepth = 10)
=> PrintObjectInternal(obj, new HashSet<object>(), 0, maxDepth);

private static string PrintObjectInternal(object obj, HashSet<object> visited, int depth, int maxDepth)
{
if (obj == null)
return "null";

if (depth >= maxDepth)
return $"[Max depth {maxDepth} reached]";

// Handle primitive types and strings
if (obj.GetType().IsPrimitive || obj is string || obj is DateTime)
return obj.ToString();

// Prevent circular references
if (!obj.GetType().IsValueType)
{
if (visited.Contains(obj))
return "[Circular Reference]";
visited.Add(obj);
}

var sb = new StringBuilder();
var type = obj.GetType();

// Handle collections
if (obj is IEnumerable enumerable && !(obj is string))
{
sb.Append($"{type.Name} [");
bool first = true;
foreach (var item in enumerable)
{
if (!first) sb.Append(", ");
sb.Append(PrintObjectInternal(item, visited, depth + 1, maxDepth));
first = false;
}

sb.Append("]");
return sb.ToString();
}

// Handle objects
sb.AppendLine($"{type.Name} {{");

// Get all fields (both public and private)
var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.Static);
foreach (var field in fields)
{
var value = field.GetValue(obj);
sb.AppendLine($" {field.Name} ({field.Attributes}): " +
PrintObjectInternal(value, visited, depth + 1, maxDepth));
}

// Get all properties
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.Static);

foreach (var prop in properties)
{
try
{
var value = prop.GetValue(obj, null);
sb.AppendLine($" {prop.Name} ({prop.Attributes}): " +
PrintObjectInternal(value, visited, depth + 1, maxDepth));
}
catch (Exception ex)
{
sb.AppendLine($" {prop.Name}: [Error accessing property: {ex.Message}]");
}
}

sb.Append("}");
return sb.ToString();
}
}
#endif
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit cd93507

Please sign in to comment.