chore: rename project and app to terebi

This commit is contained in:
2026-10-10 19:25:52 +02:00
parent 33858bd8ce
commit 681a7be6f7
4 changed files with 16 additions and 16 deletions
+3 -3
View File
@@ -7,10 +7,10 @@ ARCH := $(shell uname -m)
CXXFLAGS := -std=c++20 -mmacosx-version-min=15.0 -O2 -g -Wall -Wextra -Wno-unused-parameter $(FF_CFLAGS) CXXFLAGS := -std=c++20 -mmacosx-version-min=15.0 -O2 -g -Wall -Wextra -Wno-unused-parameter $(FF_CFLAGS)
FRAMEWORKS := -framework AppKit -framework AudioToolbox -framework CoreAudio -framework Metal -framework MetalKit -framework QuartzCore FRAMEWORKS := -framework AppKit -framework AudioToolbox -framework CoreAudio -framework Metal -framework MetalKit -framework QuartzCore
OBJECTS := .build/player.o .build/Output.o OBJECTS := .build/player.o .build/Output.o
APP := .build/Player.app APP := .build/terebi.app
.PHONY: all run check check-audio .PHONY: all run check check-audio
all: $(APP)/Contents/MacOS/Player $(APP)/Contents/Info.plist all: $(APP)/Contents/MacOS/terebi $(APP)/Contents/Info.plist
.build: .build:
mkdir -p $@ mkdir -p $@
@@ -21,7 +21,7 @@ all: $(APP)/Contents/MacOS/Player $(APP)/Contents/Info.plist
.build/Output.o: Sources/macOS/Output.mm Sources/Core/output.hpp Makefile | .build .build/Output.o: Sources/macOS/Output.mm Sources/Core/output.hpp Makefile | .build
$(CXX) $(CXXFLAGS) -fobjc-arc -c $< -o $@ $(CXX) $(CXXFLAGS) -fobjc-arc -c $< -o $@
$(APP)/Contents/MacOS/Player: Sources/App/PlayerApp.swift $(OBJECTS) Makefile $(APP)/Contents/MacOS/terebi: Sources/App/TerebiApp.swift $(OBJECTS) Makefile
mkdir -p $(APP)/Contents/MacOS mkdir -p $(APP)/Contents/MacOS
$(SWIFT) -target $(ARCH)-apple-macosx15.0 -swift-version 6 -parse-as-library -O -g -import-objc-header Sources/Core/player_macos.h $< $(OBJECTS) $(FF_LIBS) $(FRAMEWORKS) -lc++ -o $@ $(SWIFT) -target $(ARCH)-apple-macosx15.0 -swift-version 6 -parse-as-library -O -g -import-objc-header Sources/Core/player_macos.h $< $(OBJECTS) $(FF_LIBS) $(FRAMEWORKS) -lc++ -o $@
+3 -3
View File
@@ -1,4 +1,4 @@
# Player # terebi
A personal native macOS player whose playback code is small enough to own and modify. FFmpeg reads and decodes media; our C++ worker owns playback. SwiftUI owns the controls, Core Audio owns audio output, and Metal presents video. A personal native macOS player whose playback code is small enough to own and modify. FFmpeg reads and decodes media; our C++ worker owns playback. SwiftUI owns the controls, Core Audio owns audio output, and Metal presents video.
@@ -12,7 +12,7 @@ make
make run make run
``` ```
The local app is `.build/Player.app`. It links the installed FFmpeg libraries; it is not a standalone distributable bundle. After upgrading FFmpeg, rebuild with `make -B`. The local app is `.build/terebi.app`. It links the installed FFmpeg libraries; it is not a standalone distributable bundle. After upgrading FFmpeg, rebuild with `make -B`.
Open a local file with **⌘O**, drop it onto the window, or use Finder's **Open With**. Successfully opened files start playback automatically, including when replacing the current file. **⇧⌘W** closes the file; closing the window quits and joins the worker. Open a local file with **⌘O**, drop it onto the window, or use Finder's **Open With**. Successfully opened files start playback automatically, including when replacing the current file. **⇧⌘W** closes the file; closing the window quits and joins the worker.
@@ -49,7 +49,7 @@ Only implemented actions have controls; there are no placeholder playlist, subti
- `Sources/Core/player.h`: the C interface and authoritative state snapshot. Playback commands enqueue work; poll state for completion. `seek_pending` is set synchronously when a seek or frame step is accepted and clears when all queued seeks/steps on that timeline finish. `player_step_frame` accepts only `-1` or `+1`, requires ready video, and publishes the selected frame's timestamp as `position`. Volume set/get is synchronous and uses a lock-free target gain. Callers own the opaque handle and must stop using it before destruction. - `Sources/Core/player.h`: the C interface and authoritative state snapshot. Playback commands enqueue work; poll state for completion. `seek_pending` is set synchronously when a seek or frame step is accepted and clears when all queued seeks/steps on that timeline finish. `player_step_frame` accepts only `-1` or `+1`, requires ready video, and publishes the selected frame's timestamp as `position`. Volume set/get is synchronous and uses a lock-free target gain. Callers own the opaque handle and must stop using it before destruction.
- `Sources/Core/player.cpp`: single demux/decode worker, bounded buffers, timestamps, audio-master synchronization, paused previews, exact frame stepping, decoder/resampler drain, seeking, errors, and shutdown. A seek stops output and flushes all old timeline state before resuming. - `Sources/Core/player.cpp`: single demux/decode worker, bounded buffers, timestamps, audio-master synchronization, paused previews, exact frame stepping, decoder/resampler drain, seeking, errors, and shutdown. A seek stops output and flushes all old timeline state before resuming.
- `Sources/Core/output.hpp` and `Sources/macOS/Output.mm`: native output contract, allocation-free/nonblocking Core Audio callback and bounded audio ring, latency-aware presentation clock, retained frame mailbox, and Metal YUV conversion. The view can outlive the player safely. - `Sources/Core/output.hpp` and `Sources/macOS/Output.mm`: native output contract, allocation-free/nonblocking Core Audio callback and bounded audio ring, latency-aware presentation clock, retained frame mailbox, and Metal YUV conversion. The view can outlive the player safely.
- `Sources/App/PlayerApp.swift`: MainActor state polling, file panel/Finder/drop handling, autoplay, video host, AppKit glass and native slider cells, floating controls, visibility, video-fitted sizing, and native window integration. The shared `PlayerModel.open` path enqueues play after an accepted open, on the new file's command timeline; the C interface still opens paused for callers that need explicit playback control. - `Sources/App/TerebiApp.swift`: MainActor state polling, file panel/Finder/drop handling, autoplay, video host, AppKit glass and native slider cells, floating controls, visibility, video-fitted sizing, and native window integration. The shared `PlayerModel.open` path enqueues play after an accepted open, on the new file's command timeline; the C interface still opens paused for callers that need explicit playback control.
- `Makefile`: compiler/linker commands and app bundle construction. No generated Xcode project or additional build dependency. - `Makefile`: compiler/linker commands and app bundle construction. No generated Xcode project or additional build dependency.
## Runnable check ## Runnable check
+3 -3
View File
@@ -2,9 +2,9 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleExecutable</key><string>Player</string> <key>CFBundleExecutable</key><string>terebi</string>
<key>CFBundleIdentifier</key><string>local.personal.player</string> <key>CFBundleIdentifier</key><string>local.personal.terebi</string>
<key>CFBundleName</key><string>Player</string> <key>CFBundleName</key><string>terebi</string>
<key>CFBundlePackageType</key><string>APPL</string> <key>CFBundlePackageType</key><string>APPL</string>
<key>CFBundleShortVersionString</key><string>0.1</string> <key>CFBundleShortVersionString</key><string>0.1</string>
<key>LSMinimumSystemVersion</key><string>15.0</string> <key>LSMinimumSystemVersion</key><string>15.0</string>
@@ -139,7 +139,7 @@ final class PlayerModel: ObservableObject {
isSeeking = false isSeeking = false
seekPending = false seekPending = false
window?.representedURL = nil window?.representedURL = nil
window?.title = "Player" window?.title = "terebi"
revealControls() revealControls()
} }
@@ -179,7 +179,7 @@ final class PlayerModel: ObservableObject {
func attachWindow(_ window: NSWindow) { func attachWindow(_ window: NSWindow) {
self.window = window self.window = window
window.title = fileName ?? "Player" window.title = fileName ?? "terebi"
updateWindowState() updateWindowState()
fitVideoWindow() fitVideoWindow()
} }
@@ -451,11 +451,11 @@ private struct PlayerContent: View {
} else { } else {
Image(systemName: "play.rectangle.fill").font(.system(size: 14)) Image(systemName: "play.rectangle.fill").font(.system(size: 14))
} }
Text(model.fileName ?? "Player") Text(model.fileName ?? "terebi")
.font(.system(size: 13, weight: .semibold)) .font(.system(size: 13, weight: .semibold))
.lineLimit(1) .lineLimit(1)
.truncationMode(.middle) .truncationMode(.middle)
.help(model.fileName ?? "Player") .help(model.fileName ?? "terebi")
Spacer(minLength: 12) Spacer(minLength: 12)
iconButton(model.isOnTop ? "pin.fill" : "pin", iconButton(model.isOnTop ? "pin.fill" : "pin",
model.isOnTop ? "Stop Keeping Window on Top" : "Keep Window on Top", action: model.toggleOnTop) model.isOnTop ? "Stop Keeping Window on Top" : "Keep Window on Top", action: model.toggleOnTop)
@@ -601,7 +601,7 @@ private struct PlayerContent: View {
VStack(spacing: 14) { VStack(spacing: 14) {
Image(systemName: "play.rectangle").font(.system(size: 56, weight: .ultraLight)) Image(systemName: "play.rectangle").font(.system(size: 56, weight: .ultraLight))
.foregroundStyle(.white.opacity(0.65)) .foregroundStyle(.white.opacity(0.65))
Text("Player").font(.system(size: 22, weight: .medium)) Text("terebi").font(.system(size: 22, weight: .medium))
Text("Drop a media file here").font(.callout).foregroundStyle(.secondary) Text("Drop a media file here").font(.callout).foregroundStyle(.secondary)
Button("Open File…", action: model.openPanel) Button("Open File…", action: model.openPanel)
.buttonStyle(.bordered).controlSize(.large).disabled(!model.canOpen) .buttonStyle(.bordered).controlSize(.large).disabled(!model.canOpen)
@@ -808,12 +808,12 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
@main @main
@MainActor @MainActor
struct PlayerApp: App { struct TerebiApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) private var delegate @NSApplicationDelegateAdaptor(AppDelegate.self) private var delegate
@StateObject private var model = PlayerModel.shared @StateObject private var model = PlayerModel.shared
var body: some Scene { var body: some Scene {
Window("\(model.fileName ?? "Player")", id: "player") { Window("\(model.fileName ?? "terebi")", id: "terebi") {
PlayerContent(model: model) PlayerContent(model: model)
} }
.defaultSize(width: 900, height: 560) .defaultSize(width: 900, height: 560)