chore: rename project and app to terebi
This commit is contained in:
@@ -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 $@
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
Reference in New Issue
Block a user