Skip to content

Enhance audio streaming with AAC support and improved playback - #229

Open
BazZziliuS wants to merge 22 commits into
jacksonhardaway:1.21.xfrom
BazZziliuS:1.21.x
Open

BazZziliuS wants to merge 22 commits into
jacksonhardaway:1.21.xfrom
BazZziliuS:1.21.x

Conversation

@BazZziliuS

Copy link
Copy Markdown

No description provided.

close() joined the read future, rethrowing the reader's CompletionException.
Vanilla Channel.destroy() only catches IOException, so the exception escaped
mid-release and left a stale channel handle ("Tried to release unknown channel").
AsyncInputStream returned EOF whenever its buffer was momentarily empty, even
though the read thread was still running. Radios play with looping enabled, so
vanilla LoopingAudioStream treated that EOF as the end of the track and reset
to the start of the stream, replaying everything since the radio started.

- AsyncInputStream now waits (up to 3s) for data instead of reporting EOF early
- The read thread stops on real EOF instead of spinning until closed
- Live streams are never wrapped in LoopingAudioStream, which also buffered the
  entire stream in memory
Decodes .aac/.aacp radio streams with JAAD. AAC is detected before MP3 using a
strict check for two consecutive ADTS headers in the first 4KB, since the MP3
decoder can read past the 8KB mark while searching for a frame and break the
reset for later formats.

Also pin jlayer below 1.0.6, which removed Obuffer and broke compilation.
de.sfuhrm:jaad 0.8.7 decoded most frames of real radio streams as silence
(359 of 419 frames on Radio Record's 96k AAC stream), which made playback
choppy. The tianscar fork decodes the same stream without gaps.
A search button next to the URL field opens a screen that searches for music
and puts the chosen URL back into the field.

- Radios search stations from the Radio Browser catalog (MP3 and AAC streams
  only) and can switch to SoundCloud
- Etching tables search SoundCloud tracks, skipping tracks that are blocked,
  preview-only or have no format Etched can play

Searching happens entirely on the client, so no network changes are needed.
SoundCloud no longer serves MP3 for most tracks: the MP3 HLS transcoding it
still lists returns 404, and only AAC in fragmented MP4 HLS works. Etched only
tried the MP3 one, so every track failed with "Failed to connect to SoundCloud
API".

- Read AAC frames from fragmented MP4 (init segment + moof/mdat fragments)
- Prefer the AAC HLS transcoding and fall back to other formats on errors
- Accept HLS playlist version 7, which SoundCloud uses for fMP4 segments
…rvers

Client:
- Open audio streams on an IO thread. Sources are cached per URL, so replaying
  a URL completed the future immediately and opened the stream on the render
  thread, freezing the game while connecting to a radio or downloading the
  first HLS segments
- Don't wait for the read thread in AsyncInputStream.close(), which could block
  the sound thread on a stalled connection
- Add timeouts to radio stream connections
- Report read errors that happen while waiting for data instead of a clean EOF

Server:
- Log why the SoundCloud client id could not be retrieved instead of silently
  continuing with client_id=null, and add timeouts to those requests
- Don't cache failed track lookups for 15 minutes
- Create the disc without track info when the music service can't be reached
  from the server, since players resolve the URL themselves when playing
The server looked up track info itself, which fails when the server host
can't reach the music service (for example when soundcloud.com is blocked),
even though players can. Clients now resolve the tracks and send them to the
server with a new optional packet.

- The server only keeps plain text from the client, checks the track URLs and
  limits the number and length of tracks
- Lookup errors from the client are shown in the etching table as before
- Clients without the packet keep the old behavior, with the server resolving
  the tracks itself
- Yandex Music: tracks, albums and playlists. Plays 30-second previews unless
  an OAuth token is set in the client config
- Audius: tracks, albums and playlists through the public API, plus Audius
  search in etching tables and radios
- Internet Archive: plays all MP3 files of an item, or a single file when the
  link points to one
- Google Drive and Dropbox share links are turned into direct download links
  when checking and downloading them, so the music label's title and artist
  are kept
- Etching tables and radios list the supported sites in their tooltip while
  Shift is held
- Decode radio streams and SoundCloud HLS tracks on a separate thread. The
  sound thread read them straight from the network, and the main thread waits
  for the sound thread whenever any sound plays, so everyone near an AAC+
  radio froze. Silence now plays if the network falls behind
- Download HLS segments a few at a time ahead of playback instead of all at
  once. An 8-hour SoundCloud track started thousands of downloads, flooded
  the connection and timed the player out again on every rejoin
- Loop tracks by reopening them instead of keeping the whole stream in memory
Radios mounted on trains and other contraptions keep playing, follow the
contraption and can be retuned while moving. Also stop a radio's sound when
its block entity is removed without the block, as Create does on assembly.
- Album jukeboxes and jukeboxes keep playing on Create contraptions
- Stop jukebox songs lingering where a contraption picked the block up
- Account for Sable sub-levels in distance checks using Sable Companion
- Add volume and radius sliders to the radio and album jukebox menus.
  Both change while playing, so ticking sounds now fade with distance
  themselves instead of relying on the range set when they start
- Only restart a radio stream when its station or power changes
Discs, volume and radius of album jukeboxes on Create contraptions can be
changed while moving. Mounted block data is now synced through one packet
that merges only the changed keys.

This branch has not been deployed

No deployments
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.

1 participant