Skip to content

Commit ac91e2d

Browse files
committed
docs: add complete video player example and update roadmap
Add end-to-end commented example app to VIDEO_PLAYBACK.md showing file picker, playback, overlay, state tracking, and cleanup. Add VideoControl to Recently Shipped in ROADMAP.md.
1 parent eba67b5 commit ac91e2d

2 files changed

Lines changed: 122 additions & 0 deletions

File tree

ROADMAP.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ SharpConsoleUI is actively maintained and driven by real-world usage in producti
1919
- StatusBarControl — per-window status bar with left/center/right zones, clickable shortcut+label items, markup support, optional above line, theme integration
2020
- LineGraphControl — multi-series line graphs with braille (2×4 pixel grid) and ASCII rendering modes, color gradients, Y-axis labels, live data updates
2121
- SliderControl / RangeSliderControl — horizontal and vertical value sliders with keyboard, mouse drag, step/large-step, min/max labels, and dual-thumb range selection with MinRange enforcement
22+
- VideoControl — terminal video playback via FFmpeg with half-block, ASCII and braille render modes, overlay status bar, dynamic resize, looping
2223

2324
## Next
2425

docs/VIDEO_PLAYBACK.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,3 +339,124 @@ The Video Player demo falls back to `sample.mp4` if the user cancels the file pi
339339

340340
For automated testing, a deterministic test pattern is embedded as a resource:
341341
- `SharpConsoleUI/Resources/test_sample.mp4` — 3s, 160×120, 10fps (49 KB)
342+
343+
## Complete Example — Video Player App
344+
345+
A full, runnable console application that opens a file picker and plays the selected video. Copy this into a new project to get started immediately.
346+
347+
```bash
348+
# Create a new project and add SharpConsoleUI
349+
dotnet new console -n MyVideoPlayer
350+
cd MyVideoPlayer
351+
dotnet add package SharpConsoleUI
352+
```
353+
354+
```csharp
355+
// Program.cs — Complete terminal video player
356+
357+
using SharpConsoleUI;
358+
using SharpConsoleUI.Builders;
359+
using SharpConsoleUI.Controls;
360+
using SharpConsoleUI.Dialogs;
361+
using SharpConsoleUI.Drivers;
362+
using SharpConsoleUI.Helpers;
363+
using SharpConsoleUI.Video;
364+
365+
// 1. Create the window system
366+
// RenderMode.Buffer enables double-buffered rendering for smooth output.
367+
// Hide the taskbar for a cleaner single-window app look.
368+
var windowSystem = new ConsoleWindowSystem(
369+
RenderMode.Buffer,
370+
options: new ConsoleWindowSystemOptions(
371+
StatusBarOptions: new StatusBarOptions(ShowTaskBar: false)));
372+
373+
// 2. Set up status bar text — this appears at the top of the terminal
374+
windowSystem.StatusBarStateService.TopStatus =
375+
"Video Player — Space: Play/Pause | M: Mode | L: Loop | Esc: Stop";
376+
377+
// 3. Handle Ctrl+C — shut down cleanly instead of hard-killing the process
378+
Console.CancelKeyPress += (_, e) =>
379+
{
380+
e.Cancel = true;
381+
windowSystem.Shutdown(0);
382+
};
383+
384+
// 4. Build the VideoControl
385+
// Fill() — stretch to use the entire window area
386+
// WithOverlay() — bottom status bar appears on key/click, hides after 3s
387+
// WithLooping() — restart from the beginning when the video ends
388+
var videoControl = Controls.Video()
389+
.Fill()
390+
.WithOverlay()
391+
.WithLooping()
392+
.Build();
393+
394+
// 5. (Optional) React to playback state changes
395+
videoControl.PlaybackStateChanged += (_, state) =>
396+
{
397+
string mode = videoControl.RenderMode.ToString();
398+
string status = state switch
399+
{
400+
VideoPlaybackState.Playing => $"Playing ({mode})",
401+
VideoPlaybackState.Paused => $"Paused ({mode})",
402+
_ => "Stopped",
403+
};
404+
windowSystem.StatusBarStateService.TopStatus = $"Video Player — {status}";
405+
};
406+
407+
// 6. Create the window and open a file picker asynchronously
408+
// WithAsyncWindowThread runs a background task tied to the window's lifetime.
409+
// The file picker is modal — it blocks this thread but not the UI.
410+
// BuildAndShow() creates the Window, registers it with the system, and displays it.
411+
var window = new WindowBuilder(windowSystem)
412+
.WithTitle("Video Player")
413+
.Maximized()
414+
.WithColors(Color.White, Color.Black)
415+
.AddControl(videoControl)
416+
.WithAsyncWindowThread(async (win, ct) =>
417+
{
418+
// Open the file picker dialog
419+
var filePath = await FileDialogs.ShowFilePickerAsync(windowSystem,
420+
filter: "*.mp4;*.mkv;*.avi;*.webm;*.mov;*.flv;*.wmv");
421+
422+
if (string.IsNullOrEmpty(filePath))
423+
{
424+
// User cancelled — exit the app
425+
windowSystem.EnqueueOnUIThread(() => windowSystem.Shutdown(0));
426+
return;
427+
}
428+
429+
// Start playback on the UI thread
430+
// PlayFile() sets the path, launches FFmpeg, and begins decoding
431+
windowSystem.EnqueueOnUIThread(() =>
432+
{
433+
win.Title = $"Video — {Path.GetFileName(filePath)}";
434+
videoControl.PlayFile(filePath);
435+
});
436+
437+
// Keep alive until the window closes (ct is cancelled)
438+
try { await Task.Delay(Timeout.Infinite, ct); }
439+
catch (OperationCanceledException) { }
440+
})
441+
.BuildAndShow();
442+
443+
// 7. Clean up when the window closes
444+
// Stop() cancels the playback loop; Dispose() kills the FFmpeg process.
445+
window.OnClosed += (_, _) =>
446+
{
447+
videoControl.Stop();
448+
videoControl.Dispose();
449+
};
450+
451+
// 8. Run the window system — blocks until Shutdown() is called
452+
await Task.Run(() => windowSystem.Run());
453+
```
454+
455+
### What this does
456+
457+
1. Creates a **maximized window** with a black background — ideal for video
458+
2. Opens a **file picker** on launch to select a video file
459+
3. Plays the video with the **overlay enabled** — press any key to see playback info
460+
4. Updates the **status bar** in real-time with the current state and render mode
461+
5. Handles **cleanup** — FFmpeg process is killed when the window closes
462+
6. **Exits cleanly** on Ctrl+C or when the user cancels the file picker

0 commit comments

Comments
 (0)