diag: log what the broadcast is actually bound to while a stream runs
The logs answered the question and closed off a whole branch of investigation. The long run: broadcast inserted 14:17:04.577, ffmpeg to rtmp://a.rtmp.youtube.com/live2/5f6z-... at 14:17:05.686, and ffmpeg's own muxing summary at 14:18:56.473 reads video:101229KiB audio:1419KiB muxing overhead: 0.202949% ~99MB of real video plus audio over 112s, which is what 8000+128 kb/s is supposed to produce. No socket error, no 4xx from ingest, no reconnect. And the broadcast still never left `ready`; close-out 403'd invalidTransition again. The 8s run behaved the same way, so "YouTube needed longer" is disproven — 112s is longer. So the RTMP path is provably healthy. That is the finding worth keeping: it kills the entire "the push is broken" theory, which is where this had been heading for a week. The break is between ingest being accepted and the broadcast being told to start. Which puts the binding in frame, and the app was structurally blind to it. Google documents contentDetails.boundStreamId and says enableAutoStart fires "when you start streaming video on the bound live stream" — both valid on insert, neither restricted against reusable streams. But the migration guide also says a broadcast "is not bound to any streams at the time that it's created", and the failure that produces is invisible from the RTMP side precisely because a reusable stream will happily accept bytes for an unbound broadcast. Ingest succeeding proves nothing. Hence LogBroadcastDiagnosticsAsync, riding the existing health poll rather than adding a timer. One greppable line: lifecycle status, privacy, recording, boundStreamId *compared against the stream id we are actually pushing* (BoundMatches: yes / MISMATCH / NO-BINDING / unknown), boundStreamLastUpdate- TimeMs, autoStart/Stop, liveChatId presence, the stream's own streamStatus and activeLiveChatId, and the CDN settings we declared. Deliberately self-limiting: _broadcastSeenLive latches on live/liveStarting so the diagnostic shouts only while the broadcast is stuck and goes quiet once it is healthy. A permanent per-tick log line would be noise; this is the opposite. One test, per the Good Dog rule: BroadcastBindingTests pins the matrix and specifically pins `unknown` when there is no stream id to compare against, so a missing expectation can never be misreported as a match — that is the failure that would make this diagnostic lie. Suite 387/387, 0 warnings.
This commit is contained in:
@@ -167,6 +167,7 @@ public partial class MainViewModel : ViewModelBase
|
||||
|
||||
_currentBroadcastId = await _youtubeStream.CreateBroadcast(
|
||||
StreamTitle, StreamDescription, DateTime.UtcNow, stream.Id);
|
||||
_broadcastSeenLive = false;
|
||||
if (_currentBroadcastId == null)
|
||||
{
|
||||
AppLog.Write("Broadcast creation failed; check the OAuth session");
|
||||
@@ -236,7 +237,15 @@ public partial class MainViewModel : ViewModelBase
|
||||
|
||||
/// <summary>One liveStreams.list poll (TASK 5 item 3): updates the YouTube
|
||||
/// health state + the report-by-exception banner. Never throws — a poll
|
||||
/// failure logs and leaves the last state untouched.</summary>
|
||||
/// failure logs and leaves the last state untouched.
|
||||
///
|
||||
/// Also piggybacks the broadcast diagnostic (2026-10-04) — the health poll was
|
||||
/// already running during a stream but only ever read the STREAM's health, which
|
||||
/// is why two test runs could push 100MB to YouTube and still leave the app
|
||||
/// unable to say whether the broadcast had ever left `ready`. Deliberately
|
||||
/// self-limiting: it only logs while the broadcast has NOT reached `live`, so it
|
||||
/// shouts exactly when something is wrong and goes quiet once healthy, rather than
|
||||
/// adding a permanent per-tick line to the log.</summary>
|
||||
private async Task PollHealthAsync()
|
||||
{
|
||||
if (_reusableStream == null || !IsLive) return;
|
||||
@@ -254,6 +263,12 @@ public partial class MainViewModel : ViewModelBase
|
||||
CurrentHealth.ConfigurationIssues.AddRange(health.ConfigurationIssues);
|
||||
OnPropertyChanged(nameof(CurrentHealth));
|
||||
ApplyHealthIssue(health.ConfigurationIssues);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(_currentBroadcastId) && !_broadcastSeenLive)
|
||||
{
|
||||
var lifeCycle = await _youtubeStream.LogBroadcastDiagnosticsAsync(_currentBroadcastId, _reusableStream.Id);
|
||||
if (lifeCycle is "live" or "liveStarting") _broadcastSeenLive = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user