From 220787a4ed416a8bd672986ac8a41a2d133035ca Mon Sep 17 00:00:00 2001 From: Jagholin Date: Sun, 30 Aug 2026 20:38:55 +0200 Subject: [PATCH 1/7] introduce vcpkg to fetch libav; include raylib in submodules Signed-off-by: Jagholin --- .gitignore | 4 +++ .gitmodules | 3 +++ CMakeLists.txt | 16 +++++++----- CMakePresets.json | 13 ++++++++++ avlib.c | 31 ++++++++++++++++++---- main.c | 56 ++++++++++++++++++++++++++++------------ stb_avray.h | 39 +++++++++++++++++++++------- utils.h | 8 +++--- vcpkg-configuration.json | 1 + vcpkg.json | 12 +++++++++ vendor/raylib | 1 + winstuff.c | 28 ++++++++++++++++++++ 12 files changed, 171 insertions(+), 41 deletions(-) create mode 100644 CMakePresets.json create mode 100644 vcpkg-configuration.json create mode 100644 vcpkg.json create mode 160000 vendor/raylib create mode 100644 winstuff.c diff --git a/.gitignore b/.gitignore index a608b91..2f8cbe7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,7 @@ *.mp4 player* build/ +vcpkg_installed/ +CMakeFiles/ +build_tests/ +CMakeUserPresets.json diff --git a/.gitmodules b/.gitmodules index 0bc225e..43a5a0e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "vendor/raygui"] path = vendor/raygui url = git@github.com:raysan5/raygui.git +[submodule "vendor/raylib"] + path = vendor/raylib + url = git@github.com:raysan5/raylib.git diff --git a/CMakeLists.txt b/CMakeLists.txt index c05b6ba..1219699 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,14 +1,18 @@ cmake_minimum_required(VERSION 4.4) project(avrays) -cmake_pkg_config(IMPORT libavcodec REQUIRED) -cmake_pkg_config(IMPORT libavutil REQUIRED) -cmake_pkg_config(IMPORT libavformat REQUIRED) -cmake_pkg_config(IMPORT libswresample REQUIRED) +set(OLD_BUILD_TYPE "${CMAKE_BUILD_TYPE}") +set(CMAKE_BUILD_TYPE Release) +add_subdirectory(vendor/raylib vendor/raylib) +set(CMAKE_BUILD_TYPE "${OLD_BUILD_TYPE}") +if(MSVC) +find_package(PThreads4W REQUIRED) +endif(MSVC) +find_package(FFMPEG REQUIRED) add_library(avrays STATIC) target_sources(avrays PUBLIC avlib.h PRIVATE avlib.c) -target_link_libraries(avrays PRIVATE PkgConfig::libavcodec PkgConfig::libavutil PkgConfig::libavformat PkgConfig::libswresample) +target_link_libraries(avrays PRIVATE raylib avformat avutil avcodec swresample) add_executable(player) target_sources(player PRIVATE main.c) -target_link_libraries(player PRIVATE avrays raylib m X11) +target_link_libraries(player PRIVATE avrays raylib) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..10f178b --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,13 @@ +{ + "version": 2, + "configurePresets": [ + { + "name": "vcpkg", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" + } + } + ] +} diff --git a/avlib.c b/avlib.c index ba0f9b0..851a90b 100644 --- a/avlib.c +++ b/avlib.c @@ -35,7 +35,10 @@ #include #include #include +#ifndef _MSC_VER +// for usleep() #include +#endif #include "avlib.h" #include "utils.h" @@ -72,7 +75,12 @@ static void thread_create(ThreadType *t, void *(*thread_proc)(void *), pthread_create(t, NULL, thread_proc, arg); } static void thread_join(ThreadType *t) { pthread_join(*t, NULL); } + +#ifdef _MSC_VER +static void thread_sleep_ms(unsigned int ms) { Sleep(ms); } +#else static void thread_sleep_ms(unsigned int ms) { usleep(ms * 1000); } +#endif int time_to_str(double seconds, char *buf, size_t n) { int sec = floor(seconds); @@ -397,6 +405,7 @@ int avray_open_file(DecoderContext *ctx, const char *file_name) { } assert(ctx->state == DS_READY); struct DecoderPrivate *p = ctx->p; + TraceLog(LOG_INFO, "Trying to open file: %s", file_name); result = avformat_open_input(&p->fmt_ctx, file_name, NULL, NULL); if (result != 0) { return RESULT_CANT_OPEN; @@ -404,7 +413,7 @@ int avray_open_file(DecoderContext *ctx, const char *file_name) { ERRCHECK("Can't open file"); result = avformat_find_stream_info(p->fmt_ctx, NULL); - ERRCHECK("Cant find stream info"); + ERRCHECK2R(result >= 0, RESULT_CANT_OPEN, "Cant find stream info"); p->video_stream = av_find_best_stream(p->fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); @@ -1212,10 +1221,22 @@ int avray_init_graphics_objects(DecoderContext *ctx, RaylibObjects *objs) { } int avray_free_graphics_objects(RaylibObjects *objs) { - UnloadShader(objs->video_shader); - UnloadTexture(objs->tex_luma); - UnloadTexture(objs->tex_u); - UnloadTexture(objs->tex_v); + if (IsShaderValid(objs->video_shader)) { + UnloadShader(objs->video_shader); + objs->video_shader.id = 0; + } + if (IsTextureValid(objs->tex_luma)) { + UnloadTexture(objs->tex_luma); + objs->tex_luma.id = 0; + } + if (IsTextureValid(objs->tex_u)) { + UnloadTexture(objs->tex_u); + objs->tex_u.id = 0; + } + if (IsTextureValid(objs->tex_v)) { + UnloadTexture(objs->tex_v); + objs->tex_v.id = 0; + } return RESULT_OK; } diff --git a/main.c b/main.c index 2ca1712..1bdb915 100644 --- a/main.c +++ b/main.c @@ -1,13 +1,27 @@ -#include #include #include #include #include #include #include -#include #include +#ifdef __MINGW32__ +#include +#include +#elif (defined _MSC_VER) +// definded in winstuff.c +double get_monotonic_time(); +#else +#include #include +double get_monotonic_time() { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + double time = (double)ts.tv_nsec / 1000000000; + time += ts.tv_sec; + return time; +} +#endif #include "avlib.h" #define RAYGUI_IMPLEMENTATION @@ -59,22 +73,19 @@ void tracelog_impl(FILE *log_stream, TraceLogLevel logLevel, double time, void tracelog_cb(int logLevel, const char *text, va_list args) { FILE *log_stream = stdout; - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - double time = (double)ts.tv_nsec / 1000000000; - time += ts.tv_sec; + double time = get_monotonic_time(); time -= start_clock_time; if (logLevel >= LOG_ERROR) { log_stream = stderr; } - va_list aq; + /* va_list aq; va_copy(aq, args); - va_end(aq); + va_end(aq); */ tracelog_impl(log_stream, logLevel, time, text, args); - if (log_file) { + /* if (log_file) { tracelog_impl(log_file, logLevel, time, text, aq); - } + } */ } void audio_cb(void *frame_data, unsigned int frames) { @@ -147,10 +158,7 @@ void print_argv(char **argv, int argc) { int main(int argc, char **argv) { // tempfile = fopen("tempfile.out", "wb"); - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - start_clock_time = (double)ts.tv_nsec / 1000000000; - start_clock_time += ts.tv_sec; + start_clock_time = get_monotonic_time(); open_log(); atexit(&close_log); @@ -158,28 +166,35 @@ int main(int argc, char **argv) { SetTraceLogCallback(tracelog_cb); float current_volume = 0.2; if (argc < 2) { + fprintf(stderr, "No input files specified\n"); return 1; } int result = avray_init_decoder(&dc); - if (result != 0) + if (result != 0) { + fprintf(stderr, "Can't initialize decoder\n"); exit(EXIT_FAILURE); + } unsigned int file_index = 1; int current_argc = argc; char *file_name = argv[file_index]; while ((result = avray_open_file(&dc, file_name)) == RESULT_CANT_OPEN) { + fprintf(stderr, "Can't open %s\n", file_name); remove_file_from_argv(argv, ¤t_argc, file_index); // print_argv(argv, current_argc); if (file_index == current_argc) break; file_name = argv[file_index]; } - if (result != 0) + if (result != 0) { + fprintf(stderr, "File open failed\n"); exit(EXIT_FAILURE); + } float scale_factor = 1.0; int scaled_width = 1, scaled_height = 1; Vector2 letterbox = {}; + printf("Start init window\n"); InitWindow(1024, 768, file_name); rescale_window(&scale_factor, &scaled_width, &scaled_height, &letterbox, false); @@ -220,6 +235,8 @@ int main(int argc, char **argv) { } StopAudioStream(stream); UnloadAudioStream(stream); + // Set this so that IsAudioStreamValid could detect "deleted" stream. + stream.buffer = NULL; avray_free_graphics_objects(&video_tex); file_name = argv[file_index]; @@ -353,7 +370,12 @@ int main(int argc, char **argv) { avray_free_graphics_objects(&video_tex); avray_shutdown(&dc); - UnloadAudioStream(stream); + // Assume that if the stream is already unloaded, its buffer would be set to + // NULL and the IsAudioStreamValid will return false + if (IsAudioStreamValid(stream)) { + UnloadAudioStream(stream); + stream.buffer = NULL; // being pedantic + } CloseAudioDevice(); CloseWindow(); diff --git a/stb_avray.h b/stb_avray.h index 3a36735..74aaeb7 100644 --- a/stb_avray.h +++ b/stb_avray.h @@ -44,7 +44,7 @@ #include #include -#define UTILFUNC [[maybe_unused]] static inline +#define UTILFUNC static inline typedef struct CleanupStack { struct CleanupStack *pnext; @@ -250,20 +250,20 @@ UTILFUNC void avray__ringbuffer_flush(avray__RingBuffer *rb) { } typedef struct TimeLine { - void *buffer; + char *buffer; size_t elem_size; unsigned int cursor; size_t len; } TimeLine; UTILFUNC TimeLine make_timeline(size_t elem_size, size_t length) { - void *buf = malloc(elem_size * length); + char *buf = malloc(elem_size * length); memset(buf, 0, elem_size * length); return (TimeLine){buf, elem_size, 0, length}; } UTILFUNC void *timeline_push(TimeLine *self) { - void *result = self->buffer + self->elem_size * self->cursor; + char *result = self->buffer + self->elem_size * self->cursor; self->cursor += 1; if (self->cursor >= self->len) { self->cursor = 0; @@ -477,7 +477,10 @@ Vector2 avray_draw_debug_overlay(DecoderContext *ctx, RaylibObjects *objs, #include #include #include +#ifndef _MSC_VER +// for usleep() #include +#endif // Size of the video buffer if the framerate is not available @@ -512,7 +515,12 @@ static void avray__thread_create(ThreadType *t, void *(*thread_proc)(void *), pthread_create(t, NULL, thread_proc, arg); } static void thread_join(ThreadType *t) { pthread_join(*t, NULL); } + +#ifdef _MSC_VER +static void thread_sleep_ms(unsigned int ms) { Sleep(ms); } +#else static void thread_sleep_ms(unsigned int ms) { usleep(ms * 1000); } +#endif int time_to_str(double seconds, char *buf, size_t n) { int sec = floor(seconds); @@ -837,6 +845,7 @@ int avray_open_file(DecoderContext *ctx, const char *file_name) { } assert(ctx->state == DS_READY); struct DecoderPrivate *p = ctx->p; + TraceLog(LOG_INFO, "Trying to open file: %s", file_name); result = avformat_open_input(&p->fmt_ctx, file_name, NULL, NULL); if (result != 0) { return RESULT_CANT_OPEN; @@ -844,7 +853,7 @@ int avray_open_file(DecoderContext *ctx, const char *file_name) { ERRCHECK("Can't open file"); result = avformat_find_stream_info(p->fmt_ctx, NULL); - ERRCHECK("Cant find stream info"); + ERRCHECK2R(result >= 0, RESULT_CANT_OPEN, "Cant find stream info"); p->video_stream = av_find_best_stream(p->fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); @@ -1652,10 +1661,22 @@ int avray_init_graphics_objects(DecoderContext *ctx, RaylibObjects *objs) { } int avray_free_graphics_objects(RaylibObjects *objs) { - UnloadShader(objs->video_shader); - UnloadTexture(objs->tex_luma); - UnloadTexture(objs->tex_u); - UnloadTexture(objs->tex_v); + if (IsShaderValid(objs->video_shader)) { + UnloadShader(objs->video_shader); + objs->video_shader.id = 0; + } + if (IsTextureValid(objs->tex_luma)) { + UnloadTexture(objs->tex_luma); + objs->tex_luma.id = 0; + } + if (IsTextureValid(objs->tex_u)) { + UnloadTexture(objs->tex_u); + objs->tex_u.id = 0; + } + if (IsTextureValid(objs->tex_v)) { + UnloadTexture(objs->tex_v); + objs->tex_v.id = 0; + } return RESULT_OK; } diff --git a/utils.h b/utils.h index 021d80b..585e285 100644 --- a/utils.h +++ b/utils.h @@ -38,7 +38,7 @@ #include #include -#define UTILFUNC [[maybe_unused]] static inline +#define UTILFUNC static inline typedef struct CleanupStack { struct CleanupStack *pnext; @@ -244,20 +244,20 @@ UTILFUNC void ringbuffer_flush(RingBuffer *rb) { } typedef struct TimeLine { - void *buffer; + char *buffer; size_t elem_size; unsigned int cursor; size_t len; } TimeLine; UTILFUNC TimeLine make_timeline(size_t elem_size, size_t length) { - void *buf = malloc(elem_size * length); + char *buf = malloc(elem_size * length); memset(buf, 0, elem_size * length); return (TimeLine){buf, elem_size, 0, length}; } UTILFUNC void *timeline_push(TimeLine *self) { - void *result = self->buffer + self->elem_size * self->cursor; + char *result = self->buffer + self->elem_size * self->cursor; self->cursor += 1; if (self->cursor >= self->len) { self->cursor = 0; diff --git a/vcpkg-configuration.json b/vcpkg-configuration.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/vcpkg-configuration.json @@ -0,0 +1 @@ +{} diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000..d9c7f8c --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,12 @@ +{ + "dependencies": [ + { + "name": "ffmpeg", + "features": [ + "all" + ] + }, + "pthreads" + ], + "builtin-baseline": "abb6dda5cc32914d2e64d7d72b974dc301d1fc8a" +} diff --git a/vendor/raylib b/vendor/raylib new file mode 160000 index 0000000..9f3cadf --- /dev/null +++ b/vendor/raylib @@ -0,0 +1 @@ +Subproject commit 9f3cadf1e618f125bd9b282c7759f8cb26ce17fc diff --git a/winstuff.c b/winstuff.c new file mode 100644 index 0000000..0ade591 --- /dev/null +++ b/winstuff.c @@ -0,0 +1,28 @@ +#ifdef _MSC_VER +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#include +#include +#include +// Function to get monotonic clock time in seconds +double get_monotonic_time() { + static LARGE_INTEGER frequency = { 0 }; + LARGE_INTEGER counter; + + // Initialize frequency only once + if (frequency.QuadPart == 0) { + if (!QueryPerformanceFrequency(&frequency)) { + fprintf(stderr, "High-resolution performance counter not supported.\n"); + return -1.0; + } + } + + // Get current counter value + if (!QueryPerformanceCounter(&counter)) { + fprintf(stderr, "Failed to query performance counter.\n"); + return -1.0; + } + + // Convert to seconds + return (double)counter.QuadPart / (double)frequency.QuadPart; +} +#endif \ No newline at end of file From 88bc91ab9701035f90fa9888580d24ce04ab6734 Mon Sep 17 00:00:00 2001 From: Jagholin Date: Sun, 30 Aug 2026 15:20:36 -0700 Subject: [PATCH 2/7] confirm functionality with cl/vcpkg --- CMakeLists.txt | 15 ++++++++++++--- avlib.c | 9 ++++++--- avlib.h | 4 ++-- main.c | 2 +- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1219699..88030b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 4.4) +cmake_minimum_required(VERSION 4.3) project(avrays) set(OLD_BUILD_TYPE "${CMAKE_BUILD_TYPE}") @@ -7,12 +7,21 @@ add_subdirectory(vendor/raylib vendor/raylib) set(CMAKE_BUILD_TYPE "${OLD_BUILD_TYPE}") if(MSVC) find_package(PThreads4W REQUIRED) -endif(MSVC) +endif() find_package(FFMPEG REQUIRED) add_library(avrays STATIC) target_sources(avrays PUBLIC avlib.h PRIVATE avlib.c) -target_link_libraries(avrays PRIVATE raylib avformat avutil avcodec swresample) +target_include_directories(avrays PRIVATE ${FFMPEG_INCLUDE_DIRS}) +target_link_directories(avrays PRIVATE ${FFMPEG_LIBRARY_DIRS}) +target_link_libraries(avrays PRIVATE raylib ${FFMPEG_LIBRARIES}) +if(MSVC) +target_link_libraries(avrays PRIVATE PThreads4W::PThreads4W) +endif() add_executable(player) target_sources(player PRIVATE main.c) +if(MSVC) +target_sources(player PRIVATE winstuff.c) +endif() +target_include_directories(player PRIVATE ${FFMPEG_INCLUDE_DIRS}) target_link_libraries(player PRIVATE avrays raylib) diff --git a/avlib.c b/avlib.c index 851a90b..25e91c8 100644 --- a/avlib.c +++ b/avlib.c @@ -35,6 +35,7 @@ #include #include #include +#include #ifndef _MSC_VER // for usleep() #include @@ -876,10 +877,12 @@ static void try_unlock_decoder_sem(DecoderContext *ctx) { int sem_value; struct DecoderPrivate *p = ctx->p; semaphore_get_value(&p->sem, &sem_value); - if (sem_value == 0) { + // TraceLog(LOG_DEBUG, "current semaphore value is %d", sem_value); + if (sem_value <= 0) { // we can advance semaphore if both buffers are less than 80% empty if (ringbuffer_len(&p->image_buffer) < 0.8 * p->image_buffer.buf_size && ringbuffer_len(&p->audio_buffer) < 0.8 * p->audio_buffer.buf_size) { + // TraceLog(LOG_INFO, "trying to advance the semaphore..."); semaphore_incr(&p->sem); } } @@ -1393,14 +1396,14 @@ Vector2 avray_draw_debug_overlay(DecoderContext *ctx, RaylibObjects *objs, text_width = tw; DrawText(msg, x, y, FONTSIZE, WHITE); y += LINEHEIGHT; - snprintf(msg, sizeof(msg), "abytes: %ld written: %ld", ctx->abytes_pulled, + snprintf(msg, sizeof(msg), "abytes: %" PRIu64 ", written: %" PRIu64, ctx->abytes_pulled, ctx->abytes_written); tw = MeasureText(msg, FONTSIZE); if (tw > text_width) text_width = tw; DrawText(msg, x, y, FONTSIZE, WHITE); y += LINEHEIGHT; - snprintf(msg, sizeof(msg), "vbytes: %ld, written: %ld", ctx->vbytes_pulled, + snprintf(msg, sizeof(msg), "vbytes: %" PRIu64 ", written: %" PRIu64, ctx->vbytes_pulled, ctx->vbytes_written); tw = MeasureText(msg, FONTSIZE); if (tw > text_width) diff --git a/avlib.h b/avlib.h index 8aba2d5..08815fb 100644 --- a/avlib.h +++ b/avlib.h @@ -97,8 +97,8 @@ typedef struct DecoderContext { TimeLine vbuffer_timeline; TimeLine abuffer_timeline; - unsigned long int abytes_pulled, vbytes_pulled; - unsigned long int abytes_written, vbytes_written; + uint64_t abytes_pulled, vbytes_pulled; + uint64_t abytes_written, vbytes_written; DecoderState state; } DecoderContext; diff --git a/main.c b/main.c index 1bdb915..ff4cc60 100644 --- a/main.c +++ b/main.c @@ -34,7 +34,7 @@ DecoderContext dc; FILE *log_file; void open_log() { log_file = fopen("raylib.log", "w"); } -void close_log() { +void close_log(void) { if (log_file) fclose(log_file); } From b80a6dbf9ef1a383bd1f34e1b2f378c0a0bd55b6 Mon Sep 17 00:00:00 2001 From: Jagholin Date: Tue, 1 Sep 2026 14:04:24 +0200 Subject: [PATCH 3/7] make ffmpeg optional in vcpkg and add an option to provide libav with pkgconfig Signed-off-by: Jagholin --- .gitignore | 3 ++- CMakeLists.txt | 56 +++++++++++++++++++++++++++++++++++++++++++------- main.c | 17 +++++++-------- stb_avray.h | 13 +++++++----- vcpkg.json | 25 ++++++++++++++++------ 5 files changed, 87 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index 2f8cbe7..2bd527c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,9 @@ *.mkv *.webm *.mp4 +.vs player* -build/ +/build*/ vcpkg_installed/ CMakeFiles/ build_tests/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 88030b3..b5a5e2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,27 +1,69 @@ +# 4.3 is what VS ships with now, we use it as a baseline +# (but will probably work with lower versions, too) cmake_minimum_required(VERSION 4.3) +# The options logic has to be made before project(...) +# since that's when vcpkg install runs. +option(USE_FFMPEG_FROM_VCPKG "Install ffmpeg from vcpkg" ON) +option(USE_FFMPEG_FROM_PKGCONFIG "Use ffmpeg installed on the system" OFF) +if("${USE_FFMPEG_FROM_VCPKG}") + list(APPEND VCPKG_MANIFEST_FEATURES "ffmpeg") +endif() +option(USE_INCLUDED_RAYLIB "Use raylib from /vendor directory" ON) +option(USE_PTHREADS4W "Use pthreads4w lib" OFF) +option(RAYLIB_DIR "raylib base directory to use" "") +if ("${USE_INCLUDED_RAYLIB}") + set(RAYLIB_DIR "vendor/raylib") +endif() + +if(USE_PTHREADS4W) + message(STATUS "Activating pthreads4w") + list(APPEND VCPKG_MANIFEST_FEATURES "pthreads") +endif() + project(avrays) + +# We want a release build of Raylib no matter what. set(OLD_BUILD_TYPE "${CMAKE_BUILD_TYPE}") set(CMAKE_BUILD_TYPE Release) -add_subdirectory(vendor/raylib vendor/raylib) +add_subdirectory("${RAYLIB_DIR}") set(CMAKE_BUILD_TYPE "${OLD_BUILD_TYPE}") -if(MSVC) -find_package(PThreads4W REQUIRED) + +# Pthreads4w is only for microsoft's compiler, mingw has its own implementation of +# pthreads calls. +if(USE_PTHREADS4W) + find_package(PThreads4W REQUIRED) +endif() + +# Now try to find FFMPEG library. If CMAKE_TOOLCHAIN_FILE is defined and contains vcpkg, +# assume that user wants to use it to build ffmpeg +if("${USE_FFMPEG_FROM_VCPKG}") + find_package(FFMPEG REQUIRED) +elseif("${USE_FFMPEG_FROM_PKGCONFIG}") + message(STATUS "Using ffmpeg from pkgconfig") + cmake_pkg_config(IMPORT libavcodec REQUIRED) + cmake_pkg_config(IMPORT libavutil REQUIRED) + cmake_pkg_config(IMPORT libavformat REQUIRED) + cmake_pkg_config(IMPORT libswresample REQUIRED) + list(APPEND FFMPEG_LIBRARIES PkgConfig::libavcodec PkgConfig::libavutil PkgConfig::libavformat PkgConfig::libswresample) +else() + message(FATAL_ERROR "ffmpeg's libav libraries are required to build this. + Set USE_FFMPEG_FROM_PKGCONFIG or USE_FFMPEG_FROM_VCPKG variable to ON.") endif() -find_package(FFMPEG REQUIRED) + add_library(avrays STATIC) target_sources(avrays PUBLIC avlib.h PRIVATE avlib.c) target_include_directories(avrays PRIVATE ${FFMPEG_INCLUDE_DIRS}) target_link_directories(avrays PRIVATE ${FFMPEG_LIBRARY_DIRS}) target_link_libraries(avrays PRIVATE raylib ${FFMPEG_LIBRARIES}) -if(MSVC) -target_link_libraries(avrays PRIVATE PThreads4W::PThreads4W) +if(USE_PTHREADS4W) + target_link_libraries(avrays PRIVATE PThreads4W::PThreads4W) endif() add_executable(player) target_sources(player PRIVATE main.c) if(MSVC) -target_sources(player PRIVATE winstuff.c) + target_sources(player PRIVATE winstuff.c) endif() target_include_directories(player PRIVATE ${FFMPEG_INCLUDE_DIRS}) target_link_libraries(player PRIVATE avrays raylib) diff --git a/main.c b/main.c index ff4cc60..a08b61a 100644 --- a/main.c +++ b/main.c @@ -40,7 +40,7 @@ void close_log(void) { } void tracelog_impl(FILE *log_stream, TraceLogLevel logLevel, double time, - const char *text, va_list args) { + const char *text) { const char *log_type = ""; switch (logLevel) { case LOG_INFO: @@ -66,13 +66,13 @@ void tracelog_impl(FILE *log_stream, TraceLogLevel logLevel, double time, return; break; } - fprintf(log_stream, "%s (%.3lf): ", log_type, time); - vfprintf(log_stream, text, args); - fputc('\n', log_stream); + fprintf(log_stream, "%s (%.3lf): %s\n", log_type, time, text); } void tracelog_cb(int logLevel, const char *text, va_list args) { FILE *log_stream = stdout; + // Even in multibyte strings, 2 kb buffer should be enough + char buffer[2048]; double time = get_monotonic_time(); time -= start_clock_time; @@ -82,10 +82,11 @@ void tracelog_cb(int logLevel, const char *text, va_list args) { /* va_list aq; va_copy(aq, args); va_end(aq); */ - tracelog_impl(log_stream, logLevel, time, text, args); - /* if (log_file) { - tracelog_impl(log_file, logLevel, time, text, aq); - } */ + vsnprintf(buffer, sizeof(buffer), text, args); + tracelog_impl(log_stream, logLevel, time, buffer); + if (log_file) { + tracelog_impl(log_file, logLevel, time, buffer); + } } void audio_cb(void *frame_data, unsigned int frames) { diff --git a/stb_avray.h b/stb_avray.h index 74aaeb7..01f7ebf 100644 --- a/stb_avray.h +++ b/stb_avray.h @@ -428,8 +428,8 @@ typedef struct DecoderContext { TimeLine vbuffer_timeline; TimeLine abuffer_timeline; - unsigned long int abytes_pulled, vbytes_pulled; - unsigned long int abytes_written, vbytes_written; + uint64_t abytes_pulled, vbytes_pulled; + uint64_t abytes_written, vbytes_written; DecoderState state; } DecoderContext; @@ -477,6 +477,7 @@ Vector2 avray_draw_debug_overlay(DecoderContext *ctx, RaylibObjects *objs, #include #include #include +#include #ifndef _MSC_VER // for usleep() #include @@ -1316,10 +1317,12 @@ static void avray__try_unlock_decoder_sem(DecoderContext *ctx) { int sem_value; struct DecoderPrivate *p = ctx->p; avray__semaphore_get_value(&p->sem, &sem_value); - if (sem_value == 0) { + // TraceLog(LOG_DEBUG, "current semaphore value is %d", sem_value); + if (sem_value <= 0) { // we can advance semaphore if both buffers are less than 80% empty if (avray__ringbuffer_len(&p->image_buffer) < 0.8 * p->image_buffer.buf_size && avray__ringbuffer_len(&p->audio_buffer) < 0.8 * p->audio_buffer.buf_size) { + // TraceLog(LOG_INFO, "trying to advance the semaphore..."); avray__semaphore_incr(&p->sem); } } @@ -1833,14 +1836,14 @@ Vector2 avray_draw_debug_overlay(DecoderContext *ctx, RaylibObjects *objs, text_width = tw; DrawText(msg, x, y, FONTSIZE, WHITE); y += LINEHEIGHT; - snprintf(msg, sizeof(msg), "abytes: %ld written: %ld", ctx->abytes_pulled, + snprintf(msg, sizeof(msg), "abytes: %" PRIu64 ", written: %" PRIu64, ctx->abytes_pulled, ctx->abytes_written); tw = MeasureText(msg, FONTSIZE); if (tw > text_width) text_width = tw; DrawText(msg, x, y, FONTSIZE, WHITE); y += LINEHEIGHT; - snprintf(msg, sizeof(msg), "vbytes: %ld, written: %ld", ctx->vbytes_pulled, + snprintf(msg, sizeof(msg), "vbytes: %" PRIu64 ", written: %" PRIu64, ctx->vbytes_pulled, ctx->vbytes_written); tw = MeasureText(msg, FONTSIZE); if (tw > text_width) diff --git a/vcpkg.json b/vcpkg.json index d9c7f8c..0b5e3d7 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,12 +1,25 @@ { "dependencies": [ - { - "name": "ffmpeg", - "features": [ - "all" + ], + "features": + { + "ffmpeg": { + "description": "FFMPEG's libav library set", + "dependencies": [ + { + "name": "ffmpeg", + "features": [ + "all" + ] + } ] }, - "pthreads" - ], + "pthreads": { + "description": "PThreads4W pthreads implementation. Only needed on MSVC", + "dependencies": [ + "pthreads" + ] + } + }, "builtin-baseline": "abb6dda5cc32914d2e64d7d72b974dc301d1fc8a" } From 4cf443cfd29bbda294b9ea69b07b247cacde7386 Mon Sep 17 00:00:00 2001 From: Jagholin Date: Tue, 1 Sep 2026 16:24:26 +0200 Subject: [PATCH 4/7] support using precompiled libav binaries Signed-off-by: Jagholin --- CMakeLists.txt | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b5a5e2e..383ac7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,15 +6,15 @@ cmake_minimum_required(VERSION 4.3) # since that's when vcpkg install runs. option(USE_FFMPEG_FROM_VCPKG "Install ffmpeg from vcpkg" ON) option(USE_FFMPEG_FROM_PKGCONFIG "Use ffmpeg installed on the system" OFF) +option(LIBRARY_ONLY "Don't build player exe" OFF) if("${USE_FFMPEG_FROM_VCPKG}") list(APPEND VCPKG_MANIFEST_FEATURES "ffmpeg") endif() -option(USE_INCLUDED_RAYLIB "Use raylib from /vendor directory" ON) option(USE_PTHREADS4W "Use pthreads4w lib" OFF) -option(RAYLIB_DIR "raylib base directory to use" "") -if ("${USE_INCLUDED_RAYLIB}") - set(RAYLIB_DIR "vendor/raylib") -endif() +set(CACHE{RAYLIB_DIR} HELP "raylib base directory to use" TYPE PATH VALUE "./vendor/raylib") +set(CACHE{FFMPEG_DIR} HELP "base directory of libav pre-compiled files" TYPE PATH VALUE "") +message(STATUS "Raylib dir is set to ${RAYLIB_DIR}") +message(STATUS "FFMPEG base dir is set to ${FFMPEG_DIR}") if(USE_PTHREADS4W) message(STATUS "Activating pthreads4w") @@ -35,8 +35,7 @@ if(USE_PTHREADS4W) find_package(PThreads4W REQUIRED) endif() -# Now try to find FFMPEG library. If CMAKE_TOOLCHAIN_FILE is defined and contains vcpkg, -# assume that user wants to use it to build ffmpeg +# Now try to find FFMPEG library. if("${USE_FFMPEG_FROM_VCPKG}") find_package(FFMPEG REQUIRED) elseif("${USE_FFMPEG_FROM_PKGCONFIG}") @@ -46,9 +45,16 @@ elseif("${USE_FFMPEG_FROM_PKGCONFIG}") cmake_pkg_config(IMPORT libavformat REQUIRED) cmake_pkg_config(IMPORT libswresample REQUIRED) list(APPEND FFMPEG_LIBRARIES PkgConfig::libavcodec PkgConfig::libavutil PkgConfig::libavformat PkgConfig::libswresample) +elseif(FFMPEG_DIR) + if(NOT EXISTS "${FFMPEG_DIR}/include/libavcodec/avcodec.h") + message(FATAL_ERROR "ffmpeg's directory doesn't seem to have necessary files") + endif() + list(APPEND FFMPEG_INCLUDE_DIRS "${FFMPEG_DIR}/include") + list(APPEND FFMPEG_LIBRARY_DIRS "${FFMPEG_DIR}/lib") + list(APPEND FFMPEG_LIBRARIES "${FFMPEG_DIR}/lib/avcodec.lib" "${FFMPEG_DIR}/lib/avutil.lib" "${FFMPEG_DIR}/lib/avformat.lib" "${FFMPEG_DIR}/lib/swresample.lib") else() message(FATAL_ERROR "ffmpeg's libav libraries are required to build this. - Set USE_FFMPEG_FROM_PKGCONFIG or USE_FFMPEG_FROM_VCPKG variable to ON.") + Set USE_FFMPEG_FROM_PKGCONFIG or USE_FFMPEG_FROM_VCPKG variable to ON, or provide FFMPEG_DIR.") endif() add_library(avrays STATIC) @@ -67,3 +73,6 @@ if(MSVC) endif() target_include_directories(player PRIVATE ${FFMPEG_INCLUDE_DIRS}) target_link_libraries(player PRIVATE avrays raylib) +if(USE_PTHREADS4W) + target_link_libraries(player PRIVATE PThreads4W::PThreads4W) +endif() From 28cc4d9b4bd6e29aa4717817333e8df90d406e81 Mon Sep 17 00:00:00 2001 From: "Pavel B." Date: Tue, 1 Sep 2026 23:25:30 +0000 Subject: [PATCH 5/7] create a cmake build wokflow --- .github/workflows/cmake-multi-platform.yml | 145 +++++++++++++++++++++ CMakeLists.txt | 2 +- vcpkg.json | 3 +- 3 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/cmake-multi-platform.yml diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml new file mode 100644 index 0000000..9ddfc91 --- /dev/null +++ b/.github/workflows/cmake-multi-platform.yml @@ -0,0 +1,145 @@ +# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. +# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml +name: CMake on multiple platforms + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. + fail-fast: false + + # Set up a matrix to run the following 3 configurations: + # 1. + # 2. + # 3. + # + # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. + matrix: + os: [ubuntu-26.04, windows-latest] + build_type: [Release] + c_compiler: [gcc, clang, cl] + ffmpeg_from_vcpkg: [ON, OFF] + ffmpeg_from_pkgconfig: [ON, OFF] + include: + - os: windows-latest + c_compiler: cl + cpp_compiler: cl + use_pthreads: ON + - os: ubuntu-26.04 + c_compiler: gcc + cpp_compiler: g++ + use_pthreads: OFF + - os: ubuntu-26.04 + c_compiler: clang + cpp_compiler: clang++ + use_pthreads: OFF + exclude: + - os: windows-latest + c_compiler: gcc + - os: windows-latest + c_compiler: clang + - os: ubuntu-26.04 + c_compiler: cl + - ffmpeg_from_vcpkg: ON + ffmpeg_from_pkgconfig: ON + - ffmpeg_from_vcpkg: OFF + ffmpeg_from_pkgconfig: OFF + os: ubuntu-26.04 + # VCPKG is fucking broken on this pos github runner. + - ffmpeg_from_vcpkg: ON + - os: windows-latest + ffmpeg_from_pkgconfig: ON + + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + - name: Set reusable strings + # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. + id: strings + shell: bash + run: | + echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + echo "vcpkg-toolchain=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" >> "$GITHUB_OUTPUT" + + - name: Install prereqs on linux + if: ${{ contains(matrix.os, 'ubuntu') }} + id: install + run: > + sudo apt-get update + + sudo apt-get install libavutil-dev libavcodec-dev + libswresample-dev libavformat-dev nasm libasound2-dev libx11-dev libxrandr-dev + libxi-dev libgl1-mesa-dev libglu1-mesa-dev libxcursor-dev libxinerama-dev + libxkbcommon-dev autoconf autoconf-archive automake libtool + + # git clone https://github.com/microsoft/vcpkg.git + # + # cd vcpkg + # + # echo "vcpkg-toolchain=$(pwd)/scripts/buildsystems/vcpkg.cmake" >> "$GITHUB_OUTPUT" + # + # ./bootstrap-vcpkg.sh + # - name: Install ffmpeg binaries on windows + # if: ${{ contains(matrix.os, 'windows') }} + # id: ffmp + # shell: powershell + # run: | + # Invoke-WebRequest -Uri https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full-shared.7z -OutFile ffmpeg-shared.7z + # & 7z x .\ffmpeg-shared.7z -y + # (Get-ChildItem -Directory | Where-Object {$_.Name -like "*ffmpeg*"} | Select-Object -First 1 | ForEach-Object { "ffmp-dir=$($_.Name)" }) >> $GITHUB_OUTPUT + + # - name: Update vcpkg root + # shell: bash + # run: | + # cd $VCPKG_INSTALLATION_ROOT + # git reset --hard + # git pull + # cd - + + - name: download ffmpeg from github + uses: robinraju/release-downloader@v1 + if: ${{ contains(matrix.os, 'windows') }} + id: ffmp + with: + repository: 'GyanD/codexffmpeg' + tag: '9.0.1' + fileName: 'ffmpeg-9.0.1-full_build-shared.zip' + extract: true + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: > + cmake -B ${{ steps.strings.outputs.build-output-dir }} + -DCMAKE_TOOLCHAIN_FILE=${{ steps.strings.outputs.vcpkg-toolchain }} + -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -DFFMPEG_DIR="./ffmpeg-9.0.1-full_build-shared" + -DUSE_FFMPEG_FROM_VCPKG=${{ matrix.ffmpeg_from_vcpkg }} + -DUSE_FFMPEG_FROM_PKGCONFIG=${{ matrix.ffmpeg_from_pkgconfig }} + -DUSE_PTHREADS4W=${{ matrix.use_pthreads }} + -DVCPKG_TARGET_TRIPLET=${{ case( matrix.os == 'ubuntu-26.04', 'x64-linux', + matrix.os == 'windows-latest', 'x64-windows-static-md-release', + '' ) }} + -S ${{ github.workspace }} + + - name: Build + # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} + +# - name: Test +# working-directory: ${{ steps.strings.outputs.build-output-dir }} + # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail +# run: ctest --build-config ${{ matrix.build_type }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 383ac7c..cca7c45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ # 4.3 is what VS ships with now, we use it as a baseline # (but will probably work with lower versions, too) -cmake_minimum_required(VERSION 4.3) +cmake_minimum_required(VERSION 4.2) # The options logic has to be made before project(...) # since that's when vcpkg install runs. diff --git a/vcpkg.json b/vcpkg.json index 0b5e3d7..f6790f7 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -20,6 +20,5 @@ "pthreads" ] } - }, - "builtin-baseline": "abb6dda5cc32914d2e64d7d72b974dc301d1fc8a" + } } From f406440f06f65914519a4efd4f9386fe3db8e72c Mon Sep 17 00:00:00 2001 From: Jagholin Date: Wed, 2 Sep 2026 16:56:52 +0200 Subject: [PATCH 6/7] try to create a published artifact Signed-off-by: Jagholin --- .github/workflows/cmake-multi-platform.yml | 31 +++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 9ddfc91..c207f12 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -11,7 +11,10 @@ on: jobs: build: runs-on: ${{ matrix.os }} - + permissions: + id-token: write + contents: read + attestations: write strategy: # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. fail-fast: false @@ -125,7 +128,7 @@ jobs: -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} - -DFFMPEG_DIR="./ffmpeg-9.0.1-full_build-shared" + ${{ case ( contains(matrix.os, 'windows'), '-DFFMPEG_DIR="./ffmpeg-9.0.1-full_build-shared"', '') }} -DUSE_FFMPEG_FROM_VCPKG=${{ matrix.ffmpeg_from_vcpkg }} -DUSE_FFMPEG_FROM_PKGCONFIG=${{ matrix.ffmpeg_from_pkgconfig }} -DUSE_PTHREADS4W=${{ matrix.use_pthreads }} @@ -136,7 +139,29 @@ jobs: - name: Build # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). - run: cmake --build ${{ steps.strings.outputs.build-output-dir }} + run: | + cmake --build ${{ steps.strings.outputs.build-output-dir }} --config Release + + - name: Generate attestation + if: ${{ contains(matrix.os, 'windows') }} + uses: actions/attest@v4 + with: + subject-path: ${{ steps.strings.outputs.build-output-dir }}\Release\player.exe + + - name: move files into publish folder + if: ${{ contains(matrix.os, 'windows') }} + shell: bash + run: | + mkdir publish + mv "${{ steps.strings.outputs.build-output-dir }}"/Release/*.exe publish/ + mv ffmpeg-9.0.1-full_build-shared/bin/{avcodec-63,avformat-63,avutil-61,swresample-7}.dll publish/ + + - name: Upload windows build + if: ${{ contains(matrix.os, 'windows') }} + uses: actions/upload-artifact@v7 + with: + name: avrays + path: publish/ # - name: Test # working-directory: ${{ steps.strings.outputs.build-output-dir }} From f037ea3ad786fbab079ce7aa074e66edf17dd7be Mon Sep 17 00:00:00 2001 From: Jagholin Date: Sun, 13 Sep 2026 15:24:22 +0200 Subject: [PATCH 7/7] making sure everything works Signed-off-by: Jagholin --- CMakeLists.txt | 27 ++++++++----- README.md | 72 ++++++++++++++++++++++++++++++++- avlib.c | 105 +++++++++++++++++++++++++------------------------ stb_avray.h | 105 +++++++++++++++++++++++++------------------------ 4 files changed, 194 insertions(+), 115 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cca7c45..2944d8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,12 @@ option(LIBRARY_ONLY "Don't build player exe" OFF) if("${USE_FFMPEG_FROM_VCPKG}") list(APPEND VCPKG_MANIFEST_FEATURES "ffmpeg") endif() -option(USE_PTHREADS4W "Use pthreads4w lib" OFF) +if (MSVC) + option(USE_PTHREADS4W "Use pthreads4w lib" ON) +else() + option(USE_PTHREADS4W "Use pthreads4w lib" OFF) +endif() + set(CACHE{RAYLIB_DIR} HELP "raylib base directory to use" TYPE PATH VALUE "./vendor/raylib") set(CACHE{FFMPEG_DIR} HELP "base directory of libav pre-compiled files" TYPE PATH VALUE "") message(STATUS "Raylib dir is set to ${RAYLIB_DIR}") @@ -66,13 +71,15 @@ if(USE_PTHREADS4W) target_link_libraries(avrays PRIVATE PThreads4W::PThreads4W) endif() -add_executable(player) -target_sources(player PRIVATE main.c) -if(MSVC) - target_sources(player PRIVATE winstuff.c) -endif() -target_include_directories(player PRIVATE ${FFMPEG_INCLUDE_DIRS}) -target_link_libraries(player PRIVATE avrays raylib) -if(USE_PTHREADS4W) - target_link_libraries(player PRIVATE PThreads4W::PThreads4W) +if(NOT LIBRARY_ONLY) + add_executable(player) + target_sources(player PRIVATE main.c) + if(MSVC) + target_sources(player PRIVATE winstuff.c) + endif() + target_include_directories(player PRIVATE ${FFMPEG_INCLUDE_DIRS}) + target_link_libraries(player PRIVATE avrays raylib) + if(USE_PTHREADS4W) + target_link_libraries(player PRIVATE PThreads4W::PThreads4W) + endif() endif() diff --git a/README.md b/README.md index e92f74a..991df35 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,14 @@ If you don't wish to use stb_avray.h, then you will need the following files: `a - make_stb is a bash script that generates stb_avray.h file. - stb_avray.h is a single header version of the library. You can include it instead of the avlib.c/avlib.h/utils.h combo. +## Where do I get ffmpeg/libav from? + +On linux, look for packages named `ffmpeg`, `libav*-dev` or similar. Run `pkg-config --list-package-names | grep -E 'lib(av|sw)'` to see which of the libav* libraries you have installed. At the minimum, you need libavcodec, libavutil, libavformat and libswresample to compile avray. + +On windows, you can download and use precompiled binaries gyan [gyan](https://www.gyan.dev/ffmpeg/builds/) or [BtbN](https://github.com/BtbN/FFmpeg-Builds/releases). Make sure that what you downloaded actually contains dev files(headers and `*.lib`s) and not just executables for ffmpeg and friends. + +As an alternative, you can download and compile these libraries with vcpkg. If you go with this route, make sure you have enough space on your hard drive (10 Gb at the very least) + ## How to pause, or note about how video/audio sync works AVRay assumes that the audio stream will be continously pulled in raylib's audio stream callback function, and calculates current timestamps/syncs video playback based on that. This has a couple of consequences: @@ -51,6 +59,66 @@ The player has the following controls: - Double click switches borderless fullscreen mode on/off - `Esc` closes the window, as is tradition in raylib. +### CMake presets and options + +cmake builds can be configured with the following options: + +```bash +-DUSE_FFMPEG_FROM_VCPKG=ON # uses vcpkg to download and build libav libraries, default ON +-DUSE_FFMPEG_FROM_PKGCONFIG=OFF # uses pkgconfig to find libav on your system +-LIBRARY_ONLY=OFF # build only the library .a .lib +``` + +It's recommended to create `CMakeUserPresets.json` file to store build configurations. + +It could look like this + +```json +{ + "version": 2, + "configurePresets": [ + { + "name": "linux", + "inherits": "vcpkg", + "cacheVariables": { + "VCPKG_TARGET_TRIPLET": "x64-linux", + "USE_FFMPEG_FROM_VCPKG": "OFF", + "USE_FFMPEG_FROM_PKGCONFIG": "ON" + } + }, + { + "name": "windows", + "inherits": "vcpkg", + "binaryDir": "${sourceDir}/build_win", + "cacheVariables": { + "VCPKG_TARGET_TRIPLET": "x64-windows-static-md-release", + "USE_FFMPEG_FROM_VCPKG": "OFF", + "CMAKE_BUILD_TYPE": , + "FFMPEG_DIR": , + "USE_PTHREADS4W": "ON" + } + } + ], + "buildPresets": [ + { + "name": "windows", + "configurePreset": "windows" + }, + { + "name": "linux", + "configurePreset": "linux" + } + ] +} +``` + +With `CMakeUserPresets.json` file the configure and compile steps can be as easy as: + +```bash +cmake --preset=windows +cmake --build --preset=windows +``` + ## Minimal example A minimal program that uses AVRay could look something like this(see main_minimal.c): @@ -85,7 +153,7 @@ int main(int argc, char **argv) { DecoderState state = ctx.state; // Exit when the playback is finished if (state == DS_FINISHED) { - break; + break; } // Update textures checks the current timestamp(driven by the audio stream) // and updates the textures if it's time to show the next video frame. @@ -208,3 +276,5 @@ static void thread_create(ThreadType *t, void *(*thread_proc)(void *), static void thread_join(ThreadType *t) { pthread_join(*t, NULL); } static void thread_sleep_ms(unsigned int ms) { usleep(ms * 1000); } ``` + +See also open issues on the github's issue tracker. diff --git a/avlib.c b/avlib.c index 25e91c8..1bad8fd 100644 --- a/avlib.c +++ b/avlib.c @@ -28,6 +28,7 @@ * distribution. * **********************************************************************************************/ +#include #include #include #include @@ -35,7 +36,6 @@ #include #include #include -#include #ifndef _MSC_VER // for usleep() #include @@ -882,7 +882,7 @@ static void try_unlock_decoder_sem(DecoderContext *ctx) { // we can advance semaphore if both buffers are less than 80% empty if (ringbuffer_len(&p->image_buffer) < 0.8 * p->image_buffer.buf_size && ringbuffer_len(&p->audio_buffer) < 0.8 * p->audio_buffer.buf_size) { - // TraceLog(LOG_INFO, "trying to advance the semaphore..."); + // TraceLog(LOG_INFO, "trying to advance the semaphore..."); semaphore_incr(&p->sem); } } @@ -1129,55 +1129,56 @@ void avray_shutdown(DecoderContext *ctx) { // Raylib specific things #include -const char *fs_yuv420p10 = "#version 130\n" - - "in vec2 fragTexCoord;" - "out vec4 finalColor;" - "uniform sampler2D tex_luma;" - "uniform sampler2D tex_u;" - "uniform sampler2D tex_v;" - - "float samplet(sampler2D t) {" - " vec4 smp = texture(t, fragTexCoord);" - " float res = smp.r * 0.25 + smp.a * 64.0;" - " return res; }" - - "void main() {" - " float smp = samplet(tex_luma);" - " float luma_f = smp;" - " smp = samplet(tex_u);" - " float u_f = smp - 0.5;" - " smp = samplet(tex_v);" - " float v_f = smp - 0.5;" - - " luma_f = 1.1643*(luma_f - 0.0625);" - " float r=luma_f+1.5958*v_f;" - " float g=luma_f-0.39173*u_f-0.81290*v_f;" - " float b=luma_f+2.017*u_f;" - " finalColor=vec4(r, g, b, 1.0);" - "}"; +static const char *fs_yuv420p10 = "#version 130\n" + + "in vec2 fragTexCoord;" + "out vec4 finalColor;" + "uniform sampler2D tex_luma;" + "uniform sampler2D tex_u;" + "uniform sampler2D tex_v;" + + "float samplet(sampler2D t) {" + " vec4 smp = texture(t, fragTexCoord);" + " float res = smp.r * 0.25 + smp.a * 64.0;" + " return res; }" + + "void main() {" + " float smp = samplet(tex_luma);" + " float luma_f = smp;" + " smp = samplet(tex_u);" + " float u_f = smp - 0.5;" + " smp = samplet(tex_v);" + " float v_f = smp - 0.5;" + + " luma_f = 1.1643*(luma_f - 0.0625);" + " float r=luma_f+1.5958*v_f;" + " float g=luma_f-0.39173*u_f-0.81290*v_f;" + " float b=luma_f+2.017*u_f;" + " finalColor=vec4(r, g, b, 1.0);" + "}"; // shader code adopted from // https://stackoverflow.com/questions/30191911/is-it-possible-to-draw-yuv422-and-yuv420-texture-using-opengl -const char *fs_yuv420p = "#version 130 \n" - "in vec2 fragTexCoord;" - "in vec4 fragColor;" - "out vec4 finalColor;" - "uniform sampler2D tex_luma;" - "uniform sampler2D tex_u;" - "uniform sampler2D tex_v;" - "void main() {" - " float luma = texture(tex_luma, fragTexCoord).r;" - " float u = texture(tex_u, fragTexCoord).r;" - " float v = texture(tex_v, fragTexCoord).r;" - " luma=1.1643*(luma-0.0625);" - " u=u-0.5;" - " v=v-0.5;" - " float r=luma+1.5958*v;" - " float g=luma-0.39173*u-0.81290*v;" - " float b=luma+2.017*u;" - " finalColor=vec4(r, g, b, 1.0);" - "}"; +static const char *fs_yuv420p = + "#version 130 \n" + "in vec2 fragTexCoord;" + "in vec4 fragColor;" + "out vec4 finalColor;" + "uniform sampler2D tex_luma;" + "uniform sampler2D tex_u;" + "uniform sampler2D tex_v;" + "void main() {" + " float luma = texture(tex_luma, fragTexCoord).r;" + " float u = texture(tex_u, fragTexCoord).r;" + " float v = texture(tex_v, fragTexCoord).r;" + " luma=1.1643*(luma-0.0625);" + " u=u-0.5;" + " v=v-0.5;" + " float r=luma+1.5958*v;" + " float g=luma-0.39173*u-0.81290*v;" + " float b=luma+2.017*u;" + " finalColor=vec4(r, g, b, 1.0);" + "}"; int avray_init_graphics_objects(DecoderContext *ctx, RaylibObjects *objs) { *objs = (RaylibObjects){}; @@ -1396,15 +1397,15 @@ Vector2 avray_draw_debug_overlay(DecoderContext *ctx, RaylibObjects *objs, text_width = tw; DrawText(msg, x, y, FONTSIZE, WHITE); y += LINEHEIGHT; - snprintf(msg, sizeof(msg), "abytes: %" PRIu64 ", written: %" PRIu64, ctx->abytes_pulled, - ctx->abytes_written); + snprintf(msg, sizeof(msg), "abytes: %" PRIu64 ", written: %" PRIu64, + ctx->abytes_pulled, ctx->abytes_written); tw = MeasureText(msg, FONTSIZE); if (tw > text_width) text_width = tw; DrawText(msg, x, y, FONTSIZE, WHITE); y += LINEHEIGHT; - snprintf(msg, sizeof(msg), "vbytes: %" PRIu64 ", written: %" PRIu64, ctx->vbytes_pulled, - ctx->vbytes_written); + snprintf(msg, sizeof(msg), "vbytes: %" PRIu64 ", written: %" PRIu64, + ctx->vbytes_pulled, ctx->vbytes_written); tw = MeasureText(msg, FONTSIZE); if (tw > text_width) text_width = tw; diff --git a/stb_avray.h b/stb_avray.h index 01f7ebf..1d2a0f7 100644 --- a/stb_avray.h +++ b/stb_avray.h @@ -470,6 +470,7 @@ Vector2 avray_draw_debug_overlay(DecoderContext *ctx, RaylibObjects *objs, bool draw_background); #endif // !AVLIB_H #ifdef AVRAY_IMPLEMENTATION +#include #include #include #include @@ -477,7 +478,6 @@ Vector2 avray_draw_debug_overlay(DecoderContext *ctx, RaylibObjects *objs, #include #include #include -#include #ifndef _MSC_VER // for usleep() #include @@ -1322,7 +1322,7 @@ static void avray__try_unlock_decoder_sem(DecoderContext *ctx) { // we can advance semaphore if both buffers are less than 80% empty if (avray__ringbuffer_len(&p->image_buffer) < 0.8 * p->image_buffer.buf_size && avray__ringbuffer_len(&p->audio_buffer) < 0.8 * p->audio_buffer.buf_size) { - // TraceLog(LOG_INFO, "trying to advance the semaphore..."); + // TraceLog(LOG_INFO, "trying to advance the semaphore..."); avray__semaphore_incr(&p->sem); } } @@ -1569,55 +1569,56 @@ void avray_shutdown(DecoderContext *ctx) { // Raylib specific things #include -const char *fs_yuv420p10 = "#version 130\n" - - "in vec2 fragTexCoord;" - "out vec4 finalColor;" - "uniform sampler2D tex_luma;" - "uniform sampler2D tex_u;" - "uniform sampler2D tex_v;" - - "float samplet(sampler2D t) {" - " vec4 smp = texture(t, fragTexCoord);" - " float res = smp.r * 0.25 + smp.a * 64.0;" - " return res; }" - - "void main() {" - " float smp = samplet(tex_luma);" - " float luma_f = smp;" - " smp = samplet(tex_u);" - " float u_f = smp - 0.5;" - " smp = samplet(tex_v);" - " float v_f = smp - 0.5;" - - " luma_f = 1.1643*(luma_f - 0.0625);" - " float r=luma_f+1.5958*v_f;" - " float g=luma_f-0.39173*u_f-0.81290*v_f;" - " float b=luma_f+2.017*u_f;" - " finalColor=vec4(r, g, b, 1.0);" - "}"; +static const char *fs_yuv420p10 = "#version 130\n" + + "in vec2 fragTexCoord;" + "out vec4 finalColor;" + "uniform sampler2D tex_luma;" + "uniform sampler2D tex_u;" + "uniform sampler2D tex_v;" + + "float samplet(sampler2D t) {" + " vec4 smp = texture(t, fragTexCoord);" + " float res = smp.r * 0.25 + smp.a * 64.0;" + " return res; }" + + "void main() {" + " float smp = samplet(tex_luma);" + " float luma_f = smp;" + " smp = samplet(tex_u);" + " float u_f = smp - 0.5;" + " smp = samplet(tex_v);" + " float v_f = smp - 0.5;" + + " luma_f = 1.1643*(luma_f - 0.0625);" + " float r=luma_f+1.5958*v_f;" + " float g=luma_f-0.39173*u_f-0.81290*v_f;" + " float b=luma_f+2.017*u_f;" + " finalColor=vec4(r, g, b, 1.0);" + "}"; // shader code adopted from // https://stackoverflow.com/questions/30191911/is-it-possible-to-draw-yuv422-and-yuv420-texture-using-opengl -const char *fs_yuv420p = "#version 130 \n" - "in vec2 fragTexCoord;" - "in vec4 fragColor;" - "out vec4 finalColor;" - "uniform sampler2D tex_luma;" - "uniform sampler2D tex_u;" - "uniform sampler2D tex_v;" - "void main() {" - " float luma = texture(tex_luma, fragTexCoord).r;" - " float u = texture(tex_u, fragTexCoord).r;" - " float v = texture(tex_v, fragTexCoord).r;" - " luma=1.1643*(luma-0.0625);" - " u=u-0.5;" - " v=v-0.5;" - " float r=luma+1.5958*v;" - " float g=luma-0.39173*u-0.81290*v;" - " float b=luma+2.017*u;" - " finalColor=vec4(r, g, b, 1.0);" - "}"; +static const char *fs_yuv420p = + "#version 130 \n" + "in vec2 fragTexCoord;" + "in vec4 fragColor;" + "out vec4 finalColor;" + "uniform sampler2D tex_luma;" + "uniform sampler2D tex_u;" + "uniform sampler2D tex_v;" + "void main() {" + " float luma = texture(tex_luma, fragTexCoord).r;" + " float u = texture(tex_u, fragTexCoord).r;" + " float v = texture(tex_v, fragTexCoord).r;" + " luma=1.1643*(luma-0.0625);" + " u=u-0.5;" + " v=v-0.5;" + " float r=luma+1.5958*v;" + " float g=luma-0.39173*u-0.81290*v;" + " float b=luma+2.017*u;" + " finalColor=vec4(r, g, b, 1.0);" + "}"; int avray_init_graphics_objects(DecoderContext *ctx, RaylibObjects *objs) { *objs = (RaylibObjects){}; @@ -1836,15 +1837,15 @@ Vector2 avray_draw_debug_overlay(DecoderContext *ctx, RaylibObjects *objs, text_width = tw; DrawText(msg, x, y, FONTSIZE, WHITE); y += LINEHEIGHT; - snprintf(msg, sizeof(msg), "abytes: %" PRIu64 ", written: %" PRIu64, ctx->abytes_pulled, - ctx->abytes_written); + snprintf(msg, sizeof(msg), "abytes: %" PRIu64 ", written: %" PRIu64, + ctx->abytes_pulled, ctx->abytes_written); tw = MeasureText(msg, FONTSIZE); if (tw > text_width) text_width = tw; DrawText(msg, x, y, FONTSIZE, WHITE); y += LINEHEIGHT; - snprintf(msg, sizeof(msg), "vbytes: %" PRIu64 ", written: %" PRIu64, ctx->vbytes_pulled, - ctx->vbytes_written); + snprintf(msg, sizeof(msg), "vbytes: %" PRIu64 ", written: %" PRIu64, + ctx->vbytes_pulled, ctx->vbytes_written); tw = MeasureText(msg, FONTSIZE); if (tw > text_width) text_width = tw;