When a publisher terminates while a subscriber has an open subgroup, onPublishDone() resets handle to null but keeps the subscriptions_ entry alive (the forwarder marks the subscriber receivedPublishDone_ and waits for subgroups to close). If the publisher then reconnects and re-publishes the same track, the multipublisher path calls handle->unsubscribe() on the null handle — SIGSEGV.
Guard both handle->unsubscribe() and forwarder->publishDone() on the null check: when handle is null the previous publisher already terminated and the forwarder was already drained, so both calls are wrong.
Adds a regression test that reproduces the crash by opening a subgroup before dropping the publisher connection.
When a publisher terminates while a subscriber has an open subgroup, onPublishDone() resets handle to null but keeps the subscriptions_ entry alive (the forwarder marks the subscriber receivedPublishDone_ and waits for subgroups to close). If the publisher then reconnects and re-publishes the same track, the multipublisher path calls handle->unsubscribe() on the null handle — SIGSEGV.
Guard both handle->unsubscribe() and forwarder->publishDone() on the null check: when handle is null the previous publisher already terminated and the forwarder was already drained, so both calls are wrong.
Adds a regression test that reproduces the crash by opening a subgroup before dropping the publisher connection.