diff --git a/CMakeLists.txt b/CMakeLists.txt index 850e11e..34a9efa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.16) -project(video_frame_extractor LANGUAGES CXX) +project(fr LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -7,7 +7,7 @@ set(CMAKE_CXX_EXTENSIONS OFF) # ── Source files ────────────────────────────────────────────────────────────── -add_executable(video_frame_extractor +add_executable(fr main.cpp frame_extractor.cpp tiff_writer.cpp @@ -29,7 +29,7 @@ if(PkgConfig_FOUND) pkg_check_modules(AVUTIL REQUIRED IMPORTED_TARGET libavutil) pkg_check_modules(SWSCALE REQUIRED IMPORTED_TARGET libswscale) - target_link_libraries(video_frame_extractor PRIVATE + target_link_libraries(fr PRIVATE PkgConfig::AVCODEC PkgConfig::AVFORMAT PkgConfig::AVUTIL @@ -50,7 +50,7 @@ else() "Please install pkg-config or pass the FFmpeg paths manually.") endif() - target_include_directories(video_frame_extractor PRIVATE "${FFMPEG_INCLUDE_DIR}") + target_include_directories(fr PRIVATE "${FFMPEG_INCLUDE_DIR}") foreach(_lib avcodec avformat avutil swscale) find_library(_found_${_lib} @@ -61,7 +61,7 @@ else() if(NOT _found_${_lib}) message(FATAL_ERROR "Could not find lib${_lib} in ${FFMPEG_LIB_DIR}") endif() - target_link_libraries(video_frame_extractor PRIVATE "${_found_${_lib}}") + target_link_libraries(fr PRIVATE "${_found_${_lib}}") endforeach() endif() @@ -69,7 +69,7 @@ endif() if(WIN32) # Suppress MSVC warnings about C runtime functions and enable Unicode APIs - target_compile_definitions(video_frame_extractor PRIVATE + target_compile_definitions(fr PRIVATE _CRT_SECURE_NO_WARNINGS UNICODE _UNICODE @@ -78,9 +78,9 @@ endif() if(APPLE) # Silence deprecation warnings that FFmpeg headers can trigger on macOS - target_compile_options(video_frame_extractor PRIVATE -Wno-deprecated-declarations) + target_compile_options(fr PRIVATE -Wno-deprecated-declarations) endif() # ── Install ─────────────────────────────────────────────────────────────────── -install(TARGETS video_frame_extractor RUNTIME DESTINATION bin) +install(TARGETS fr RUNTIME DESTINATION bin) diff --git a/README.md b/README.md index 67ffe3a..72c514a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# video_frame_extractor +# framerip Extracts every frame from a video file and writes them as uncompressed RGB TIFF images into a temporary directory. Compiles on **macOS**, **Linux**, and @@ -13,7 +13,8 @@ TIFF images into a temporary directory. Compiles on **macOS**, **Linux**, and | `libavutil` | FFmpeg utilities | | `libswscale` | Pixel-format conversion | -All four are part of [FFmpeg](https://ffmpeg.org/). No other external libraries +All four are part of [FFmpeg](https://ffmpeg.org/). +No other external libraries are required — TIFF files are written with a hand-rolled encoder. --- @@ -26,7 +27,7 @@ are required — TIFF files are written with a hand-rolled encoder. brew install ffmpeg cmake git clone -cd video_frame_extractor +cd fr cmake -B build -DCMAKE_BUILD_TYPE=Release cmake --build build ``` @@ -89,13 +90,13 @@ cmake --build build --config Release ## Usage ``` -video_frame_extractor +fr ``` **Example:** ```bash -./build/video_frame_extractor sample.mp4 +./build/fr sample.mp4 ``` Output: @@ -137,7 +138,7 @@ ImageMagick, and any other TIFF-capable application. ## Project structure ``` -video_frame_extractor/ +fr/ ├── CMakeLists.txt # Cross-platform build script ├── main.cpp # Entry point ├── frame_extractor.h/.cpp # libav decoding + RGB conversion diff --git a/frame_extractor.cpp b/frame_extractor.cpp index 0957923..bf0e73d 100644 --- a/frame_extractor.cpp +++ b/frame_extractor.cpp @@ -51,6 +51,7 @@ struct FrameExtractor::Impl { int width = 0; int height = 0; double duration = 0.0; + double framerate = 0.0; }; // ── Public API ──────────────────────────────────────────────────────────────── @@ -117,6 +118,11 @@ bool FrameExtractor::open() d->duration = static_cast(d->fmt.ctx->duration) / AV_TIME_BASE; } + // Framerate in fps + + AVRational fps = stream->avg_frame_rate; + d->framerate = static_cast(fps.num); + return true; } diff --git a/frame_extractor.h b/frame_extractor.h index ea6328d..f25d3e4 100644 --- a/frame_extractor.h +++ b/frame_extractor.h @@ -41,6 +41,7 @@ public: int width() const; int height() const; double durationSeconds() const; + double framreate() const; private: struct Impl; diff --git a/main.cpp b/main.cpp index 48faae1..18d49c1 100644 --- a/main.cpp +++ b/main.cpp @@ -37,26 +37,26 @@ int main(int argc, char* argv[]) if (argc >= 3) { outputDir = argv[2]; if (!platform::directoryExists(outputDir)) { - std::cerr << "Output directory does not exist: " << outputDir << "\n"; + std::cerr << "{ \"error\" : \"Output directory does not exist: " << outputDir << "\" }\n"; return 1; } - std::cout << "Output directory: " << outputDir << "\n"; + std::cout << "{ \"Output\" : \"" << outputDir << "\" }\n"; } else { if (!platform::createTempDirectory("vfe_frames_", outputDir)) { - std::cerr << "Failed to create temporary directory.\n"; + std::cerr << "{ \"error\" : \"Failed to create temporary directory.\" }\n"; return 1; } - std::cout << "Output directory: " << outputDir << " (temporary)\n"; + std::cout << "{ \"Output\" : \"" << outputDir << "\", \"Temporary\" : true }\n"; } // Open video FrameExtractor extractor(inputPath); if (!extractor.open()) { - std::cerr << "Failed to open video: " << inputPath << "\n"; + std::cerr << "{ \"error\" : \"Failed to open video: " << inputPath << "\" }\n"; return 1; } - std::cout << "Video: " << inputPath << "\n" + std::cout << "{ \"Video\" : \"" << inputPath << "\" }\n" << "Stream duration: " << extractor.durationSeconds() << "s | " << "Resolution: " << extractor.width() << "x" << extractor.height() << "\n" << "Extracting frames...\n";