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:
parent
25af5c0079
commit
5f46252011
|
|
@ -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)
|
||||
|
|
|
|||
13
README.md
13
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 <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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ public:
|
|||
int width() const;
|
||||
int height() const;
|
||||
double durationSeconds() const;
|
||||
double framreate() const;
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
|
|
|
|||
12
main.cpp
12
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";
|
||||
|
|
|
|||
Loading…
Reference in New Issue