Initial import

This commit is contained in:
Jan Hamal Dvořák 2025-03-16 19:00:26 +01:00
commit d389fbd6d5
5 changed files with 406 additions and 0 deletions

112
.clang-format Normal file
View file

@ -0,0 +1,112 @@
---
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 100
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
ForEachMacros:
- 'for_each'
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
IndentGotoLabels: false
IndentPPDirectives: None
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
# Taken from git's rules
PenaltyBreakAssignment: 10
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakComment: 10
PenaltyBreakFirstLessLess: 0
PenaltyBreakString: 10
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: false
SortIncludes: false
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatementsExceptForEachMacros
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp03
TabWidth: 4
UseTab: Always
...

2
.clangd Normal file
View file

@ -0,0 +1,2 @@
CompileFlags:
CompilationDatabase: build

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

25
src/CMakeLists.txt Normal file
View file

@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.21)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project(lazier-player)
find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3)
find_package(PkgConfig REQUIRED)
pkg_check_modules(mpv REQUIRED IMPORTED_TARGET mpv)
if(CMAKE_BUILD_TYPE MATCHES "Debug")
add_compile_options(-fsanitize=address)
add_link_options(-fsanitize=address)
endif()
add_executable(
lazier-player
main.c
)
target_link_libraries(lazier-player PRIVATE SDL3::SDL3 mpv m)
set_property(TARGET lazier-player PROPERTY C_STANDARD 23)
target_compile_options(lazier-player PRIVATE -Wall -Wextra -Wnull-dereference)
target_include_directories(lazier-player PRIVATE include)
install(TARGETS lazier-player DESTINATION bin)

266
src/main.c Normal file
View file

