Streaming, Better Example Client, and Cleanup
Table of Contents
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.
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.