Skip to main content
  1. Devlogs/

Streaming, Better Example Client, and Cleanup

·8 mins

The main focus of the past two weeks has been on creating a better Sample Client with an actual user interface for demonstration purposes, getting this example client to support song streaming, and cleaning up the codebase for the SongWeb reference library. A working, albeit limited, TUI has been created which loads and displays songs from a local directory, automatically establishes connections with other peers in the network, sends song requests to them, and can create a RTSP stream to stream the requested song to the destination peer.

Streaming #

While the SongWeb protocol itself is not meant to provide any streaming capabilities directly, as its purpose is solely for coordinating playlist state and song requests between peers, one of the design goals was nonetheless to provide the capacity for clients to request and coordinate streams between themselves. This is done through the Request::Stream message, replacing the placeholder Request::SongFile which would be for requesting a song file directly. The Request::Stream message is sent by a hosting peer to the peer that requested the song it is trying to play, if it does not have access to a local copy. The receiver of this request can then make a stream available to the requester and send a Response::StreamInfo message containing the information needed to connect to the stream. This is meant to be protocol-agnostic, so peers can use any streaming protocol they wish, and then peers can announce which protocols they support. For simplicity, the example client uses RTSP for streaming, as GStreamer is being used for the player backend and it provides drop-in support for playing from an RTSP stream. The GST-RTSP-Server also provides an easy way to create a RTSP server. For a real-world application, however, a protocol that better handles poor network conditions would be more appropriate, as RTSP requires uninterrupted transmission for best performance. For demonstration purposes, however, it is sufficient to demonstrate the intended use of the SongWeb.

In setting this up for the example client, I realized that it would be useful to provide additional capabilities for peers to communicate their streaming capabilities, as currently peers only know what protocol the stream will be when they receive the Response::StreamInfo message after requesting a stream. Since the Request::Stream message is intended to be sent just before the requester wants to start playing the song, the requester only knows whether it can play the stream right when it needs to play it.

It would be instead be useful for the requester to know in advance whether it can actually play the stream, so it can deny adding the song to it’s playlist if it can’t play it. As such, it would therefore be useful to have some sort of additional message that peers can send to request the streaming capabilities of another peer, or for peers to announce their streaming capabilities to the network. Planning and design for this capability is ongoing.

Regardless, in the current iteration of the protocol/library, the capability for peers to stream songs to each other is now functional, marking the completion of the last major deliverable for the project, leaving the remaining time to be largely focused on cleanup and documentation.

Sample Client #

The example client is now a user-interactable program in the form of a TUI application, rather than what was being used before, which was just a program that launched two headless clients that ran automatic actions and printed status messages to the console. The old one was mostly for testing purposes, but the new example client is meant to be a demonstration of how a real application would actually make use of the SongWeb library. The TUI is built using the ratatui crate, which provides a simple framework for rendering terminal interfaces and responding to terminal events. In it’s current state it is still quite rough and mostly for testing, as the actual playback of songs and playlist management is extremely rudimentary and mostly copied in from one of my other projects, Recordbox, and work is ongoing to clean up the code and generally make it more presentable.

The current iteration can be seen in the following screenshots of two instances of the client, where the first is hosting it’s playlist, and the second has subscribed to the first and submitted songs to it. As previously mentioned, and as can clearly be seen, the interface doesn’t provide much (or any) feedback on the status of the songs being played, or any status updates at all, so it’s not really that obvious what’s going on. Work to improve this is ongoing, but its not quite functional enough to demonstrate in this update. Once the improvements are finished, a proper live demonstration will be able to be provided.

Example Client 1
Client 1: It can be seen that it’s playlist contains songs that are not present in it’s library

Example Client 2
Client 2: It can be seen that it is subscribed to client 1 and is displaying the state of Client 1’s playlist

Cleanup #

With the creating of this example client, adjustments to the interface of the SongWeb library were made as well, to smooth out and/or fix rough edges that were encountered while creating the client, as it is otherwise difficult to discover these issues without practical use of the library. This included adjustments to the methods required to be implemented by the Client trait, and moving some responsibilities out of the library’s Server struct and into the implementing Client struct. The most notable responsibility shifted out of the purview of the library was the responsibility of tracking which peers requested which song, as this is something that is more appropriately handled by the client application. Previously, the Server would maintain an internal record of peers and the accepted song requests they made, so that it could inform the Client which songs needed to be removed from the playlist when a Peer disconnects. The rational for this was that if a peer leaves the network, all songs it requested that aren’t available locally need to be removed from the playlist because they are no longer accessible. While that still holds, it instead made more sense to instead inform the Client when a peer becomes unavailable, rather than a song becoming unavailable, and have the Server just additionally inform the Client of the ID of the requester when informing it of a new song request. This allows the Client to track which peers requested songs, and then when a peer disconnects, the Server just provides the ID and the client can remove all songs it has with that ID.

As a result of this, the only internal state the Server now maintains is whether or not the local playlist is being hosted, and a record of which peers are subscribed to the local playlist. The record of which peers are subscribed is done so that song requests from peers which are not subscribed are automatically denied. Whether this is something that should be enforced is something I am still considering, but to me it makes sense for a peer to not be able to request to add songs to a playlist that it is not monitoring the state of. If the requests are expanded in the future to allow for more advanced editing of the playlist this would be especially important, which is mainly the reason why this limitation is imposed.

Remaining Work #

At this point, the main focus is going to be on cleaning up the codebase, finishing the improvements to the example client, and writing documentation. The library is basically feature complete at this point, but there are additional considerations that need to be made and specific interactions or edge cases that need to be considered. There is also some testing/experimentation that should be done regarding the use of Gossipsub for the broadcast messages, as at the moment I think it might be possible for peers to remain subscribed to the wrong topics, or just in generally I don’t necessarily think the Server is doing enough checks on received Gossipsub messages to make sure they are coming from the right peer. For example, only the hosting peer is meant to post playlist status updates on the topic for said playlist, but by default Gossipsub allows any peer to post to any topic, so theoretically a peer could post a fake status update to a playlist it’s not hosting if it knows the topic name, which is currently just the Peer ID of the host. However, libp2p’s Gossipsub implementation does return the originating peer ID for messages, so this should just be as simple as checking that the peer ID of the sender matches the topic name.

Otherwise, the project is on track to be fully completed by the next and final biweekly update, and at this point I also believe that I have effectively implemented all the functionality that I originally planned for, albeit some of it (the lowest priority stuff, like the example client) still in a rough state. Once the refinements are complete, the project will be fully presentable and meeting the design requirements of the project proposal.