@ -0,0 +1,266 @@
#include <SDL3/SDL.h>
#include <mpv/client.h>
#include <mpv/render.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
mpv_handle *mpv;
mpv_render_context *render_ctx;
SDL_Window *window;
SDL_Renderer *renderer;
SDL_Texture *texture;
int width;
int height;
int running;
int needs_redraw;
} App;
static void SDL_PrintError(const char *msg)
{
fprintf(stderr, "sdl: %s: %s\n", msg, SDL_GetError());
}
static void mpv_print_error(int code, const char *msg)
{
fprintf(stderr, "mpv: %s: %s\n", msg, mpv_error_string(code));
}
static void handle_mpv_events(App *app)
{
while (true) {
mpv_event *event = mpv_wait_event(app->mpv, 0);
if (event->event_id == MPV_EVENT_NONE)
break;
printf("mpv: event=%s\n", mpv_event_name(event->event_id));
if (event->event_id == MPV_EVENT_PROPERTY_CHANGE) {
mpv_event_property *prop = event->data;
if (!strcmp(prop->name, "width") || !strcmp(prop->name, "height")) {
int64_t width = 0;
int64_t height = 0;
mpv_get_property(app->mpv, "width", MPV_FORMAT_INT64, &width);
mpv_get_property(app->mpv, "height", MPV_FORMAT_INT64, &height);
if (width > 0 && height > 0 && (width != app->width || height != app->height)) {
app->width = width;
app->height = height;
if (app->texture)
SDL_DestroyTexture(app->texture);
app->texture = SDL_CreateTexture(app->renderer, SDL_PIXELFORMAT_XBGR8888,
SDL_TEXTUREACCESS_STREAMING, width, height);
if (!app->texture) {
SDL_PrintError("SDL_CreateTexture");
} else {
// Resize window to match video dimensions
SDL_SetWindowSize(app->window, app->width, app->height);
}
}
}
} else if (event->event_id == MPV_EVENT_END_FILE) {
app->running = 0;
}
}
}
static void render_video(App *app)
{
int err;
// Clear the renderer
if (!SDL_SetRenderDrawColor(app->renderer, 0, 0, 0, 255))
SDL_PrintError("SDL_SetRenderDrawColor");
if (!SDL_RenderClear(app->renderer))
SDL_PrintError("SDL_RenderClear");
// Render MPV frame
if (app->texture && app->render_ctx) {
void *pixels;
int stride = 0;
if (!SDL_LockTexture(app->texture, NULL, &pixels, &stride)) {
SDL_PrintError("SDL_LockTexture");
return;
}
float w, h;
if (!SDL_GetTextureSize(app->texture, &w, &h))
SDL_PrintError("SDL_GetTextureSize");
int size[] = { w, h };
mpv_render_param params[] = {
{ MPV_RENDER_PARAM_SW_SIZE, &size }, //
{ MPV_RENDER_PARAM_SW_FORMAT, "rgb0" }, //
{ MPV_RENDER_PARAM_SW_STRIDE, &stride }, //
{ MPV_RENDER_PARAM_SW_POINTER, pixels }, //
{ MPV_RENDER_PARAM_INVALID, NULL },
};
if ((err = mpv_render_context_render(app->render_ctx, params)))
mpv_print_error(err, "mpv_render_context_render");
SDL_UnlockTexture(app->texture);
if (!SDL_RenderTexture(app->renderer, app->texture, NULL, NULL)) {
SDL_PrintError("SDL_RenderTexture");
}
}
// Draw shapes over the video
// 1. Red rectangle
if (!SDL_SetRenderDrawColor(app->renderer, 255, 0, 0, 128))
SDL_PrintError("SDL_SetRenderDrawColor");
SDL_FRect rect = { 50, 50, 100, 80 };
if (!SDL_RenderFillRect(app->renderer, &rect))
SDL_PrintError("SDL_RenderFillRect");
// 2. Blue circle (approximated with points)
if (!SDL_SetRenderDrawColor(app->renderer, 0, 0, 255, 128))
SDL_PrintError("SDL_SetRenderDrawColor");
int centerX = app->width - 100;
int centerY = app->height - 100;
int radius = 40;
for (int i = 0; i < 360; i++) {
float angle = i * 3.14159f / 180.0f;
float x = centerX + radius * SDL_cosf(angle);
float y = centerY + radius * SDL_sinf(angle);
if (!SDL_RenderPoint(app->renderer, x, y))
SDL_PrintError("SDL_RenderPoint");
}
// 3. Green line
if (!SDL_SetRenderDrawColor(app->renderer, 0, 255, 0, 200))
SDL_PrintError("SDL_SetRenderDrawColor");
if (!SDL_RenderLine(app->renderer, 0, 0, app->width, app->height))
SDL_PrintError("SDL_RenderLine");
if (!SDL_RenderPresent(app->renderer))
SDL_PrintError("SDL_RenderPresent");
}
int main(int argc, char *argv[])
{
int err;
if (argc < 2) {
printf("Usage: %s <video_file>\n", argv[0]);
return 1;
}
static App app = { 0 };
app.running = 1;
app.width = 800;
app.height = 600;
// Initialize SDL
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_PrintError("SDL_Init");
return 1;
}
// Create window
app.window = SDL_CreateWindow("SDL3 + MPV Video Player", app.width, app.height, 0);
if (!app.window) {
SDL_PrintError("SDL_CreateWindow");
SDL_Quit();
return 1;
}
// Create renderer
app.renderer = SDL_CreateRenderer(app.window, "software");
if (!app.renderer) {
SDL_PrintError("SDL_CreateRenderer");
SDL_DestroyWindow(app.window);
SDL_Quit();
return 1;
}
// Initialize MPV
app.mpv = mpv_create();
if (!app.mpv) {
fprintf(stderr, "mpv: mpv_create: returned NULL\n");
SDL_DestroyRenderer(app.renderer);
SDL_DestroyWindow(app.window);
SDL_Quit();
return 1;
}
// Configure MPV
if ((err = mpv_set_option_string(app.mpv, "video-sync", "display-resample")))
mpv_print_error(err, "mpv_set_option_string");
if ((err = mpv_set_option_string(app.mpv, "vo", "libmpv")))
mpv_print_error(err, "mpv_set_option_string");
if ((err = mpv_initialize(app.mpv)))
mpv_print_error(err, "mpv_initialize");
// Initialize MPV render context
mpv_render_param params[] = {
{ MPV_RENDER_PARAM_API_TYPE, MPV_RENDER_API_TYPE_SW },
{ MPV_RENDER_PARAM_INVALID, NULL },
};
if ((err = mpv_render_context_create(&app.render_ctx, app.mpv, params)))
mpv_print_error(err, "mpv_render_context_create");
// Load the video file
const char *cmd[] = { "loadfile", argv[1], NULL };
if ((err = mpv_command(app.mpv, cmd)))
mpv_print_error(err, "mpv_command");
// Observe properties
if ((err = mpv_observe_property(app.mpv, 0, "width", MPV_FORMAT_INT64)))
mpv_print_error(err, "mpv_observe_property");
if ((err = mpv_observe_property(app.mpv, 0, "height", MPV_FORMAT_INT64)))
mpv_print_error(err, "mpv_observe_property");
// Main loop
while (app.running) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_EVENT_QUIT) {
app.running = 0;
}
}
handle_mpv_events(&app);
render_video(&app);
SDL_Delay(16); // ~60fps
}
// Cleanup
if (app.texture)
SDL_DestroyTexture(app.texture);
if (app.render_ctx)
mpv_render_context_free(app.render_ctx);
mpv_terminate_destroy(app.mpv);
SDL_DestroyRenderer(app.renderer);
SDL_DestroyWindow(app.window);
SDL_Quit();
return 0;
}
// vim:set ts=4 sw=4 et: