Skip to content

Feat/multicast - #1

Merged
bojackduy merged 15 commits into
mainfrom
feat/multicast
Jun 17, 2026
Merged

Feat/multicast#1
bojackduy merged 15 commits into
mainfrom
feat/multicast

Conversation

@bojackduy

Copy link
Copy Markdown
Collaborator

This pull request refactors the Client.py to support video streaming exclusively via UDP multicast, removes legacy TCP and quality selection code, and introduces a multicast-based state synchronization mechanism. The UI and client logic are updated to reflect these changes, ensuring that all clients join the same multicast group for both media and state updates. The code is also simplified to remove unnecessary options and methods related to TCP and quality selection.

Multicast Streaming and State Synchronization:

  • The client now always streams video using UDP multicast, joining a shared group defined in Config.py. All TCP-related code and quality selection logic have been removed. The UI is updated to reflect this, and the setup dialog now only informs users about the multicast stream. (Client.py, removal of listenRtpWithTCP, chooseQuality, and related UI; addition of chooseMulticastStream, openRtpPort, and associated changes) [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]

State Multicast Listener and Synchronization:

  • Introduces a new state multicast listener: the client joins a state multicast group and listens for official server state updates (e.g., PLAYING, PAUSED, READY, STOPPED). The client UI and playback state are updated in real time based on these multicast packets, ensuring all clients stay in sync with the server's stream state. (startStateListener, listenStateMulticast, applyServerState, closeStateListener, and integration in constructor and teardown) [1] [2]

UI/UX Simplification and Robustness:

  • The setup, play, and pause logic is simplified to reflect the new multicast-only model. The play/pause buttons are enabled/disabled appropriately to prevent duplicate actions, and state transitions are now more robust thanks to the server-driven state updates. (setupMovie, pauseMovie, playMovie, startPlaybackPipeline, and related UI changes) [1] [2] [3] [4] [5] [6] [7]

Code Cleanup:

  • Removes all code related to TCP streaming, HD/SD quality selection, and associated UI elements, making the codebase easier to maintain and less error-prone. (listenRtpWithTCP, recv_all, radio button UI, and related logic) [1] [2]

Configuration Centralization:

  • All relevant multicast addresses, ports, and default media file names are now imported from Config.py, centralizing configuration and making it easier to update deployment parameters. [1] [2]

These changes modernize the client, ensuring all clients receive the same stream and state information in real time, while simplifying the user experience and internal logic.

Copilot AI review requested due to automatic review settings June 17, 2026 17:46
@bojackduy

Copy link
Copy Markdown
Collaborator Author

good to me

@bojackduy
bojackduy merged commit 11318ba into main Jun 17, 2026
1 check passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the video streaming app toward a UDP-multicast-first architecture by introducing a shared multicast stream manager on the server and adding a multicast-based state synchronization channel that drives client UI/playback behavior.

Changes:

  • Added shared multicast configuration/constants and a state packet format for UDP multicast announcements.
  • Introduced MulticastStreamManager to own the shared RTP multicast sender and official stream state/versioning.
  • Updated server/client control flow and UI to align with multicast delivery and server-driven state synchronization (plus documentation updates).

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
StatePacket.py Adds JSON-based encode/decode helpers for multicast state announcements.
MulticastStreamManager.py New shared manager for RTP multicast sending and state multicast announcements.
Config.py Centralizes multicast groups/ports/TTL and default media file name.
ServerWorker.py Delegates UDP media control to MulticastStreamManager and manages multicast client registration.
Server.py Creates and injects a shared MulticastStreamManager into per-client workers.
ClientLauncher.py Simplifies CLI args; uses DEFAULT_MEDIA_FILE and removes RTP port arg.
Client.py Joins RTP/state multicast groups, listens for official state updates, and simplifies setup/play/pause around multicast.
README.md Updates documentation and diagrams to reflect multicast-only media delivery and shared state control.
doc/multicast_design.md Adds a checkpoint design doc describing current vs target multicast behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ServerWorker.py
Comment on lines +63 to 67
previousTransport = self.clientInfo.get('transport')
transport_line = request[2]
self.clientInfo['transport'] = 'UDP'
if 'TCP' in transport_line:
self.clientInfo['transport'] = 'TCP'
Comment thread Client.py
Comment on lines +588 to +594
try:
self.rtpSocket.bind(('', RTP_MULTICAST_PORT))
mreq = socket.inet_aton(RTP_MULTICAST_GROUP) + socket.inet_aton('0.0.0.0')
self.rtpSocket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
print(f"Joined RTP multicast group {RTP_MULTICAST_GROUP}:{RTP_MULTICAST_PORT}")
except Exception as e:
tkMessageBox.showwarning('Unable to Join Multicast', f'Unable to join RTP multicast group {RTP_MULTICAST_GROUP}:{RTP_MULTICAST_PORT}: {e}')
Comment thread Client.py

def listenStateMulticast(self):
"""Receive official state multicast packets and apply them on the UI thread."""
while not self.stateEvent.isSet():
Comment thread MulticastStreamManager.py
frameSize = len(data)
bytesSent = 0

while bytesSent < frameSize and not self.stopEvent.isSet():
Comment thread MulticastStreamManager.py
Comment on lines +161 to +168
data = videoStream.nextFrame()
if not data:
with self.lock:
self.state = STOPPED
self.worker = None
self.stopEvent.set()
self._send_state_multicast(STOPPED)
break
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants