Rename binary fr. Add ability to list framerate and throw warning for values not equal to 24. In process converting all output strings to json

This commit is contained in:
mattmcw 2026-04-04 12:52:33 -04:00
parent 25af5c0079
commit 5f46252011
5 changed files with 28 additions and 20 deletions

View File

@ -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)

View File

@ -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 <this-repo>
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 <video_file>
fr <video_file>
```
**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

View File

@ -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<double>(d->fmt.ctx->duration) / AV_TIME_BASE;
}
// Framerate in fps
AVRational fps = stream->avg_frame_rate;
d->framerate = static_cast<double>(fps.num);
return true;
}

View File

@ -41,6 +41,7 @@ public:
int width() const;
int height() const;
double durationSeconds() const;
double framreate() const;
private:
struct Impl;

View File

@ -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";