Initial import
This commit is contained in:
commit
d389fbd6d5
5 changed files with 406 additions and 0 deletions
112
.clang-format
Normal file
112
.clang-format
Normal 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
2
.clangd
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
CompileFlags:
|
||||||
|
CompilationDatabase: build
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/build
|
25
src/CMakeLists.txt
Normal file
25
src/CMakeLists.txt
Normal 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
266
src/main.c
Normal 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:
|
Loading…
Reference in a new issue