Skip to content

rc.10 regression: RoutingFeedbackManager deletes route descriptors it cannot rebuild, so destinations with a destinationPortKey can never be cleared #1496

Description

@ediablo80

Summary

RoutingFeedbackManager.UpdateDestinationImmediate removes a destination's route descriptors before re-deriving them, and does not restore them when the re-derivation yields no source. The destination's routing state is then gone, so a subsequent release finds nothing to release and the hardware is never torn down.

User-visible symptom: routing an "off"/clear source to an affected destination silently does nothing. The route stays up.

Regression in v3.0.0-rc.10. Not present in rc.5.

Why it only hits some destinations

RemoveRouteDescriptors matches on input port key:

.Where(rd => rd.Destination == destination &&
    (string.IsNullOrEmpty(inputPortKey) || (rd.InputPort != null && rd.InputPort.Key == inputPortKey)))

The feedback manager always passes a concrete inputPort.Key. So:

  • A destination configured without destinationPortKey builds descriptors with a null InputPort (logged as dest:auto). They never match, and survive.
  • A destination configured with destinationPortKey builds descriptors carrying that port. They match, and are removed.

That is the whole difference between a destination that works and one that doesn't.

Observed

Three destinations, same room, same NVX fabric. display has no destinationPortKey; content and audio do.

Routing a source to audio, then clearing it:

11:03:31.003  Adding route descriptor: tx-zoom -> dsp-audio-in:anyVideoIn (Audio)
11:03:31.003  Adding route descriptor: tx-zoom -> dsp-audio-in:anyVideoIn (Video)
              ... route executes, NVX stream starts ...
11:03:31.611  [routingFeedbackManager] Updating destination dsp-audio-in with inputPort anyVideoIn
11:03:31.611  Removed 2 route descriptor(s) for 'dsp-audio-in':'anyVideoIn'
11:03:31.612  [routingFeedbackManager] Setting dsp-audio-in current Audio source to none
11:03:31.614  [routingFeedbackManager] Setting dsp-audio-in current Video source to none

11:03:33.689  Release route for 'dsp-audio-in':'anyVideoIn'
11:03:33.689  Removed 0 route descriptor(s) for 'dsp-audio-in':'anyVideoIn'

No switch is executed, no stream cleared. The decoder stays tuned.

The same clear on display works, because its descriptors were never removed:

11:03:23.146  Removed 2 route descriptor(s) for 'display':'auto'
11:03:23.146  [display] Releasing current route: tx-pc (Audio)
11:03:23.147  *** Executing switch: null to rx-display with signal type Audio ***
11:03:23.148  [rx-display] Clearing stream
11:03:23.250  activeRoutes: {"display":"none"}

Cause

RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptors(destination, inputPort.Key);

foreach (var signalType in new[] { eRoutingSignalType.Audio, eRoutingSignalType.Video })
{
    if (!TryGetActiveSourcePort(firstTieLine, signalType, new HashSet<string>(), out var sourcePort))
        continue;                               // bails - descriptors stay removed

    var source = sourcePort?.ParentDevice as IRoutingOutputs;
    destination.SetCurrentSource(signalType, source as IRoutingSource);

    if (source == null)
        continue;                               // bails - descriptors stay removed

    var (route, _) = destination.GetRouteToSource(source, signalType, inputPort, sourcePort);
    RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(route);
}

The removal is unconditional; both re-add paths are conditional. Any upstream walk that fails to resolve a source destroys state it cannot rebuild. In our case TryGetActiveSourcePort resolves to a null source while walking back through the NVX virtual matrix, roughly 600 ms after the route executed.

Suggested fix

Do not remove before you can replace. Either:

  • derive the replacement descriptors first, and only remove the old ones once a replacement exists for that signal type; or
  • remove per signal type, inside the loop, after TryGetActiveSourcePort has succeeded.

Setting the current source to none when the walk fails seems reasonable on its own — the feedback genuinely is unknown. Deleting the route descriptors is the part that breaks teardown, because ReleaseRouteInternal depends on them to know what to switch off.

Worth noting this interacts with #1494: before that fix this call removed at most one descriptor, so the blast radius was smaller. The #1494 fix is correct; this call site just needs to stop assuming removal is free.

Workaround

Omit destinationPortKey from the destination so its descriptors are keyed auto and escape the match. Only viable when a single tie line feeds the sink, so the port still resolves unambiguously.

Environment

  • Essentials v3.0.0-rc.10 on a CP4N
  • PepperDash.Essentials.Plugins.Crestron.Nvx 4.0.0-feat-matrix-routing-videosync-status.4
  • Destinations: LG display (no port key), Zoom codec hdmiIn1, genericsink anyVideoIn

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions