Lab Directory

Apps, tools and experiments, built in the open.

Logs 12

LAB-015keiver/tomotv
Playback engine · Dolby audio

Dolby Atmos and TrueHD on Apple TV

Tomo TV plays Dolby Atmos on Apple TV by copying Dolby Digital Plus untouched, so the object data reaches the receiver. A 7.1 Blu-ray track needed one more step: its Atmos flag sits in a dependent frame the MP4 muxer never read, so the player saw plain multichannel. TrueHD and DTS-HD decode to lossless FLAC and keep every channel.

6 min read
LAB-014keiver/tomotv
Release tooling · App Store screenshots

iPhone Duo App Store screenshots, automated

Tomo TV's screenshot script builds its iPhone Duo App Store screenshots from two hand-taken captures per shot: the closed screen and the open one. It draws both at the same scale on a 2853 by 2007 canvas, inside outlines traced from the simulator's own screen shapes. Nine shots take 17 captures, because the player shows only the open screen.

6 min read
LAB-013keiver/tomotv
Music · Lyrics

Synced Jellyfin lyrics on Apple TV

Tomo TV shows synced Jellyfin lyrics on Apple TV, iPhone, iPad and Mac. When the server has none it asks lrclib.net, then Apple's on-device speech transcriber times the words, but only when it heard at least half of them. On test songs that passed, the heard start of each line fell a median 0.34 seconds from lrclib.net's own line time, once each song's overall offset was removed.

8 min read
LAB-010keiver/tomotv
Player · Up Next

Up Next on Apple TV with AVContentProposal

Tomo TV on Apple TV uses AVContentProposal, AVKit's built-in card for offering the next episode. Leaving didReject unimplemented makes AVKit dismiss the player view controller. The card's time must be one playback really passes: the outro's end sat 44 ms past the player's own duration, so the card never appeared.

6 min read
LAB-009keiver/tomotv
Live TV · Channel wall

Live TV channel previews that loop

Tomo TV loops a five-second live TV channel preview clip on every card of its channel wall. Each channel is opened once: the first keyframe becomes the card still and the next five seconds are encoded on device to 360p at 1 Mbps. Clips are written as a temporary file and renamed only when whole. A red badge reads LIVE for 15 seconds after the picture changes, and the strip under the card turns red, then fades to black over two minutes. The player pool starts at 12 on Apple TV and lowers itself when the hardware decoder reports busy.

5 min read
LAB-008keiver/tomotv
Playback engine · Posters

Generating video thumbnails on device

Tomo TV makes video poster cards from the file itself on Apple TV and iPhone. A batch of keyframes from the 10% mark is scored by color histogram and the one nearest the batch average wins, the measure FFmpeg's thumbnail filter uses. Dark and flat frames are rejected by luma and contrast thresholds, letterbox bars are cropped using the union of every frame's content box, and decodes run one at a time from a low-priority queue, newest card first, cancelling jobs for cards that scroll off screen.

5 min read
LAB-006keiver/tomotv
Playback engine · Adaptive streaming

When should a Jellyfin client transcode?

Tomo TV treats the Jellyfin server as a file host. The server transcodes only when the measured link cannot carry the file at 1.2 times its bitrate. The engine measures the link by reading the file it is about to play, on three connections, and tracks it with two exponential averages so a drop is followed at once and a rise waits until it holds.

6 min read
LAB-005keiver/tomotv
Live TV · Channel flipping

Channel surfing on Apple TV with AVKit

Tomo TV on Apple TV uses AVKit's own channel-skip delegate to flip live TV channels. Neighbor channels run in the engine before you need them, so a hot flip takes 0.84 to 1.11 seconds against 11 to 16 seconds cold. Neighbors run for 30 seconds after each flip and yield the moment the playing channel needs the bandwidth.

4 min read
LAB-004keiver/tomotv
Playback engine · Subtitles

Why Jellyfin subtitles delay playback

Films with embedded subtitles took between 5.7 and 11.8 seconds to start in Tomo TV on Apple TV, because the player held while Jellyfin ran FFmpeg over the whole file before answering. The app now decodes the same packets it was already reading and cuts them into segments alongside the video. A CRLF quirk in Swift surfaced after real Aegisub files revealed styling that our synthetic fixtures never produced.

4 min read
LAB-003keiver/tomotv
Playback engine · Scrub thumbnails

Apple TV scrub thumbnails without trickplay

Tomo TV, a Jellyfin client for Apple TV, builds scrub thumbnails from each video file's own keyframes. The device serves them as an HLS I-frame rendition, the track tvOS scrubs with, so no server-side trickplay is needed. Jellyfin's JPEG trickplay images were tried first: AVPlayer ignored one codec label and stalled on the other.

4 min read
LAB-002keiver/tomotv
Playback engine · Image subtitles

PGS subtitles on Apple TV without transcoding

Tomo TV decodes PGS, VobSub, DVB and XSUB on the Apple TV without help from the server. The engine reads each subtitle stream while demuxing the file, writing display sets to disk as PNGs. A React Native overlay draws them over the native player, inside the content area the controls avoid.

5 min read
LAB-001keiver/tomotv
Playback engine · Dolby Vision

Dolby Vision Profile 7 on Apple TV

Tomo TV converts Dolby Vision Profile 7 UHD disc rips to Profile 8.1 on Apple TV by rewriting four fields in each frame's RPU NAL, without decoding the base layer. The enhancement layer is dropped, while the mapping curves, MMR constants and color metadata travel unchanged. AVPlayer then draws what Console.app confirms as hdrMode = Dolby.

5 min read

apps 5

tools 6

Archive 13

Everything here is provided as is, without warranty of any kind. Use it at your own risk.