Repository: allenporter/rtsp-to-webrtc-client (language: python)

Issue:
Offer process drops any currently active stream
I've mostly succeeded in configuring Home Assistant + RTSPtoWeb and am pleased with the results to far, but there's one major issue that is prevening me from being able to use this full time.

Whenever a stream is initialized, as part of the offer process, it looks like we make a call to the RTSPtoWeb API to create or update the stream configuration:

https://github.com/allenporter/rtsp-to-webrtc-client/blob/86db8ef18f1d39d303892d52a90565964a861fa8/rtsp_to_webrtc/web_client.py#L211

It appears that updating a stream config via RTSPtoWeb's API causes any currently active streams to drop. I can raise a linked issue over there, but I'm wondering if a quick win can be made here: since we already have the result from `list_streams`, would it be possible to do a comparison of the desired config vs. the current config, and skip the update altogether if none is necessary? Hopefully, this should prevent streams from dropping unnecessarily.

I'd offer to raise a PR, but I'm not a Python guy.


Discussion / hints:
Makes sense, and does not seem TOO hard.
I was able to sidestep this issue temporarily by cloning the `rtsp_to_webrtc` Integration and modifying it so that it effectively skips an insert/update operation:

https://github.com/wms/rtsp_to_webrtc_wms/blob/master/__init__.py#L70-L76

This means that:

1. Each stream must be manually configured in RTSPtoWeb
2. Each stream must have a name that matches the Entity ID
3. The RTSP URL in the HA config itself is effectively ignored

However, this is a compromise I can deal with, as my feeds are now running with near-zero latency and can watch the feeds from anywhere without breaking existing connections :)
I've applied @wms changes and it works for me as a temporary fix.

Fix patch:
diff --git a/rtsp_to_webrtc/web_client.py b/rtsp_to_webrtc/web_client.py
index 248e02b..f760719 100644
--- a/rtsp_to_webrtc/web_client.py
+++ b/rtsp_to_webrtc/web_client.py
@@ -208,7 +208,10 @@ async def offer_stream_id(
             },
         }
         if stream_id in streams:
-            await self.update_stream(stream_id, stream_payload)
+            if streams[stream_id].get("channels", {}).get("0", {}).get("url") == rtsp_url:
+                _LOGGER.debug("Not updating stream url since already set")
+            else:
+                await self.update_stream(stream_id, stream_payload)
         else:
             await self.add_stream(stream_id, stream_payload)
         return await self.webrtc(stream_id, "0", offer_sdp)


Test patch:
diff --git a/tests/test_web_client.py b/tests/test_web_client.py
index ecfafd6..e5247e2 100644
--- a/tests/test_web_client.py
+++ b/tests/test_web_client.py
@@ -21,11 +21,11 @@
     "channels": {
         "0": {
             "name": "ch1",
-            "url": "rtsp://example",
+            "url": RTSP_URL,
         },
         "1": {
             "name": "ch2",
-            "url": "rtsp://example",
+            "url": RTSP_URL,
         },
     },
 }
@@ -405,14 +405,14 @@ async def test_offer_update_stream(cli: TestClient) -> None:
             }
         )
     )
-    # Add stream
+    # Edit stream
     cli.server.app["response"].append(aiohttp.web.json_response(SUCCESS_RESPONSE))
     # Offer
     cli.server.app["response"].append(aiohttp.web.Response(body=ANSWER_PAYLOAD))
 
     client = WebClient(cast(ClientSession, cli))
 
-    answer_sdp = await client.offer_stream_id("demo1", OFFER_SDP, RTSP_URL)
+    answer_sdp = await client.offer_stream_id("demo1", OFFER_SDP, f"{RTSP_URL}?example")
     assert answer_sdp == ANSWER_SDP
     requests = cli.server.app["request"]
     assert requests == [
@@ -463,3 +463,34 @@ async def test_offer_channel_data(cli: TestClient) -> None:
             "name": "demo1",
         }
     ]
+
+
+async def test_offer_update_no_op(cli: TestClient) -> None:
+    """Test that an offer is a no-up when stream matches."""
+    assert isinstance(cli.server, TestServer)
+    # List call
+    cli.server.app["response"].append(
+        aiohttp.web.json_response(
+            {
+                "status": 1,
+                "payload": {
+                    "demo1": STREAM_1,
+                },
+            }
+        )
+    )
+    # Offer
+    cli.server.app["response"].append(aiohttp.web.Response(body=ANSWER_PAYLOAD))
+
+    client = WebClient(cast(ClientSession, cli))
+
+    answer_sdp = await client.offer_stream_id("demo1", OFFER_SDP, RTSP_URL)
+    assert answer_sdp == ANSWER_SDP
+    requests = cli.server.app["request"]
+    assert requests == [
+        "/streams",
+        "/stream/demo1/channel/0/webrtc",
+    ]
+
+
+