From 902449eb9b520ffc831d9566f84a98d6ddcb5e61 Mon Sep 17 00:00:00 2001 From: nabijaczleweli Date: Tue, 26 Dec 2017 07:23:12 +0100 Subject: [PATCH 01/29] Fix CI auth --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ba2e5c3..f6cf30b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -167,7 +167,7 @@ after_success: deploy: provider: releases api-key: - secure: stxnyBJJBRCIL5CdzwQeCGwrZ9Bh1Z8jZA4ojKZ/1RzXyAV/h1g2tQnbbW7cUA7QT4AvITwRDN5CW6KzZfUAqUr2Kz0XoMuE7+zuyFUihVt6Vmilr//ZUpkTbektfJDUvfYA4P4kna9S2zUfE1sW3wGheq5lbi70UVN39Zc1d8aE2dO25DP4Hcqf2HH/sHRA+U7uyMtD75qPlGgcl4YPQaPVyuVfIthMaXYYr2Pt21qIOoMib2oMIKJWLc9ftkBQaIvs9Zt0sECMTmQhaO0li8N4ZpaxiUJsSyGxt/ZkydRjDoReJVkQQO+/p4Ed7jQp8TcaBlSmFivNyd18/L06CX2h0BiawftbtEIDC9U1Xi+7VqVWfk2kIK6MqjnY0AtdsAJmQvTq60e7O1lnHkDZTu3tvXR8v+6HQdofeUM6oU75CRXgmO+MTtxQkyB1v1tDqh7REfYHCO4Q6HKaPVGpJP5XOuY0qN9vragTP5UlVgVDYi0+jzX8zrL+gE0SKm55lYkYrfNqheCvTref1No4K92Lt5fjQbT2LSjp3b/NESvcLHiL42S7KrkhI3qFqJwEYPVXl/3eGyRp/EpGUaNdFh9eZECTBOAaGLVtHQlVgfUpoST+9fBiFuRHmSexlV19Ke3NPa2h229kuIBshFWxq3Dlk9ZJMpWXKzJII36/TAA= + secure: l6Yx5e+Q6o+xxxjYALB2tY/8z5d02o+VAvx1dawEFfUd+FLnRh2IQHvjbJLCr2jqwVtCVeD/RD+D3S02d9LonNl5Og2ueStCzH9JcF5ApsWIW+IlnTnRfL7TfaH/RtoTlmYcsjwon0oe3laM/+B/YuWijtxEv4Nasmk6Nxo453b3wpu8+Xh8SwVfrbvV82niibj/DbCen+ikLn8Z2ZmuOmo8tjObFGIbO0t/h+peH3iJet+oWs+Qh1s/GRyJM/KZJxTyE8mtb5gGpFAqsFz0PUvJBb0zUHtXRqlznOdBHQA8isTQxNZqQGag5LvCuCp+MQnzd8r4xQageY5iT2kN/MqpE8WaY1ePtgHsxT/jd24nhakuhP72bzKq2UojwTMVHgNF4SXcG+QZOLBFZ9eQP1n5FDpSbPbI7rVXB5uc/sR0yFyQF1Q4P0R7kpqI/jAvLf7sOAXmpBgDK28hSpkywe1hk5Hx3V5jFGrdBcPfyq8Zm3YFxVDW3IkfZYJPACYfv2Wsa4VM5pUiZpxsOzE8WIgREm9V8ax5vpggTZtpa1fHKzjODX4wjp4ps8hLJ4y2yhpO98lfckAC0qjLk6MchPosDYQVUEsE0nRyhtD1Zr8DxYC87UY50ZeXs73dYRUgrlQwLAXvGrYedTZq/5ONGvE9kXMpB24lvRYgu48BMNI= file: "$DEPLOY_FILE" skip_cleanup: true on: diff --git a/appveyor.yml b/appveyor.yml index 3de6239..c63eca8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -31,7 +31,7 @@ deploy: provider: GitHub artifact: pictura-mediocritas-v0.1.0.exe auth_token: - secure: /6TsIkMcI5dtUAvr8RJZxjUMR9uDgFLLZDyNbm5HqNCD0zbuj4TUY2JPIte4F+E7 + secure: ulV16u9GQNsy21XpNcP46K2SSbTHTv68LK0UGobzLbTEswEPEg0hso5XTpmuwa7Y on: appveyor_repo_tag: true From d4b9a02383d66cfab77ef03ae5179db825bb0c4c Mon Sep 17 00:00:00 2001 From: nabijaczleweli Date: Sat, 6 Oct 2018 07:03:21 +0200 Subject: [PATCH 02/29] Replace RawGit links with GitHack --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9371218..106a4e0 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Like aurea mediocritas, but with frames in a video instead. Or, you know, get an average frame from a video. -## [Manpage](https://cdn.rawgit.com/LoungeCPP/PicturaMediocritas/man/pictura-mediocritas.1.html) +## [Manpage](https://rawcdn.githack.com/LoungeCPP/PicturaMediocritas/man/pictura-mediocritas.1.html) ## Examples @@ -12,7 +12,7 @@ Or, you know, get an average frame from a video. $ pictura-mediocritas "video.mp4" ``` -For details, see the [manpage](https://cdn.rawgit.com/LoungeCPP/PicturaMediocritas/man/pictura-mediocritas.1.html). +For details, see the [manpage](https://rawcdn.githack.com/LoungeCPP/PicturaMediocritas/man/pictura-mediocritas.1.html). ## Installation From 958feb09b04943bd9badc6cde5b9538263899496 Mon Sep 17 00:00:00 2001 From: nabijaczleweli Date: Sat, 19 Oct 2019 07:26:21 +0200 Subject: [PATCH 03/29] Bump ffmpeg to n4.2.1 --- .gitmodules | 2 +- Makefile | 4 ++-- ext/ffmpeg | 2 +- src/main.cpp | 6 ++---- tests/average_frame.cpp | 6 +++--- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/.gitmodules b/.gitmodules index c8839ff..b567709 100644 --- a/.gitmodules +++ b/.gitmodules @@ -12,4 +12,4 @@ [submodule "ext/ffmpeg"] path = ext/ffmpeg url = https://git.ffmpeg.org/ffmpeg.git - branch = n3.4 + branch = n4.2.1 diff --git a/Makefile b/Makefile index 549e3e7..aed3f78 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ include configMakefile # If ffmpeg autodetects something for your system, you can put it here to link to -FFMPEG_PREREQUESITE_LD_LIBS := bz2 lzma z +FFMPEG_PREREQUESITE_LD_LIBS := bcrypt bz2 lzma z LDAR := $(PIC) $(LNCXXAR) $(foreach l,ffmpeg/lib pb-cpp,-L$(BLDDIR)$(l)) $(foreach l,pb-cpp swscale avformat avcodec avutil freeimage $(FFMPEG_PREREQUESITE_LD_LIBS) $(OS_LD_LIBS),-l$(l)) VERAR := $(foreach l,PICTURA_MEDIOCRITAS CATCH2 PB_CPP TCLAP,-D$(l)_VERSION='$($(l)_VERSION)') INCAR := $(foreach l,$(foreach l,$(foreach l,pb-cpp pb-cpp/ext/optional-lite TCLAP,$(l)/include) Catch2/single_include,ext/$(l)) $(foreach l,ffmpeg,$(BLDDIR)$(l)/include),-isystem$(l)) @@ -57,7 +57,7 @@ $(OUTDIR)pictura-mediocritas-tests$(EXE) : $(subst tests/,$(BLDDIR)test_obj/,$(s $(BLDDIR)ffmpeg/lib/libavcodec$(ARCH) : ext/ffmpeg/configure @mkdir -p $(abspath $(dir $@)..) - cd $(abspath $(dir $@)..) && $(abspath $^) --enable-static $(foreach l,shared programs doc avdevice swresample postproc avfilter iconv jack alsa appkit coreimage sndio schannel securetransport avfoundation encoders filters muxers libxcb libxcb-shm libxcb-xfixes libxcb-shape audiotoolbox encoders filters muxers,--disable-$(l)) --prefix="$(abspath $(dir $@)..)" && $(MAKE) install + cd $(abspath $(dir $@)..) && $(abspath $^) --enable-static $(foreach l,shared programs doc avdevice swresample postproc avfilter iconv libjack alsa appkit coreimage sndio schannel securetransport avfoundation encoders filters muxers libxcb libxcb-shm libxcb-xfixes libxcb-shape audiotoolbox encoders filters muxers,--disable-$(l)) --prefix="$(abspath $(dir $@)..)" && $(MAKE) install $(BLDDIR)pb-cpp/libpb-cpp$(ARCH) : ext/pb-cpp/Makefile @mkdir -p $(abspath $(dir $@)..) diff --git a/ext/ffmpeg b/ext/ffmpeg index 01e291a..1529dfb 160000 --- a/ext/ffmpeg +++ b/ext/ffmpeg @@ -1 +1 @@ -Subproject commit 01e291a592452f27b3a4e811536aaaf94096e244 +Subproject commit 1529dfb73a5157dcb8762051ec4c8d8341762478 diff --git a/src/main.cpp b/src/main.cpp index 7a4b220..23cbf0b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -66,11 +66,9 @@ int main(int argc, const char ** argv) { parser.next(); progress.inc(); } - } else { - av_register_all(); - avcodec_register_all(); - + progress.finish(); + } else { pictura_mediocritas::ffmpeg_parser parser(opts.in_video.c_str(), decltype(avg_frame)::channels); if(parser) { std::unique_ptr progress; diff --git a/tests/average_frame.cpp b/tests/average_frame.cpp index 9677bde..706dec0 100644 --- a/tests/average_frame.cpp +++ b/tests/average_frame.cpp @@ -31,7 +31,7 @@ using namespace std::literals; -namespace Catch { +/*namespace Catch { template <> template struct StringMaker> { @@ -58,7 +58,7 @@ namespace Catch { return oss.str(); } }; -} +}*/ TEST_CASE("pictura_mediocritas::channels", "[average_frame]") { @@ -85,7 +85,7 @@ TEST_CASE("pictura_mediocritas::channels", "[average_frame]") { } TEST_CASE("pictura_mediocritas::value_type", "[average_frame]") { -#define TYPES(t) \ +#define TYPES(t) \ REQUIRE((std::is_same::value_type, t>::value)); \ REQUIRE((std::is_same::value_type, t>::value)); \ REQUIRE((std::is_same::value_type, t>::value)); \ From b7a31b0704e00eef4274bae84decb92e0c7d25b5 Mon Sep 17 00:00:00 2001 From: nabijaczleweli Date: Sat, 19 Oct 2019 07:52:17 +0200 Subject: [PATCH 04/29] Bump pb-cpp to v0.1.3. Bump Catch2 to v2.10.1 --- .gitmodules | 4 ++-- Makefile | 2 +- ext/Catch2 | 2 +- ext/pb-cpp | 2 +- tests/average_frame.cpp | 23 ++--------------------- 5 files changed, 7 insertions(+), 26 deletions(-) diff --git a/.gitmodules b/.gitmodules index b567709..8828502 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,11 +4,11 @@ [submodule "ext/Catch2"] path = ext/Catch2 url = https://github.com/catchorg/Catch2 - branch = v2.0.1 + branch = v2.10.1 [submodule "ext/pb-cpp"] path = ext/pb-cpp url = https://github.com/nabijaczleweli/pb-cpp - branch = v0.1.1 + branch = v0.1.3 [submodule "ext/ffmpeg"] path = ext/ffmpeg url = https://git.ffmpeg.org/ffmpeg.git diff --git a/Makefile b/Makefile index aed3f78..c8e4992 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ include configMakefile FFMPEG_PREREQUESITE_LD_LIBS := bcrypt bz2 lzma z LDAR := $(PIC) $(LNCXXAR) $(foreach l,ffmpeg/lib pb-cpp,-L$(BLDDIR)$(l)) $(foreach l,pb-cpp swscale avformat avcodec avutil freeimage $(FFMPEG_PREREQUESITE_LD_LIBS) $(OS_LD_LIBS),-l$(l)) VERAR := $(foreach l,PICTURA_MEDIOCRITAS CATCH2 PB_CPP TCLAP,-D$(l)_VERSION='$($(l)_VERSION)') -INCAR := $(foreach l,$(foreach l,$(foreach l,pb-cpp pb-cpp/ext/optional-lite TCLAP,$(l)/include) Catch2/single_include,ext/$(l)) $(foreach l,ffmpeg,$(BLDDIR)$(l)/include),-isystem$(l)) +INCAR := $(foreach l,$(foreach l,$(foreach l,pb-cpp pb-cpp/ext/optional-lite TCLAP,$(l)/include) Catch2/single_include/catch2,ext/$(l)) $(foreach l,ffmpeg,$(BLDDIR)$(l)/include),-isystem$(l)) TEST_SOURCES := $(sort $(wildcard tests/*.cpp tests/**/*.cpp tests/**/**/*.cpp tests/**/**/**/*.cpp)) BUILD_TEST_SOURCES := $(sort $(wildcard build-tests/*.cpp build-tests/**/*.cpp build-tests/**/**/*.cpp build-tests/**/**/**/*.cpp)) SOURCES := $(sort $(wildcard src/*.cpp src/**/*.cpp src/**/**/*.cpp src/**/**/**/*.cpp)) diff --git a/ext/Catch2 b/ext/Catch2 index 19ab211..a2c8dce 160000 --- a/ext/Catch2 +++ b/ext/Catch2 @@ -1 +1 @@ -Subproject commit 19ab2117c5bac2f376f8da4a4b25e183137bcec0 +Subproject commit a2c8dce85c554aa30607914c57be300feaa3b05f diff --git a/ext/pb-cpp b/ext/pb-cpp index 35c52af..ccf28ba 160000 --- a/ext/pb-cpp +++ b/ext/pb-cpp @@ -1 +1 @@ -Subproject commit 35c52af00b9f414fe9bd1ec38b9d88bfed80f5ff +Subproject commit ccf28ba3dd7a3ec7898bc155f6b14755cc79a8f3 diff --git a/tests/average_frame.cpp b/tests/average_frame.cpp index 706dec0..79015cf 100644 --- a/tests/average_frame.cpp +++ b/tests/average_frame.cpp @@ -31,8 +31,7 @@ using namespace std::literals; -/*namespace Catch { - template <> +namespace Catch { template struct StringMaker> { static std::string convert(const pictura_mediocritas::average_frame & value) { @@ -40,25 +39,7 @@ using namespace std::literals; ", frames=" + StringMaker::convert(value.processed_frames()) + "}"; } }; - - template <> - template - struct StringMaker> { - static std::string convert(const std::array & value) { - std::stringstream oss; - oss << "["; - bool first = true; - for(auto && v : value) { - if(!first) - oss << ", "; - first = false; - oss << v; - } - oss << "]"; - return oss.str(); - } - }; -}*/ +} TEST_CASE("pictura_mediocritas::channels", "[average_frame]") { From c541af05aee1c3271643fb4bd9820313df63dfe7 Mon Sep 17 00:00:00 2001 From: nabijaczleweli Date: Sun, 20 Oct 2019 07:26:15 +0200 Subject: [PATCH 05/29] Final pass over README et al. Closes #9 --- .travis.yml | 4 +++- Makefile | 2 +- README.md | 4 +++- appveyor.yml | 2 ++ pictura-mediocritas.md | 2 ++ 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index f6cf30b..533b5a6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -69,6 +69,8 @@ matrix: - env: LANGUAGE=Ruby DEPLOY=true DEPLOY_FILE="$TRAVIS_BUILD_DIR/../pictura-mediocritas-man-$TRAVIS_TAG.tbz2" language: ruby rvm: "2.2" + allow_failures: + - env: LANGUAGE=C++ CXX="clang++-5.0 -stdlib=libc++" CC=clang-5.0 before_install: - if [ "$TRAVIS_SECURE_ENV_VARS" == "true" ]; then @@ -167,7 +169,7 @@ after_success: deploy: provider: releases api-key: - secure: l6Yx5e+Q6o+xxxjYALB2tY/8z5d02o+VAvx1dawEFfUd+FLnRh2IQHvjbJLCr2jqwVtCVeD/RD+D3S02d9LonNl5Og2ueStCzH9JcF5ApsWIW+IlnTnRfL7TfaH/RtoTlmYcsjwon0oe3laM/+B/YuWijtxEv4Nasmk6Nxo453b3wpu8+Xh8SwVfrbvV82niibj/DbCen+ikLn8Z2ZmuOmo8tjObFGIbO0t/h+peH3iJet+oWs+Qh1s/GRyJM/KZJxTyE8mtb5gGpFAqsFz0PUvJBb0zUHtXRqlznOdBHQA8isTQxNZqQGag5LvCuCp+MQnzd8r4xQageY5iT2kN/MqpE8WaY1ePtgHsxT/jd24nhakuhP72bzKq2UojwTMVHgNF4SXcG+QZOLBFZ9eQP1n5FDpSbPbI7rVXB5uc/sR0yFyQF1Q4P0R7kpqI/jAvLf7sOAXmpBgDK28hSpkywe1hk5Hx3V5jFGrdBcPfyq8Zm3YFxVDW3IkfZYJPACYfv2Wsa4VM5pUiZpxsOzE8WIgREm9V8ax5vpggTZtpa1fHKzjODX4wjp4ps8hLJ4y2yhpO98lfckAC0qjLk6MchPosDYQVUEsE0nRyhtD1Zr8DxYC87UY50ZeXs73dYRUgrlQwLAXvGrYedTZq/5ONGvE9kXMpB24lvRYgu48BMNI= + secure: jKKJHvnWlVzfHVn67OcSbtJTiDSHpAu7bMRs0Of64q2fqb6EAjL0l8+9ckgY1TnLU9AIXZrFJzkrt45t7qbWPb2ZsAUxd/O7n6cZuh+TDYkYj3SUsfNx9CAV8Gxb86GVogg1mXDuqYS+FKCK1lsfSV0y50bMkLz5oVtLP6Iq8/JkCugCO/XQluXCoE6tDH/lCVl3yLsgtG66Trv7bufLS05D9bnEAg/UvI4W4oxucnxRpwd0rSRCHi+rmaSjDVRFIvGTj8RzLtGm8SJTeedulT5CWXr7pvXxo19cnWSyXeRhjjrnV2aM3JEtDYerqeIW0e1TyRSs2q9oUmMFs1fQ2ZGwm5fLjeYSB2jVU637mdUnh/SmrvXQ5mv+IZTI/RejUm16qLqH4/IFlxkGmAwyhLtj0A+thula+PuZ2RMd2FfymVGvEklC7/hyWTjE3kJESc2uv0xph9iceJrLaQI4qXI++uJPVJntto7hoBEJWTLcc0nVvigjMLkCfmfifzb/KwItYyImHHjCs8dCYc771Oup3jEqHDprmiJtizp+mAi3cNIr3mCtspcll7cxELg9RBR9AUIhyql0FtfH1qatFQYB8KYyUMeLdmJRzkz023dCMznenkgIm/Pg7KzQjGxsRePJMA4qR4FxMOGnnvZMwziHh7DWQu6y7bpfMh8NVAw= file: "$DEPLOY_FILE" skip_cleanup: true on: diff --git a/Makefile b/Makefile index c8e4992..f37da37 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ include configMakefile # If ffmpeg autodetects something for your system, you can put it here to link to -FFMPEG_PREREQUESITE_LD_LIBS := bcrypt bz2 lzma z +FFMPEG_PREREQUESITE_LD_LIBS := bz2 lzma z LDAR := $(PIC) $(LNCXXAR) $(foreach l,ffmpeg/lib pb-cpp,-L$(BLDDIR)$(l)) $(foreach l,pb-cpp swscale avformat avcodec avutil freeimage $(FFMPEG_PREREQUESITE_LD_LIBS) $(OS_LD_LIBS),-l$(l)) VERAR := $(foreach l,PICTURA_MEDIOCRITAS CATCH2 PB_CPP TCLAP,-D$(l)_VERSION='$($(l)_VERSION)') INCAR := $(foreach l,$(foreach l,$(foreach l,pb-cpp pb-cpp/ext/optional-lite TCLAP,$(l)/include) Catch2/single_include/catch2,ext/$(l)) $(foreach l,ffmpeg,$(BLDDIR)$(l)/include),-isystem$(l)) diff --git a/README.md b/README.md index 106a4e0..1b9fe7a 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,10 @@ Or, you know, get an average frame from a video. ## Examples ```sh -# TODO: concretify $ pictura-mediocritas "video.mp4" +# Averaged frame in "video.png" + +$ pictura-mediocritas "video.mp4" "video-average.jpg" ``` For details, see the [manpage](https://rawcdn.githack.com/LoungeCPP/PicturaMediocritas/man/pictura-mediocritas.1.html). diff --git a/appveyor.yml b/appveyor.yml index c63eca8..1c2d842 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -13,6 +13,8 @@ install: - bash -lc "pacman --needed --noconfirm -Sy pacman-mirrors" - bash -lc "pacman --noconfirm -Syyu" - bash -lc "pacman --noconfirm -Su mingw-w64-x86_64-freeimage mingw-w64-x86_64-nasm" + - + - sed -i "s/FFMPEG_PREREQUESITE_LD_LIBS :=/\0 bcrypt/" Makefile build: off build_script: diff --git a/pictura-mediocritas.md b/pictura-mediocritas.md index 43d8b66..f70c473 100644 --- a/pictura-mediocritas.md +++ b/pictura-mediocritas.md @@ -17,6 +17,8 @@ Or, you know, get an average frame from a video. Video to average, must exist. + Can be in any format supported by ffmpeg and gif. + [OUT_IMAGE] Image to write the averaged frame to. From 22261c3e8b8a24833673a517df56fa95901e9846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Fri, 22 Mar 2024 12:14:32 +0100 Subject: [PATCH 06/29] modern libav can understand gifs, and much faster --- src/main.cpp | 61 ++++++++------------- src/parser/multi_image.cpp | 105 ------------------------------------- src/parser/multi_image.hpp | 85 ------------------------------ 3 files changed, 23 insertions(+), 228 deletions(-) delete mode 100644 src/parser/multi_image.cpp delete mode 100644 src/parser/multi_image.hpp diff --git a/src/main.cpp b/src/main.cpp index 23cbf0b..8ebe352 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -55,45 +55,30 @@ int main(int argc, const char ** argv) { pictura_mediocritas::average_frame_u64 avg_frame(0, 0); - if(pictura_mediocritas::has_extension(opts.in_video.c_str(), "gif")) { - pictura_mediocritas::multi_image_parser parser(FreeImage_OpenMultiBitmap(FIF_GIF, opts.in_video.c_str(), false, true, true, GIF_LOAD256 | GIF_PLAYBACK), - decltype(avg_frame)::channels); - pictura_mediocritas::progressbar progress("Processing " + opts.in_video + ' ', parser.length()); - avg_frame = decltype(avg_frame)(parser.size()); - - for(auto i = 0u; i < parser.length(); ++i) { - avg_frame.process_frame(parser); - parser.next(); - progress.inc(); - } - - progress.finish(); - } else { - pictura_mediocritas::ffmpeg_parser parser(opts.in_video.c_str(), decltype(avg_frame)::channels); - if(parser) { - std::unique_ptr progress; - if(!parser.process([&]() { - if(!progress) - progress = std::make_unique("Processing " + opts.in_video + ' ', parser.length()); - if(avg_frame.size().first == 0) - avg_frame = decltype(avg_frame)(parser.size()); - - avg_frame.process_frame(parser); - progress->inc(); - - return true; - })) { - std::cerr << "\nParsing " << opts.in_video << " failed: " << *parser.error() << '\n'; - return 1; - } else - progress->finish(); - } else if(parser.error() == "") { - std::cerr << "Couldn't open " << opts.in_video << ".\n"; - return 1; - } else { - std::cerr << "Could not find codec for " << opts.in_video << ": " << *parser.error() << '\n'; + pictura_mediocritas::ffmpeg_parser parser(opts.in_video.c_str(), decltype(avg_frame)::channels); + if(parser) { + std::unique_ptr progress; + if(!parser.process([&]() { + if(!progress) + progress = std::make_unique("Processing " + opts.in_video + ' ', parser.length()); + if(avg_frame.size().first == 0) + avg_frame = decltype(avg_frame)(parser.size()); + + avg_frame.process_frame(parser); + progress->inc(); + + return true; + })) { + std::cerr << "\nParsing " << opts.in_video << " failed: " << *parser.error() << '\n'; return 1; - } + } else + progress->finish(); + } else if(parser.error() == "") { + std::cerr << "Couldn't open " << opts.in_video << ".\n"; + return 1; + } else { + std::cerr << "Could not find codec for " << opts.in_video << ": " << *parser.error() << '\n'; + return 1; } diff --git a/src/parser/multi_image.cpp b/src/parser/multi_image.cpp deleted file mode 100644 index caa8107..0000000 --- a/src/parser/multi_image.cpp +++ /dev/null @@ -1,105 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#include "multi_image.hpp" -#include - - -void pictura_mediocritas::freeimage_multi_bitmap_deleter::operator()(FIMULTIBITMAP * multi_bitmap) const noexcept { - FreeImage_CloseMultiBitmap(multi_bitmap); -} - -void pictura_mediocritas::freeimage_page_unlocker::operator()(FIBITMAP * page) const noexcept { - FreeImage_UnlockPage(owner, page, changed); -} - - -void pictura_mediocritas::multi_image_parser::lock_page() { - cur_page.reset(FreeImage_LockPage(image.get(), cur_page_idx)); - width = 0; - height = 0; -} - -pictura_mediocritas::multi_image_parser::multi_image_parser(FIMULTIBITMAP * i, std::size_t c) - : channels(c), image(i), cur_page(0, freeimage_page_unlocker{i, false}), cur_page_idx(0), pages(0), width(0), height(0), cached(false) { - lock_page(); -} - -std::pair pictura_mediocritas::multi_image_parser::size() noexcept { - if(cur_page && (!width || !height)) { - width = FreeImage_GetWidth(cur_page.get()); - height = FreeImage_GetHeight(cur_page.get()); - } - - return {width, height}; -} - -std::size_t pictura_mediocritas::multi_image_parser::length() noexcept { - if(!pages) - pages = FreeImage_GetPageCount(image.get()); - - return pages; -} - -void pictura_mediocritas::multi_image_parser::next() { - ++cur_page_idx; - lock_page(); -} - -std::uint8_t pictura_mediocritas::multi_image_parser::operator[](std::size_t idx) { - if(!channels) - return -1; - - const auto width = size().first; - if(!width) - return -1; - - const auto channel = idx % channels; // idx = (y * width + x) * decltype(frame)::channels + channel - idx -= channel; // idx = (y * width + x) * decltype(frame)::channels - idx /= channels; // idx = y * width + x - const auto x = idx % width; // - idx -= x; // idx = y * width - idx /= width; // idx = y - const auto y = idx; // - - if(!cached || (cache_x != x || cache_y != y)) { - cached = FreeImage_GetPixelColor(cur_page.get(), x, y, &cache); - cache_x = x; - cache_y = y; - } - - if(cached) - switch(channel) { - case 0: - return cache.rgbRed; - case 1: - return cache.rgbGreen; - case 2: - return cache.rgbBlue; - case 3: - return cache.rgbReserved; - } - - std::cerr << "Failed to get pixel " << x << 'x' << y << " frame #" << cur_page_idx << ".\n"; - return -1; -} diff --git a/src/parser/multi_image.hpp b/src/parser/multi_image.hpp deleted file mode 100644 index 7371add..0000000 --- a/src/parser/multi_image.hpp +++ /dev/null @@ -1,85 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#pragma once - - -#include -#include -#include - - -namespace pictura_mediocritas { - struct freeimage_multi_bitmap_deleter { - void operator()(FIMULTIBITMAP * multi_bitmap) const noexcept; - }; - - struct freeimage_page_unlocker { - FIMULTIBITMAP * owner; - bool changed; - - void operator()(FIBITMAP * page) const noexcept; - }; - - class multi_image_parser { - private: - std::size_t channels; - - std::unique_ptr image; - std::unique_ptr cur_page; - std::size_t cur_page_idx; - - std::size_t pages; - std::size_t width; - std::size_t height; - - bool cached; - RGBQUAD cache; - std::size_t cache_x; - std::size_t cache_y; - - void lock_page(); - - public: - /// Create a parser from an opened multi-image and the channel count in the target frame. - multi_image_parser(FIMULTIBITMAP * image, std::size_t channels); - - - /// Get current frame's size as `{width, height}`. - std::pair size() noexcept; - - /// Get frame count. - std::size_t length() noexcept; - - /// Load the next frame. - void next(); - - /// Get the specified colour fragment. - /// - /// Valid inside [0; width * height * channels) ∩ ℤ, undefined behaviour thereoutside. - /// - /// `idx` is in the format `(y * width + x) * decltype(frame)::channels + channel` - /// (i.e. the one required by `average_frame`). - std::uint8_t operator[](std::size_t idx); - }; -} From 3986543d4ff83dbfa0ffadcd88bf3de6528e8928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Fri, 22 Mar 2024 12:41:14 +0100 Subject: [PATCH 07/29] Normal parsing, no TCLAP --- .gitmodules | 3 -- ext/TCLAP | 1 - src/{options => }/options.cpp | 52 ++++++------------- src/{options => }/options.hpp | 3 +- src/options/existing_file_constraint.cpp | 40 -------------- src/options/existing_file_constraint.hpp | 45 ---------------- .../existing_parent_dir_constraint.cpp | 46 ---------------- .../existing_parent_dir_constraint.hpp | 45 ---------------- 8 files changed, 19 insertions(+), 216 deletions(-) delete mode 160000 ext/TCLAP rename src/{options => }/options.cpp (51%) rename src/{options => }/options.hpp (97%) delete mode 100644 src/options/existing_file_constraint.cpp delete mode 100644 src/options/existing_file_constraint.hpp delete mode 100644 src/options/existing_parent_dir_constraint.cpp delete mode 100644 src/options/existing_parent_dir_constraint.hpp diff --git a/.gitmodules b/.gitmodules index 8828502..bbbcea3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "ext/TCLAP"] - path = ext/TCLAP - url = git://git.code.sf.net/p/tclap/code [submodule "ext/Catch2"] path = ext/Catch2 url = https://github.com/catchorg/Catch2 diff --git a/ext/TCLAP b/ext/TCLAP deleted file mode 160000 index 3d02fed..0000000 --- a/ext/TCLAP +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3d02fed104fc9cfd1831195cb2ab82b4e08c02c8 diff --git a/src/options/options.cpp b/src/options.cpp similarity index 51% rename from src/options/options.cpp rename to src/options.cpp index 33ef06a..f0bc2b4 100644 --- a/src/options/options.cpp +++ b/src/options.cpp @@ -21,46 +21,28 @@ #include "options.hpp" -#include "../util.hpp" -#include "existing_file_constraint.hpp" -#include "existing_parent_dir_constraint.hpp" -#include -#include -#include - +#include "util.hpp" using namespace std::literals; std::tuple pictura_mediocritas::options::parse(int argc, const char * const * argv) { - options ret; - - try { - pictura_mediocritas::existing_file_constraint input_video_constraint("input video"); - pictura_mediocritas::existing_parent_dir_constraint out_image_constraint("output image"); - - TCLAP::CmdLine command_line("Pictūra Mediocritas -- like aurea mediocritas, but with frames in a video instead", ' ', PICTURA_MEDIOCRITAS_VERSION); - TCLAP::UnlabeledValueArg in_video("in_video", "Video to average", true, "", &input_video_constraint, command_line); - TCLAP::UnlabeledValueArg out_image("out_image", "Image to write the average frame to. Default: in_video.png", false, "", &out_image_constraint, - command_line); - - command_line.setExceptionHandling(false); - command_line.parse(argc, argv); - - ret.in_video = in_video; - - if(out_image.getValue().empty()) - ret.out_image = switch_extenstion(ret.in_video, "png"); - else - ret.out_image = out_image; - } catch(const TCLAP::ArgException & e) { - auto arg_id = e.argId(); - if(arg_id == " ") - arg_id = "undefined argument"; - return std::make_tuple(ret, 1, std::string(argv[0]) + ": error: parsing arguments failed (" + e.error() + ") for " + arg_id); - } catch(const TCLAP::ExitException & e) { - return std::make_tuple(ret, e.getExitStatus() ? e.getExitStatus() : 1, ""); - } + auto self = *argv ? *argv : "pictura-mediocritas"; + if(*argv) + ++argv; + + options ret{}; + if(!*argv) + usage: + return std::make_tuple(ret, 1, ("usage: "s += self) += " in-video [out-image]"); + ret.in_video = *argv++; + + if(*argv) { + ret.out_image = *argv++; + if(*argv) + goto usage; + } else + ret.out_image = switch_extenstion(ret.in_video, "png"); return std::make_tuple(ret, 0, ""s); } diff --git a/src/options/options.hpp b/src/options.hpp similarity index 97% rename from src/options/options.hpp rename to src/options.hpp index ec1d9a7..d5bb4e1 100644 --- a/src/options/options.hpp +++ b/src/options.hpp @@ -24,6 +24,7 @@ #include +#include #include @@ -33,7 +34,7 @@ namespace pictura_mediocritas { /// Path to the video file to analyse. /// /// Must exist. - std::string in_video; + std::string_view in_video; /// Path to the image file to write the result to. /// /// Parent directory must exist. diff --git a/src/options/existing_file_constraint.cpp b/src/options/existing_file_constraint.cpp deleted file mode 100644 index abb7e65..0000000 --- a/src/options/existing_file_constraint.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#include "existing_file_constraint.hpp" -#include "../util.hpp" - - -std::string pictura_mediocritas::existing_file_constraint::description() const { - return "existing file"; -} - -std::string pictura_mediocritas::existing_file_constraint::shortID() const { - return arg_name; -} - -bool pictura_mediocritas::existing_file_constraint::check(const std::string & value) const { - return file_exists(value.c_str()); -} - -pictura_mediocritas::existing_file_constraint::existing_file_constraint(std::string argname) : arg_name(std::move(argname)) {} diff --git a/src/options/existing_file_constraint.hpp b/src/options/existing_file_constraint.hpp deleted file mode 100644 index 42046df..0000000 --- a/src/options/existing_file_constraint.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#pragma once - - -#include -#include - - -namespace pictura_mediocritas { - class existing_file_constraint : public TCLAP::Constraint { - private: - std::string arg_name; - - public: - virtual std::string description() const override; - virtual std::string shortID() const override; - virtual bool check(const std::string & value) const override; - - existing_file_constraint(std::string argname); - - virtual ~existing_file_constraint() = default; - }; -} diff --git a/src/options/existing_parent_dir_constraint.cpp b/src/options/existing_parent_dir_constraint.cpp deleted file mode 100644 index 97a6df1..0000000 --- a/src/options/existing_parent_dir_constraint.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#include "existing_parent_dir_constraint.hpp" -#include "../util.hpp" - - -std::string pictura_mediocritas::existing_parent_dir_constraint::description() const { - return "creatable file"; -} - -std::string pictura_mediocritas::existing_parent_dir_constraint::shortID() const { - return arg_name; -} - -bool pictura_mediocritas::existing_parent_dir_constraint::check(const std::string & value) const { - auto last_slash_idx = value.find_last_of("\\/"); - if(last_slash_idx == std::string::npos) - return directory_exists("."); - else { - return directory_exists(value.substr(0, last_slash_idx).c_str()) && last_slash_idx < value.size() - 1; - } -} - -pictura_mediocritas::existing_parent_dir_constraint::existing_parent_dir_constraint(std::string argname) : arg_name(std::move(argname)) {} - diff --git a/src/options/existing_parent_dir_constraint.hpp b/src/options/existing_parent_dir_constraint.hpp deleted file mode 100644 index 12006f7..0000000 --- a/src/options/existing_parent_dir_constraint.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#pragma once - - -#include -#include - - -namespace pictura_mediocritas { - class existing_parent_dir_constraint : public TCLAP::Constraint { - private: - std::string arg_name; - - public: - virtual std::string description() const override; - virtual std::string shortID() const override; - virtual bool check(const std::string & value) const override; - - existing_parent_dir_constraint(std::string argname); - - virtual ~existing_parent_dir_constraint() = default; - }; -} From 904b0b7dfcb3c5013ba0fc313f470f64696b3d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Fri, 22 Mar 2024 12:47:11 +0100 Subject: [PATCH 08/29] Delete progressbar (and pb-cpp). Delete ffmpeg dep and port to modern ffmpeg --- .gitmodules | 8 --- Makefile | 20 ++---- configMakefile | 2 - ext/ffmpeg | 1 - ext/pb-cpp | 1 - src/main.cpp | 16 ++--- src/parser/ffmpeg.hpp | 2 +- src/progressbar/os_progressbar.hpp | 72 ------------------- .../os_progressbar_non_windows.cpp | 40 ----------- src/progressbar/os_progressbar_windows.cpp | 62 ---------------- src/progressbar/progressbar.cpp | 60 ---------------- src/progressbar/progressbar.hpp | 59 --------------- 12 files changed, 10 insertions(+), 333 deletions(-) delete mode 160000 ext/ffmpeg delete mode 160000 ext/pb-cpp delete mode 100644 src/progressbar/os_progressbar.hpp delete mode 100644 src/progressbar/os_progressbar_non_windows.cpp delete mode 100644 src/progressbar/os_progressbar_windows.cpp delete mode 100644 src/progressbar/progressbar.cpp delete mode 100644 src/progressbar/progressbar.hpp diff --git a/.gitmodules b/.gitmodules index bbbcea3..7642872 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,11 +2,3 @@ path = ext/Catch2 url = https://github.com/catchorg/Catch2 branch = v2.10.1 -[submodule "ext/pb-cpp"] - path = ext/pb-cpp - url = https://github.com/nabijaczleweli/pb-cpp - branch = v0.1.3 -[submodule "ext/ffmpeg"] - path = ext/ffmpeg - url = https://git.ffmpeg.org/ffmpeg.git - branch = n4.2.1 diff --git a/Makefile b/Makefile index f37da37..99e4d84 100644 --- a/Makefile +++ b/Makefile @@ -25,16 +25,16 @@ include configMakefile # If ffmpeg autodetects something for your system, you can put it here to link to FFMPEG_PREREQUESITE_LD_LIBS := bz2 lzma z -LDAR := $(PIC) $(LNCXXAR) $(foreach l,ffmpeg/lib pb-cpp,-L$(BLDDIR)$(l)) $(foreach l,pb-cpp swscale avformat avcodec avutil freeimage $(FFMPEG_PREREQUESITE_LD_LIBS) $(OS_LD_LIBS),-l$(l)) -VERAR := $(foreach l,PICTURA_MEDIOCRITAS CATCH2 PB_CPP TCLAP,-D$(l)_VERSION='$($(l)_VERSION)') -INCAR := $(foreach l,$(foreach l,$(foreach l,pb-cpp pb-cpp/ext/optional-lite TCLAP,$(l)/include) Catch2/single_include/catch2,ext/$(l)) $(foreach l,ffmpeg,$(BLDDIR)$(l)/include),-isystem$(l)) +LDAR := $(PIC) $(LNCXXAR) $(foreach l,swscale avformat avcodec avutil freeimage $(FFMPEG_PREREQUESITE_LD_LIBS) $(OS_LD_LIBS),-l$(l)) +VERAR := $(foreach l,PICTURA_MEDIOCRITAS CATCH2,-D$(l)_VERSION='$($(l)_VERSION)') +INCAR := $(foreach l,$(foreach l,Catch2/single_include,ext/$(l)) $(foreach l,ffmpeg,$(BLDDIR)$(l)/include),-isystem$(l)) TEST_SOURCES := $(sort $(wildcard tests/*.cpp tests/**/*.cpp tests/**/**/*.cpp tests/**/**/**/*.cpp)) BUILD_TEST_SOURCES := $(sort $(wildcard build-tests/*.cpp build-tests/**/*.cpp build-tests/**/**/*.cpp build-tests/**/**/**/*.cpp)) SOURCES := $(sort $(wildcard src/*.cpp src/**/*.cpp src/**/**/*.cpp src/**/**/**/*.cpp)) -.PHONY : all clean ffmpeg pb-cpp exe tests no-build-tests run-tests +.PHONY : all clean exe tests no-build-tests run-tests -all : ffmpeg pb-cpp exe tests no-build-tests run-tests +all : exe tests no-build-tests run-tests clean : rm -rf $(OUTDIR) @@ -45,8 +45,6 @@ run-tests : $(OUTDIR)pictura-mediocritas-tests$(EXE) exe : $(OUTDIR)pictura-mediocritas$(EXE) tests : $(OUTDIR)pictura-mediocritas-tests$(EXE) no-build-tests : $(subst build-tests/,$(BLDDIR)build_test_obj/,$(subst .cpp,$(OBJ),$(BUILD_TEST_SOURCES))) -ffmpeg : $(BLDDIR)ffmpeg/lib/libavcodec$(ARCH) -pb-cpp : $(BLDDIR)pb-cpp/libpb-cpp$(ARCH) $(OUTDIR)pictura-mediocritas$(EXE) : $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,$(OBJ),$(SOURCES))) @@ -55,14 +53,6 @@ $(OUTDIR)pictura-mediocritas$(EXE) : $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,$( $(OUTDIR)pictura-mediocritas-tests$(EXE) : $(subst tests/,$(BLDDIR)test_obj/,$(subst .cpp,$(OBJ),$(TEST_SOURCES))) $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,$(OBJ),$(filter-out src/main.cpp,$(SOURCES)))) $(CXX) $(CXXAR) -o$@ $^ $(PIC) $(LDAR) -$(BLDDIR)ffmpeg/lib/libavcodec$(ARCH) : ext/ffmpeg/configure - @mkdir -p $(abspath $(dir $@)..) - cd $(abspath $(dir $@)..) && $(abspath $^) --enable-static $(foreach l,shared programs doc avdevice swresample postproc avfilter iconv libjack alsa appkit coreimage sndio schannel securetransport avfoundation encoders filters muxers libxcb libxcb-shm libxcb-xfixes libxcb-shape audiotoolbox encoders filters muxers,--disable-$(l)) --prefix="$(abspath $(dir $@)..)" && $(MAKE) install - -$(BLDDIR)pb-cpp/libpb-cpp$(ARCH) : ext/pb-cpp/Makefile - @mkdir -p $(abspath $(dir $@)..) - cd $(dir $^) && $(MAKE) OUTDIR="$(abspath $(dir $@))/" static - $(OBJDIR)%$(OBJ) : $(SRCDIR)%.cpp @mkdir -p $(dir $@) diff --git a/configMakefile b/configMakefile index 0cbf657..2f4ff3f 100644 --- a/configMakefile +++ b/configMakefile @@ -48,8 +48,6 @@ endif # PICTURA_MEDIOCRITAS_VERSION := "$(patsubst v%,%,$(shell git describe --tags --abbrev=0))" PICTURA_MEDIOCRITAS_VERSION := "0.1.0" CATCH2_VERSION := "$(patsubst v%,%,$(shell git -C ext/Catch2 describe --tags --abbrev=0))" -PB_CPP_VERSION := "$(patsubst v%,%,$(shell git -C ext/pb-cpp describe --tags --abbrev=0))" -TCLAP_VERSION := "$(shell cat ext/TCLAP/configure.in | grep AM_INIT_AUTOMAKE | sed "s/.*(tclap,\(.*\))/\1/")" OBJ := .o ARCH := .a diff --git a/ext/ffmpeg b/ext/ffmpeg deleted file mode 160000 index 1529dfb..0000000 --- a/ext/ffmpeg +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1529dfb73a5157dcb8762051ec4c8d8341762478 diff --git a/ext/pb-cpp b/ext/pb-cpp deleted file mode 160000 index ccf28ba..0000000 --- a/ext/pb-cpp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ccf28ba3dd7a3ec7898bc155f6b14755cc79a8f3 diff --git a/src/main.cpp b/src/main.cpp index 8ebe352..68ecfc4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,8 +20,6 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -#include "progressbar/progressbar.hpp" - #include "average_frame.hpp" #include "options/options.hpp" #include "output_image.hpp" @@ -46,33 +44,27 @@ int main(int argc, const char ** argv) { } const auto opts = std::move(std::get<0>(opts_r)); -#ifdef _WIN32 - CoInitialize(nullptr); -#endif FreeImage_Initialise(); pictura_mediocritas::quickscope_wrapper freeimage_deinitialiser{FreeImage_DeInitialise}; pictura_mediocritas::average_frame_u64 avg_frame(0, 0); - pictura_mediocritas::ffmpeg_parser parser(opts.in_video.c_str(), decltype(avg_frame)::channels); + pictura_mediocritas::ffmpeg_parser parser(opts.in_video.data(), decltype(avg_frame)::channels); if(parser) { - std::unique_ptr progress; if(!parser.process([&]() { - if(!progress) - progress = std::make_unique("Processing " + opts.in_video + ' ', parser.length()); if(avg_frame.size().first == 0) avg_frame = decltype(avg_frame)(parser.size()); avg_frame.process_frame(parser); - progress->inc(); + write(2, ".", 1); return true; })) { std::cerr << "\nParsing " << opts.in_video << " failed: " << *parser.error() << '\n'; return 1; } else - progress->finish(); + write(2, "\n", 1); } else if(parser.error() == "") { std::cerr << "Couldn't open " << opts.in_video << ".\n"; return 1; @@ -82,7 +74,7 @@ int main(int argc, const char ** argv) { } - std::cout << "\nWriting to " << opts.out_image << '\n'; + std::cout << "Writing to " << opts.out_image << '\n'; switch(pictura_mediocritas::output_image(avg_frame.size(), decltype(avg_frame)::channels, avg_frame, opts.out_image.c_str())) { case pictura_mediocritas::output_image_result_t::ok: break; diff --git a/src/parser/ffmpeg.hpp b/src/parser/ffmpeg.hpp index 06bb2c2..3c62b74 100644 --- a/src/parser/ffmpeg.hpp +++ b/src/parser/ffmpeg.hpp @@ -85,7 +85,7 @@ namespace pictura_mediocritas { std::unique_ptr out_frame; int best_stream; - AVCodec * best_codec; + const AVCodec * best_codec; std::size_t channels; error_class_t error_class; diff --git a/src/progressbar/os_progressbar.hpp b/src/progressbar/os_progressbar.hpp deleted file mode 100644 index bcb3e49..0000000 --- a/src/progressbar/os_progressbar.hpp +++ /dev/null @@ -1,72 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#pragma once - - -#include - -#ifdef _WIN32 -#include - -#include - -#include -#else -#endif - - -namespace pictura_mediocritas { - /// An OS-dependent progress bar. - /// - /// Taskbar progress on Windows. - /// No-op on non-Windows. - class os_progressbar { - private: -#ifdef _WIN32 - struct ITaskbarList3_releaser { - void operator()(ITaskbarList3 * list); - }; - - std::unique_ptr taskbar_list; - HWND console_window; - std::size_t curr_progress; - std::size_t max_progress; -#else -#endif - - public: - /// Create a new progressbar with the specified number of steps. - /// - /// @param max The number of times the progressbar must be incremented before it is considered complete, - /// or, in other words, the number of tasks that this progressbar is tracking. - os_progressbar(std::size_t max); - ~os_progressbar(); - - /// Increment progressbar. Don't increment past the initialized # of steps, though. - void inc(); - - /// Set the current status on the progressbar. - void update(std::size_t value); - }; -} diff --git a/src/progressbar/os_progressbar_non_windows.cpp b/src/progressbar/os_progressbar_non_windows.cpp deleted file mode 100644 index 96265d6..0000000 --- a/src/progressbar/os_progressbar_non_windows.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#ifdef _WIN32 -#else - - -#include "os_progressbar.hpp" - - -pictura_mediocritas::os_progressbar::os_progressbar(std::size_t) {} - -pictura_mediocritas::os_progressbar::~os_progressbar() {} - -void pictura_mediocritas::os_progressbar::inc() {} - -void pictura_mediocritas::os_progressbar::update(std::size_t) {} - - -#endif diff --git a/src/progressbar/os_progressbar_windows.cpp b/src/progressbar/os_progressbar_windows.cpp deleted file mode 100644 index f52ce87..0000000 --- a/src/progressbar/os_progressbar_windows.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#ifdef _WIN32 - - -#define INITGUID -#include "os_progressbar.hpp" -#include - - -void pictura_mediocritas::os_progressbar::ITaskbarList3_releaser::operator()(ITaskbarList3 * list) { - list->Release(); -} - -pictura_mediocritas::os_progressbar::os_progressbar(std::size_t max) : console_window(GetConsoleWindow()), curr_progress(0), max_progress(max) { - void * tbar; - CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_ALL, IID_ITaskbarList3, &tbar); - taskbar_list.reset(static_cast(tbar)); - - taskbar_list->SetProgressState(console_window, TBPF_NORMAL); -} - -pictura_mediocritas::os_progressbar::~os_progressbar() { - taskbar_list->SetProgressState(console_window, TBPF_NOPROGRESS); -} - -void pictura_mediocritas::os_progressbar::inc() { - if(curr_progress != std::numeric_limits::max()) - update(curr_progress + 1); -} - -void pictura_mediocritas::os_progressbar::update(std::size_t value) { - if(value <= max_progress) { - curr_progress = value; - taskbar_list->SetProgressValue(console_window, curr_progress, max_progress); - } -} - - -#else -#endif diff --git a/src/progressbar/progressbar.cpp b/src/progressbar/progressbar.cpp deleted file mode 100644 index 8bb0a85..0000000 --- a/src/progressbar/progressbar.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#include "progressbar.hpp" - - -using namespace std::literals; - - -void pictura_mediocritas::progressbar::set_defaults(std::size_t max) { - text_bar.max_refresh_rate(100ms); - if(!max) { - text_bar.show_tick = true; - text_bar.show_bar = false; - } -} - -pictura_mediocritas::progressbar::progressbar(const char * label, std::size_t max) : text_bar(max), os_bar(max) { - text_bar.message(label); - set_defaults(max); -} - -pictura_mediocritas::progressbar::progressbar(const std::string & label, std::size_t max) : text_bar(max), os_bar(max) { - text_bar.message(label); - set_defaults(max); -} - -void pictura_mediocritas::progressbar::inc() { - ++text_bar; - os_bar.inc(); -} - -void pictura_mediocritas::progressbar::update(std::size_t value) { - text_bar = value; - os_bar.update(value); -} - -void pictura_mediocritas::progressbar::finish() { - text_bar.finish(); -} diff --git a/src/progressbar/progressbar.hpp b/src/progressbar/progressbar.hpp deleted file mode 100644 index b47391d..0000000 --- a/src/progressbar/progressbar.hpp +++ /dev/null @@ -1,59 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#pragma once - - -#include -#include "os_progressbar.hpp" -#include - - -namespace pictura_mediocritas { - /// A colluded text and taskbar (on Windows) progress bar. - class progressbar { - private: - pb::progressbar text_bar; - os_progressbar os_bar; - - void set_defaults(std::size_t max); - - public: - /// Create a new progressbar with the specified label and number of steps. - /// - /// @param label The label that will prefix the progressbar. - /// @param max The number of times the progressbar must be incremented before it is considered complete, - /// or, in other words, the number of tasks that this progressbar is tracking. - progressbar(const char * label, std::size_t max); - progressbar(const std::string & label, std::size_t max); - - /// Increment progressbar. Don't increment past the initialized # of steps, though. - void inc(); - - /// Set the current status on the progressbar. - void update(std::size_t value); - - /// Mark the status bar as "finished". - void finish(); - }; -} From c4e24936c04f072b78fe5b51f9de7ace8181f81b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Fri, 22 Mar 2024 12:47:31 +0100 Subject: [PATCH 09/29] C++17 to replace nonstd --- configMakefile | 2 +- src/average_frame.hpp | 1 + src/main.cpp | 4 ++-- src/parser/ffmpeg.cpp | 49 +++++++++++++++++++++++-------------------- src/parser/ffmpeg.hpp | 4 ++-- src/util.cpp | 7 ++----- src/util.hpp | 2 +- 7 files changed, 35 insertions(+), 34 deletions(-) diff --git a/configMakefile b/configMakefile index 2f4ff3f..a963267 100644 --- a/configMakefile +++ b/configMakefile @@ -52,7 +52,7 @@ CATCH2_VERSION := "$(patsubst v%,%,$(shell git -C ext/Catch2 describe --tags --a OBJ := .o ARCH := .a AR := ar -CXXAR := -O3 -fomit-frame-pointer -std=c++14 -pedantic -Wall -Wextra -pipe $(INCCXXAR) $(PIC) +CXXAR := -O3 -fomit-frame-pointer -std=c++17 -pedantic -Wall -Wextra -pipe $(INCCXXAR) $(PIC) CCAR := -O3 -fomit-frame-pointer -std=c11 -pipe $(PIC) OUTDIR := out/ diff --git a/src/average_frame.hpp b/src/average_frame.hpp index 1b5e6be..d860f0f 100644 --- a/src/average_frame.hpp +++ b/src/average_frame.hpp @@ -25,6 +25,7 @@ #include #include +#include #include #include diff --git a/src/main.cpp b/src/main.cpp index 68ecfc4..23226d5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,14 +21,14 @@ #include "average_frame.hpp" -#include "options/options.hpp" +#include "options.hpp" #include "output_image.hpp" #include "parser/ffmpeg.hpp" -#include "parser/multi_image.hpp" #include "util.hpp" #include #include #include +#include extern "C" { #include diff --git a/src/parser/ffmpeg.cpp b/src/parser/ffmpeg.cpp index 39342e3..e7e1ae7 100644 --- a/src/parser/ffmpeg.cpp +++ b/src/parser/ffmpeg.cpp @@ -24,6 +24,9 @@ #include "ffmpeg.hpp" #include +using namespace std::literals; + + void pictura_mediocritas::av_format_context_deleter::operator()(AVFormatContext * ctx) const noexcept { avformat_close_input(&ctx); @@ -190,21 +193,21 @@ pictura_mediocritas::ffmpeg_parser::operator bool() const noexcept { return packet && orig_frame && best_codec_ctx && out_frame && (error_class == error_class_t::none && error_value >= 0); } -nonstd::optional pictura_mediocritas::ffmpeg_parser::error() const { +std::optional pictura_mediocritas::ffmpeg_parser::error() const { switch(error_class) { case error_class_t::none: break; case error_class_t::open_input: - return {""}; + return ""s; case error_class_t::find_best_stream: switch(error_value) { case AVERROR_STREAM_NOT_FOUND: - return {"No video stream in input."}; + return "No video stream in input."s; case AVERROR_DECODER_NOT_FOUND: - return {"No available decoder for input stream."}; + return "No available decoder for input stream."s; default: // Unreachable @@ -212,70 +215,70 @@ nonstd::optional pictura_mediocritas::ffmpeg_parser::error() const } case error_class_t::find_stream_info: - return {"Couldn't find best stream info: " + error_str() + '.'}; + return ("Couldn't find best stream info: "s += error_str()) += '.'; case error_class_t::set_codec_parameters: - return {"Couldn't set codec context parameters: " + error_str() + '.'}; + return ("Couldn't set codec context parameters: "s += error_str()) += '.'; case error_class_t::open_codec: - return {"Couldn't open codec: " + error_str() + '.'}; + return ("Couldn't open codec: "s += error_str()) += '.'; case error_class_t::send_packet: switch(error_value) { case AVERROR(EINVAL): - return {"Couldn't send packet – invalid video."}; + return "Couldn't send packet – invalid video."s; case AVERROR(ENOMEM): - return {"Couldn't send packet – out of memory."}; + return "Couldn't send packet – out of memory."s; case AVERROR_INVALIDDATA: - return {"Couldn't send packet – invalid data."}; + return "Couldn't send packet – invalid data."s; default: - return {"Couldn't send packet: " + error_str() + '.'}; + return ("Couldn't send packet: "s += error_str()) += '.'; } case error_class_t::receive_frame: switch(error_value) { case AVERROR(EINVAL): - return {"Couldn't receive frame – invalid video."}; + return "Couldn't receive frame – invalid video."s; default: - return {"Couldn't receive frame: " + error_str() + '.'}; + return ("Couldn't receive frame: "s += error_str()) += '.'; } case error_class_t::channel_count: switch(channels) { case 0: - return {"Zero-channel video makes no sense."}; + return "Zero-channel video makes no sense."s; default: - return {"Invalid channel count: " + error_str() + '.'}; + return ("Invalid channel count: "s += error_str()) += '.'; } case error_class_t::read_frame: - return {"Couldn't read frame: " + error_str() + '.'}; + return ("Couldn't read frame: "s += error_str()) += '.'; case error_class_t::get_frame_buffer: - return {"Couldn't get output frame buffer: " + error_str() + '.'}; + return ("Couldn't get output frame buffer: "s += error_str()) += '.'; case error_class_t::scale: - return {"Couldn't scale image: " + error_str() + '.'}; + return ("Couldn't scale image: "s += error_str()) += '.'; } if(!packet) - return {"Couldn't allocate packet."}; + return "Couldn't allocate packet."s; if(!orig_frame) - return {"Couldn't allocate frame."}; + return "Couldn't allocate frame."s; if(!best_codec_ctx) - return {"Couldn't allocate codec context."}; + return "Couldn't allocate codec context."s; if(!out_frame) - return {"Couldn't allocate output frame."}; + return "Couldn't allocate output frame."s; - return nonstd::nullopt; + return std::nullopt; } std::pair pictura_mediocritas::ffmpeg_parser::size() const noexcept { diff --git a/src/parser/ffmpeg.hpp b/src/parser/ffmpeg.hpp index 3c62b74..09b1add 100644 --- a/src/parser/ffmpeg.hpp +++ b/src/parser/ffmpeg.hpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include extern "C" { @@ -104,7 +104,7 @@ namespace pictura_mediocritas { explicit operator bool() const noexcept; /// Get the error string, or `nullopt` if conversion to bool is `true`. - nonstd::optional error() const; + std::optional error() const; /// Get current frame's size as `{width, height}`, or `{0, 0}` if noty yet ready. std::pair size() const noexcept; diff --git a/src/util.cpp b/src/util.cpp index 9b8c2f7..de8ae67 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -65,12 +65,9 @@ bool pictura_mediocritas::has_extension(const char * path, const char * ext) { return false; } -std::string pictura_mediocritas::switch_extenstion(const std::string & path, const char * new_ext) { +std::string pictura_mediocritas::switch_extenstion(const std::string_view & path, const char * new_ext) { const auto dot = path.find_last_of('.'); - if(dot == std::string::npos) - return path + '.' + new_ext; - else - return path.substr(0, dot) + '.' + new_ext; + return (std::string{(dot == std::string::npos) ? path : path.substr(0, dot)} += '.') += new_ext; } FREE_IMAGE_FORMAT pictura_mediocritas::deduce_image_format(const char * path) { diff --git a/src/util.hpp b/src/util.hpp index fec7dce..ea1a6bc 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -43,7 +43,7 @@ namespace pictura_mediocritas { bool directory_exists(const char * path); bool has_extension(const char * path, const char * ext); - std::string switch_extenstion(const std::string & path, const char * new_ext); + std::string switch_extenstion(const std::string_view & path, const char * new_ext); FREE_IMAGE_FORMAT deduce_image_format(const char * path); std::vector read_file(const char * path); From 49728e4543b2f4b404cad042168165bed82b36e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Fri, 22 Mar 2024 13:30:03 +0100 Subject: [PATCH 10/29] C++17 quickscope_wrapper --- src/options.cpp | 2 +- src/util.cpp | 6 ------ src/util.hpp | 11 ++++++----- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/options.cpp b/src/options.cpp index f0bc2b4..249f16e 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -26,7 +26,7 @@ using namespace std::literals; -std::tuple pictura_mediocritas::options::parse(int argc, const char * const * argv) { +std::tuple pictura_mediocritas::options::parse(int, const char * const * argv) { auto self = *argv ? *argv : "pictura-mediocritas"; if(*argv) ++argv; diff --git a/src/util.cpp b/src/util.cpp index de8ae67..fb6ce33 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -30,12 +30,6 @@ #include -pictura_mediocritas::quickscope_wrapper::~quickscope_wrapper() { - if(func) - func(); -} - - bool pictura_mediocritas::file_exists(const char * path) { struct stat info; return stat(path, &info) == 0 && info.st_mode & S_IFREG; diff --git a/src/util.hpp b/src/util.hpp index ea1a6bc..b1b6f7c 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -24,19 +24,20 @@ #pragma once +#include #include -#include #include -#include #include namespace pictura_mediocritas { + template struct quickscope_wrapper { - std::function func; - - ~quickscope_wrapper(); + F func; + ~quickscope_wrapper() { func(); } }; + template + quickscope_wrapper(F) -> quickscope_wrapper; bool file_exists(const char * path); From 85a705334be10d08e35dfa77226e8f2160c6091d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Fri, 22 Mar 2024 13:30:17 +0100 Subject: [PATCH 11/29] Port tests to doctest2 --- .gitmodules | 4 -- Makefile | 9 ++- PicturaMediocritas.sublime-project | 4 -- README.md | 2 - configMakefile | 5 +- ext/Catch2 | 1 - tests/average_frame.cpp | 44 ++++++++---- tests/main.cpp | 11 +-- tests/options.cpp | 107 ++++++++++------------------- tests/test_util.hpp | 1 - tests/util/deduce_image_format.cpp | 6 +- tests/util/directory_exists.cpp | 6 +- tests/util/file_exists.cpp | 6 +- tests/util/has_extension.cpp | 22 +++--- tests/util/quickscope_wrapper.cpp | 4 +- tests/util/read_file.cpp | 6 +- tests/util/switch_extenstion.cpp | 6 +- 17 files changed, 101 insertions(+), 143 deletions(-) delete mode 100644 .gitmodules delete mode 160000 ext/Catch2 diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 7642872..0000000 --- a/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ -[submodule "ext/Catch2"] - path = ext/Catch2 - url = https://github.com/catchorg/Catch2 - branch = v2.10.1 diff --git a/Makefile b/Makefile index 99e4d84..6c5bc71 100644 --- a/Makefile +++ b/Makefile @@ -26,8 +26,7 @@ include configMakefile # If ffmpeg autodetects something for your system, you can put it here to link to FFMPEG_PREREQUESITE_LD_LIBS := bz2 lzma z LDAR := $(PIC) $(LNCXXAR) $(foreach l,swscale avformat avcodec avutil freeimage $(FFMPEG_PREREQUESITE_LD_LIBS) $(OS_LD_LIBS),-l$(l)) -VERAR := $(foreach l,PICTURA_MEDIOCRITAS CATCH2,-D$(l)_VERSION='$($(l)_VERSION)') -INCAR := $(foreach l,$(foreach l,Catch2/single_include,ext/$(l)) $(foreach l,ffmpeg,$(BLDDIR)$(l)/include),-isystem$(l)) +VERAR := $(foreach l,PICTURA_MEDIOCRITAS,-D$(l)_VERSION='$($(l)_VERSION)') TEST_SOURCES := $(sort $(wildcard tests/*.cpp tests/**/*.cpp tests/**/**/*.cpp tests/**/**/**/*.cpp)) BUILD_TEST_SOURCES := $(sort $(wildcard build-tests/*.cpp build-tests/**/*.cpp build-tests/**/**/*.cpp build-tests/**/**/**/*.cpp)) SOURCES := $(sort $(wildcard src/*.cpp src/**/*.cpp src/**/**/*.cpp src/**/**/**/*.cpp)) @@ -56,14 +55,14 @@ $(OUTDIR)pictura-mediocritas-tests$(EXE) : $(subst tests/,$(BLDDIR)test_obj/,$(s $(OBJDIR)%$(OBJ) : $(SRCDIR)%.cpp @mkdir -p $(dir $@) - $(CXX) $(CXXAR) $(INCAR) $(VERAR) -c -o$@ $^ + $(CXX) $(CXXAR) $(VERAR) -c -o$@ $^ $(BLDDIR)test_obj/%$(OBJ) : tests/%.cpp @mkdir -p $(dir $@) - $(CXX) $(CXXAR) $(INCAR) -Isrc -c -o$@ $^ + $(CXX) $(CXXAR) -Isrc -c -o$@ $^ $(BLDDIR)build_test_obj/%$(OBJ) : build-tests/%.cpp @mkdir -p $(dir $@) - ! $(CXX) $(CXXAR) $(INCAR) -Isrc -c -o$@ $^ 2>$(subst $(OBJ),.err_out,$@) + ! $(CXX) $(CXXAR) -Isrc -c -o$@ $^ 2>$(subst $(OBJ),.err_out,$@) grep -q "$(shell grep ERROR_MUST_CONTAIN $^ | sed -e 's/#define ERROR_MUST_CONTAIN "//' -e 's/"$$//')" $(subst $(OBJ),.err_out,$@) touch $@ diff --git a/PicturaMediocritas.sublime-project b/PicturaMediocritas.sublime-project index f2e45af..2858752 100644 --- a/PicturaMediocritas.sublime-project +++ b/PicturaMediocritas.sublime-project @@ -26,10 +26,6 @@ "name": "Build tests", "path": "build-tests" }, - { - "name": "External code", - "path": "ext" - }, { "file_include_patterns": [ diff --git a/README.md b/README.md index 1b9fe7a..85e710f 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,6 @@ Either acquire a binary release from [the releases page](https://github.com/Loun or build it yourself: ```sh -# You need FreeImage to be includable and linkable - $ git clone --recursive https://github.com/LoungeCPP/PicturaMediocritas $ cd PicturaMediocritas $ make diff --git a/configMakefile b/configMakefile index a963267..982809b 100644 --- a/configMakefile +++ b/configMakefile @@ -24,12 +24,12 @@ ifeq "$(OS)" "Windows_NT" EXE := .exe PIC := ECHO := /usr/bin/echo - OS_LD_LIBS := ncurses ole32 ws2_32 + OS_LD_LIBS := ole32 ws2_32 else EXE := PIC := -fPIC ECHO := echo - OS_LD_LIBS := ncurses dl pthread + OS_LD_LIBS := dl pthread endif ifneq "$(ADDITIONAL_INCLUDE_DIR)" "" @@ -47,7 +47,6 @@ endif # TODO: uncomment when there's a first tag # PICTURA_MEDIOCRITAS_VERSION := "$(patsubst v%,%,$(shell git describe --tags --abbrev=0))" PICTURA_MEDIOCRITAS_VERSION := "0.1.0" -CATCH2_VERSION := "$(patsubst v%,%,$(shell git -C ext/Catch2 describe --tags --abbrev=0))" OBJ := .o ARCH := .a diff --git a/ext/Catch2 b/ext/Catch2 deleted file mode 160000 index a2c8dce..0000000 --- a/ext/Catch2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a2c8dce85c554aa30607914c57be300feaa3b05f diff --git a/tests/average_frame.cpp b/tests/average_frame.cpp index 79015cf..3da1b04 100644 --- a/tests/average_frame.cpp +++ b/tests/average_frame.cpp @@ -20,8 +20,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -#define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER -#include +#include #include "average_frame.hpp" #include @@ -31,18 +30,35 @@ using namespace std::literals; -namespace Catch { +namespace doctest { template struct StringMaker> { - static std::string convert(const pictura_mediocritas::average_frame & value) { + static String convert(const pictura_mediocritas::average_frame & value) { return "{size=" + StringMaker::convert(value.size()) + ", frames=" + StringMaker::convert(value.processed_frames()) + "}"; } }; + + template + struct StringMaker> { + static String convert(const std::array & value) { + std::stringstream oss; + oss << "["; + bool first = true; + for(auto && v : value) { + if(!first) + oss << ", "; + first = false; + oss << v; + } + oss << "]"; + return oss.str().c_str(); + } + }; } -TEST_CASE("pictura_mediocritas::channels", "[average_frame]") { +TEST_CASE("pictura_mediocritas::channels") { #define CHANNELS(ch) \ REQUIRE((pictura_mediocritas::average_frame::channels) == ch); \ REQUIRE((pictura_mediocritas::average_frame::channels) == ch); \ @@ -65,8 +81,8 @@ TEST_CASE("pictura_mediocritas::channels", "[average_frame]") { #undef CHANNELS } -TEST_CASE("pictura_mediocritas::value_type", "[average_frame]") { -#define TYPES(t) \ +TEST_CASE("pictura_mediocritas::value_type") { +#define TYPES(t) \ REQUIRE((std::is_same::value_type, t>::value)); \ REQUIRE((std::is_same::value_type, t>::value)); \ REQUIRE((std::is_same::value_type, t>::value)); \ @@ -87,7 +103,7 @@ TEST_CASE("pictura_mediocritas::value_type", "[average_frame]") { #undef TYPES } -TEST_CASE("pictura_mediocritas::average_frame(width, height)", "[average_frame]") { +TEST_CASE("pictura_mediocritas::average_frame(width, height)") { std::array zero{}; pictura_mediocritas::average_frame_u64 hd(1280, 720); @@ -110,7 +126,7 @@ TEST_CASE("pictura_mediocritas::average_frame(width, height)", "[average_frame]" REQUIRE(hd.pixel(i) == zero); } -TEST_CASE("pictura_mediocritas::average_frame(size)", "[average_frame]") { +TEST_CASE("pictura_mediocritas::average_frame(size)") { std::array zero{}; pictura_mediocritas::average_frame_u64 hd(std::make_pair(1280, 720)); @@ -132,19 +148,19 @@ TEST_CASE("pictura_mediocritas::average_frame(size)", "[average_frame]") { REQUIRE(hd.pixel(i) == zero); } -TEST_CASE("pictura_mediocritas::average_frame::operator==()", "[average_frame]") { +TEST_CASE("pictura_mediocritas::average_frame::operator==()") { REQUIRE(pictura_mediocritas::average_frame_u64(1280, 720) == pictura_mediocritas::average_frame_u64(std::make_pair(1280, 720))); REQUIRE(pictura_mediocritas::average_frame_u64(1920, 1080) == pictura_mediocritas::average_frame_u64(std::make_pair(1920, 1080))); } -TEST_CASE("pictura_mediocritas::average_frame::operator!=()", "[average_frame]") { +TEST_CASE("pictura_mediocritas::average_frame::operator!=()") { REQUIRE(pictura_mediocritas::average_frame_u64(1280, 720) != pictura_mediocritas::average_frame_u64(1920, 1080)); REQUIRE_FALSE(pictura_mediocritas::average_frame_u64(1280, 720) != pictura_mediocritas::average_frame_u64(std::make_pair(1280, 720))); REQUIRE_FALSE(pictura_mediocritas::average_frame_u64(1920, 1080) != pictura_mediocritas::average_frame_u64(std::make_pair(1920, 1080))); } -TEST_CASE("pictura_mediocritas::average_frame::processed_frames()", "[average_frame]") { +TEST_CASE("pictura_mediocritas::average_frame::processed_frames()") { pictura_mediocritas::average_frame frame(1, 1); for(int i = 0; i < 10; ++i) { REQUIRE(frame.processed_frames() == i); @@ -154,7 +170,7 @@ TEST_CASE("pictura_mediocritas::average_frame::processed_frames()", "[average_fr } } -TEST_CASE("pictura_mediocritas::average_frame::operator[]()", "[average_frame]") { +TEST_CASE("pictura_mediocritas::average_frame::operator[]()") { pictura_mediocritas::average_frame frame(1, 1); REQUIRE(frame[0] == 0); @@ -168,7 +184,7 @@ TEST_CASE("pictura_mediocritas::average_frame::operator[]()", "[average_frame]") } } -TEST_CASE("pictura_mediocritas::average_frame::pixel()", "[average_frame]") { +TEST_CASE("pictura_mediocritas::average_frame::pixel()") { pictura_mediocritas::average_frame frame(1, 1); REQUIRE(frame.pixel(0) == (std::array{0, 0})); diff --git a/tests/main.cpp b/tests/main.cpp index b5f405b..39716d8 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -20,14 +20,12 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -#define CATCH_CONFIG_MAIN -#include +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include #include "test_util.hpp" #include #include -#include -#include const char * temp_dir() { for(auto e : {"TEMP", "TMP"}) @@ -72,8 +70,3 @@ void make_directory_recursive(const char * path) { } make_last_dir(tmp.c_str()); } - -// Will fail spuriously otherwise, becasuse statics :angery: -void reset_TCLAP() { - TCLAP::OptionalUnlabeledTracker::alreadyOptional() = false; -} diff --git a/tests/options.cpp b/tests/options.cpp index a9fe8f0..e13ddf8 100644 --- a/tests/options.cpp +++ b/tests/options.cpp @@ -20,83 +20,50 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -#define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER -#include +#include -#include "options/options.hpp" +#include "options.hpp" #include "test_util.hpp" #include +#include #include #include - using namespace std::literals; -namespace Catch { +namespace doctest { template <> struct StringMaker { - static std::string convert(const pictura_mediocritas::options & value) { - return "{in_video=\'" + value.in_video + "\', out_image=\'" + value.out_image + "\'}"; // + static String convert(const pictura_mediocritas::options & value) { + return (((("{in_video=\'"s += value.in_video) += "\', out_image=\'") += value.out_image) + "\'}").c_str(); } }; } +#define TUPLEQ(l_, r_) \ + { \ + auto l = l_; \ + auto r = r_; \ + REQUIRE(std::get<0>(l) == std::get<0>(r)); \ + REQUIRE(std::get<1>(l) == std::get<1>(r)); \ + REQUIRE(std::get<2>(l) == std::get<2>(r)); \ + } -TEST_CASE("pictura_mediocritas::options::parse() -- not enough args", "[options]") { - reset_TCLAP(); - const char * args[] = {"pictura-mediocritas tests", nullptr}; - REQUIRE(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args) == - std::make_tuple(pictura_mediocritas::options{}, 1, - "pictura-mediocritas tests: error: parsing arguments failed (Required argument missing: in_video) for undefined argument")); -} - -TEST_CASE("pictura_mediocritas::options::parse() -- nonexistant input file", "[options][incorrect]") { - const auto temp = temp_dir() + "/PicturaMediocritas/options/incorrect/nonexistant-input/"s; - make_directory_recursive(temp.c_str()); - - const auto nonexistant_file = temp + "nonexistant_file"; - - reset_TCLAP(); - const char * args[] = {"pictura-mediocritas tests", nonexistant_file.c_str(), nullptr}; - REQUIRE(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args) == - std::make_tuple(pictura_mediocritas::options{}, 1, - "pictura-mediocritas tests: error: parsing arguments failed (Value '" + nonexistant_file + - "' does not meet constraint: existing file) for Argument: (--in_video)")); -} - -TEST_CASE("pictura_mediocritas::options::parse() -- nonexistant output file parent", "[options][incorrect]") { - const auto temp = temp_dir() + "/PicturaMediocritas/options/incorrect/nonexistant-input/"s; - make_directory_recursive(temp.c_str()); - - const auto in_video = temp + "in_video.avi"; - const auto unparented_file = temp + "nonexistant_dir/unparented_file"; - std::ofstream{in_video}; - reset_TCLAP(); - const char * args[] = {"pictura-mediocritas tests", in_video.c_str(), unparented_file.c_str(), nullptr}; - REQUIRE(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args) == - std::make_tuple(pictura_mediocritas::options{}, 1, - "pictura-mediocritas tests: error: parsing arguments failed (Value '" + unparented_file + - "' does not meet constraint: creatable file) for Argument: (--out_image)")); +TEST_CASE("pictura_mediocritas::options::parse() -- not enough args") { + const char * args[] = {"pictura-mediocritas-tests", nullptr}; + TUPLEQ(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args), + std::make_tuple(pictura_mediocritas::options{}, 1, "usage: pictura-mediocritas-tests in-video [out-image]")); } -TEST_CASE("pictura_mediocritas::options::parse() -- output file a dir", "[options][incorrect]") { - const auto temp = temp_dir() + "/PicturaMediocritas/options/incorrect/nonexistant-input/"s; - make_directory_recursive(temp.c_str()); - - const auto in_video = temp + "in_video.avi"; - std::ofstream{in_video}; - - reset_TCLAP(); - const char * args[] = {"pictura-mediocritas tests", in_video.c_str(), temp.c_str(), nullptr}; - REQUIRE(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args) == - std::make_tuple(pictura_mediocritas::options{}, 1, - "pictura-mediocritas tests: error: parsing arguments failed (Value '" + temp + - "' does not meet constraint: creatable file) for Argument: (--out_image)")); +TEST_CASE("pictura_mediocritas::options::parse() -- too many args") { + const char * args[] = {"pictura-mediocritas-tests", "in", "out", "extra", nullptr}; + TUPLEQ(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args), + std::make_tuple(pictura_mediocritas::options{"in", "out"}, 1, "usage: pictura-mediocritas-tests in-video [out-image]")); } -TEST_CASE("pictura_mediocritas::options::parse() -- correct", "[options]") { +TEST_CASE("pictura_mediocritas::options::parse() -- correct") { const auto temp = temp_dir() + "/PicturaMediocritas/options/correct/"s; make_directory_recursive(temp.c_str()); @@ -107,30 +74,26 @@ TEST_CASE("pictura_mediocritas::options::parse() -- correct", "[options]") { std::ofstream{in_video_ext}; { - reset_TCLAP(); - const char * args[] = {"pictura-mediocritas tests", in_video.c_str(), nullptr}; - REQUIRE(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args) == - std::make_tuple(pictura_mediocritas::options{in_video, in_video + ".png"}, 0, "")); + const char * args[] = {"pictura-mediocritas-tests", in_video.c_str(), nullptr}; + TUPLEQ(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args), + std::make_tuple(pictura_mediocritas::options{in_video, in_video + ".png"}, 0, "")); } { - reset_TCLAP(); - const char * args[] = {"pictura-mediocritas tests", in_video_ext.c_str(), nullptr}; - REQUIRE(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args) == - std::make_tuple(pictura_mediocritas::options{in_video_ext, in_video + ".png"}, 0, "")); + const char * args[] = {"pictura-mediocritas-tests", in_video_ext.c_str(), nullptr}; + TUPLEQ(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args), + std::make_tuple(pictura_mediocritas::options{in_video_ext, in_video + ".png"}, 0, "")); } { - reset_TCLAP(); - const char * args[] = {"pictura-mediocritas tests", in_video.c_str(), out_image.c_str(), nullptr}; - REQUIRE(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args) == - std::make_tuple(pictura_mediocritas::options{in_video, out_image}, 0, "")); + const char * args[] = {"pictura-mediocritas-tests", in_video.c_str(), out_image.c_str(), nullptr}; + TUPLEQ(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args), + std::make_tuple(pictura_mediocritas::options{in_video, out_image}, 0, "")); } { - reset_TCLAP(); - const char * args[] = {"pictura-mediocritas tests", in_video_ext.c_str(), out_image.c_str(), nullptr}; - REQUIRE(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args) == - std::make_tuple(pictura_mediocritas::options{in_video_ext, out_image}, 0, "")); + const char * args[] = {"pictura-mediocritas-tests", in_video_ext.c_str(), out_image.c_str(), nullptr}; + TUPLEQ(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args), + std::make_tuple(pictura_mediocritas::options{in_video_ext, out_image}, 0, "")); } } diff --git a/tests/test_util.hpp b/tests/test_util.hpp index c468a2b..2d51e94 100644 --- a/tests/test_util.hpp +++ b/tests/test_util.hpp @@ -22,4 +22,3 @@ void make_directory_recursive(const char * path); const char * temp_dir(); -void reset_TCLAP(); diff --git a/tests/util/deduce_image_format.cpp b/tests/util/deduce_image_format.cpp index 2f72eac..8e3c3ca 100644 --- a/tests/util/deduce_image_format.cpp +++ b/tests/util/deduce_image_format.cpp @@ -22,7 +22,7 @@ #include "../test_util.hpp" #include "util.hpp" -#include +#include #include #include @@ -30,7 +30,7 @@ using namespace std::literals; -TEST_CASE("util::deduce_image_format() -- nonexistant", "[util]") { +TEST_CASE("util::deduce_image_format() -- nonexistant") { REQUIRE(pictura_mediocritas::deduce_image_format("image.bMP") == FIF_BMP); REQUIRE(pictura_mediocritas::deduce_image_format("image.Ico") == FIF_ICO); @@ -56,7 +56,7 @@ TEST_CASE("util::deduce_image_format() -- nonexistant", "[util]") { REQUIRE(pictura_mediocritas::deduce_image_format("image.WeBp") == FIF_WEBP); } -TEST_CASE("util::deduce_image_format() -- unrecognised", "[util]") { +TEST_CASE("util::deduce_image_format() -- unrecognised") { REQUIRE(pictura_mediocritas::deduce_image_format("README.md") == FIF_UNKNOWN); REQUIRE(pictura_mediocritas::deduce_image_format("Makefile") == FIF_UNKNOWN); } diff --git a/tests/util/directory_exists.cpp b/tests/util/directory_exists.cpp index bb02300..131e11f 100644 --- a/tests/util/directory_exists.cpp +++ b/tests/util/directory_exists.cpp @@ -22,7 +22,7 @@ #include "../test_util.hpp" #include "util.hpp" -#include +#include #include #include @@ -30,14 +30,14 @@ using namespace std::literals; -TEST_CASE("util::directory_exists() -- nonexistant", "[util]") { +TEST_CASE("util::directory_exists() -- nonexistant") { const auto temp = temp_dir() + "/PicturaMediocritas/util/directory_exists/"s; make_directory_recursive(temp.c_str()); REQUIRE_FALSE(pictura_mediocritas::directory_exists((temp + "nonexistant_dir").c_str())); } -TEST_CASE("util::directory_exists() -- existant file", "[util]") { +TEST_CASE("util::directory_exists() -- existant file") { const auto temp = temp_dir() + "/PicturaMediocritas/util/directory_exists/"s; make_directory_recursive(temp.c_str()); diff --git a/tests/util/file_exists.cpp b/tests/util/file_exists.cpp index 41c9a7a..af3b0b5 100644 --- a/tests/util/file_exists.cpp +++ b/tests/util/file_exists.cpp @@ -22,7 +22,7 @@ #include "../test_util.hpp" #include "util.hpp" -#include +#include #include #include @@ -30,14 +30,14 @@ using namespace std::literals; -TEST_CASE("util::file_exists() -- nonexistant", "[util]") { +TEST_CASE("util::file_exists() -- nonexistant") { const auto temp = temp_dir() + "/PicturaMediocritas/util/file_exists/"s; make_directory_recursive(temp.c_str()); REQUIRE_FALSE(pictura_mediocritas::file_exists((temp + "nonexistant_file").c_str())); } -TEST_CASE("util::file_exists() -- existant file", "[util]") { +TEST_CASE("util::file_exists() -- existant file") { const auto temp = temp_dir() + "/PicturaMediocritas/util/file_exists/"s; make_directory_recursive(temp.c_str()); diff --git a/tests/util/has_extension.cpp b/tests/util/has_extension.cpp index 475cde1..8f2a621 100644 --- a/tests/util/has_extension.cpp +++ b/tests/util/has_extension.cpp @@ -21,20 +21,20 @@ #include "util.hpp" -#include +#include -TEST_CASE("util::has_extension() -- equadistant", "[util]") { - REQUIRE(pictura_mediocritas::has_extension("gif_load/test.GIf", "giF")); - REQUIRE(pictura_mediocritas::has_extension("test.pnG", "PnG")); - REQUIRE(pictura_mediocritas::has_extension("README.MD", "md")); +TEST_CASE("util::has_extension() -- equadistant") { + REQUIRE(pictura_mediocritas::has_extension("gif_load/test.GIf", "GIF")); + REQUIRE(pictura_mediocritas::has_extension("test.pnG", "PNG")); + REQUIRE(pictura_mediocritas::has_extension("README.MD", "MD")); - REQUIRE_FALSE(pictura_mediocritas::has_extension("gif_load/test_GIf", "giF")); - REQUIRE_FALSE(pictura_mediocritas::has_extension("test", "PnG")); + REQUIRE_FALSE(pictura_mediocritas::has_extension("gif_load/test_GIf", "gif")); + REQUIRE_FALSE(pictura_mediocritas::has_extension("test", "gif")); } -TEST_CASE("util::has_extension() -- interdistant", "[util]") { - REQUIRE_FALSE(pictura_mediocritas::has_extension("gif_load/test.Gf", "giF")); - REQUIRE_FALSE(pictura_mediocritas::has_extension("test.", "PnG")); - REQUIRE_FALSE(pictura_mediocritas::has_extension("README.MD", "m")); +TEST_CASE("util::has_extension() -- interdistant") { + REQUIRE_FALSE(pictura_mediocritas::has_extension("gif_load/test.Gf", "GIF")); + REQUIRE_FALSE(pictura_mediocritas::has_extension("test.", "PNG")); + REQUIRE_FALSE(pictura_mediocritas::has_extension("README.MDD", "MD")); } diff --git a/tests/util/quickscope_wrapper.cpp b/tests/util/quickscope_wrapper.cpp index ad562d4..591ac61 100644 --- a/tests/util/quickscope_wrapper.cpp +++ b/tests/util/quickscope_wrapper.cpp @@ -21,10 +21,10 @@ #include "util.hpp" -#include +#include -TEST_CASE("util::quickscope_wrapper()", "[util]") { +TEST_CASE("util::quickscope_wrapper()") { bool updated = false; { pictura_mediocritas::quickscope_wrapper updated_updater{[&]() { updated = true; }}; diff --git a/tests/util/read_file.cpp b/tests/util/read_file.cpp index 4977bde..29d8751 100644 --- a/tests/util/read_file.cpp +++ b/tests/util/read_file.cpp @@ -22,7 +22,7 @@ #include "../test_util.hpp" #include "util.hpp" -#include +#include #include #include @@ -30,14 +30,14 @@ using namespace std::literals; -TEST_CASE("util::read_file() -- nonexistant", "[util]") { +TEST_CASE("util::read_file() -- nonexistant") { const auto temp = temp_dir() + "/PicturaMediocritas/util/read_file/"s; make_directory_recursive(temp.c_str()); REQUIRE(pictura_mediocritas::read_file((temp + "nonexistant_file").c_str()) == std::vector{}); } -TEST_CASE("util::read_file() -- existant file", "[util]") { +TEST_CASE("util::read_file() -- existant file") { const auto temp = temp_dir() + "/PicturaMediocritas/util/read_file/"s; make_directory_recursive(temp.c_str()); diff --git a/tests/util/switch_extenstion.cpp b/tests/util/switch_extenstion.cpp index e419a40..4cddfaa 100644 --- a/tests/util/switch_extenstion.cpp +++ b/tests/util/switch_extenstion.cpp @@ -21,16 +21,16 @@ #include "util.hpp" -#include +#include -TEST_CASE("util::switch_extenstion() -- with extension", "[util]") { +TEST_CASE("util::switch_extenstion() -- with extension") { REQUIRE(pictura_mediocritas::switch_extenstion("gif_load/test.GIf", "png") == "gif_load/test.png"); REQUIRE(pictura_mediocritas::switch_extenstion("test.png", "png") == "test.png"); REQUIRE(pictura_mediocritas::switch_extenstion("README.md", "png") == "README.png"); } -TEST_CASE("util::switch_extenstion() -- no extension", "[util]") { +TEST_CASE("util::switch_extenstion() -- no extension") { REQUIRE(pictura_mediocritas::switch_extenstion("gif_load/test", "png") == "gif_load/test.png"); REQUIRE(pictura_mediocritas::switch_extenstion("test", "png") == "test.png"); REQUIRE(pictura_mediocritas::switch_extenstion("README", "png") == "README.png"); From c22d93287d81711f5ffbedb0a936a8410ac91438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Fri, 22 Mar 2024 13:46:10 +0100 Subject: [PATCH 12/29] {file,directory}_exists() not used --- src/util.cpp | 10 ------- src/util.hpp | 3 --- tests/util/directory_exists.cpp | 45 -------------------------------- tests/util/file_exists.cpp | 46 --------------------------------- 4 files changed, 104 deletions(-) delete mode 100644 tests/util/directory_exists.cpp delete mode 100644 tests/util/file_exists.cpp diff --git a/src/util.cpp b/src/util.cpp index fb6ce33..08ab1d6 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -30,16 +30,6 @@ #include -bool pictura_mediocritas::file_exists(const char * path) { - struct stat info; - return stat(path, &info) == 0 && info.st_mode & S_IFREG; -} - -bool pictura_mediocritas::directory_exists(const char * path) { - struct stat info; - return stat(path, &info) == 0 && info.st_mode & S_IFDIR; -} - bool pictura_mediocritas::has_extension(const char * path, const char * ext) { const auto path_len = std::strlen(path); auto path_ext = path + path_len; diff --git a/src/util.hpp b/src/util.hpp index b1b6f7c..96cea67 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -40,9 +40,6 @@ namespace pictura_mediocritas { quickscope_wrapper(F) -> quickscope_wrapper; - bool file_exists(const char * path); - bool directory_exists(const char * path); - bool has_extension(const char * path, const char * ext); std::string switch_extenstion(const std::string_view & path, const char * new_ext); FREE_IMAGE_FORMAT deduce_image_format(const char * path); diff --git a/tests/util/directory_exists.cpp b/tests/util/directory_exists.cpp deleted file mode 100644 index 131e11f..0000000 --- a/tests/util/directory_exists.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -#include "../test_util.hpp" -#include "util.hpp" -#include -#include -#include - - -using namespace std::literals; - - -TEST_CASE("util::directory_exists() -- nonexistant") { - const auto temp = temp_dir() + "/PicturaMediocritas/util/directory_exists/"s; - make_directory_recursive(temp.c_str()); - - REQUIRE_FALSE(pictura_mediocritas::directory_exists((temp + "nonexistant_dir").c_str())); -} - -TEST_CASE("util::directory_exists() -- existant file") { - const auto temp = temp_dir() + "/PicturaMediocritas/util/directory_exists/"s; - make_directory_recursive(temp.c_str()); - - REQUIRE(pictura_mediocritas::directory_exists(temp.c_str())); -} diff --git a/tests/util/file_exists.cpp b/tests/util/file_exists.cpp deleted file mode 100644 index af3b0b5..0000000 --- a/tests/util/file_exists.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -#include "../test_util.hpp" -#include "util.hpp" -#include -#include -#include - - -using namespace std::literals; - - -TEST_CASE("util::file_exists() -- nonexistant") { - const auto temp = temp_dir() + "/PicturaMediocritas/util/file_exists/"s; - make_directory_recursive(temp.c_str()); - - REQUIRE_FALSE(pictura_mediocritas::file_exists((temp + "nonexistant_file").c_str())); -} - -TEST_CASE("util::file_exists() -- existant file") { - const auto temp = temp_dir() + "/PicturaMediocritas/util/file_exists/"s; - make_directory_recursive(temp.c_str()); - - std::ofstream(temp + "file"); - REQUIRE(pictura_mediocritas::file_exists((temp + "file").c_str())); -} From 7d2c27d3086b94bad07ceb0b52b9b292a555a08f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Fri, 22 Mar 2024 13:48:26 +0100 Subject: [PATCH 13/29] read_file() not used --- src/util.cpp | 15 --------- src/util.hpp | 3 -- tests/util/read_file.cpp | 71 ---------------------------------------- 3 files changed, 89 deletions(-) delete mode 100644 tests/util/read_file.cpp diff --git a/src/util.cpp b/src/util.cpp index 08ab1d6..dd82be8 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -80,17 +79,3 @@ FREE_IMAGE_FORMAT pictura_mediocritas::deduce_image_format(const char * path) { else return FIF_UNKNOWN; } - -std::vector pictura_mediocritas::read_file(const char * path) { - std::vector ret; - - std::ifstream in_file(path, std::ios::in | std::ios::binary); - std::uint8_t buf[1024]; - in_file.read(static_cast(static_cast(buf)), sizeof buf / sizeof *buf); - while(in_file.gcount()) { - ret.insert(ret.end(), buf, buf + in_file.gcount()); - in_file.read(static_cast(static_cast(buf)), sizeof buf / sizeof *buf); - } - - return ret; -} diff --git a/src/util.hpp b/src/util.hpp index 96cea67..58f5c50 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -27,7 +27,6 @@ #include #include #include -#include namespace pictura_mediocritas { @@ -43,6 +42,4 @@ namespace pictura_mediocritas { bool has_extension(const char * path, const char * ext); std::string switch_extenstion(const std::string_view & path, const char * new_ext); FREE_IMAGE_FORMAT deduce_image_format(const char * path); - - std::vector read_file(const char * path); } diff --git a/tests/util/read_file.cpp b/tests/util/read_file.cpp deleted file mode 100644 index 29d8751..0000000 --- a/tests/util/read_file.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -#include "../test_util.hpp" -#include "util.hpp" -#include -#include -#include - - -using namespace std::literals; - - -TEST_CASE("util::read_file() -- nonexistant") { - const auto temp = temp_dir() + "/PicturaMediocritas/util/read_file/"s; - make_directory_recursive(temp.c_str()); - - REQUIRE(pictura_mediocritas::read_file((temp + "nonexistant_file").c_str()) == std::vector{}); -} - -TEST_CASE("util::read_file() -- existant file") { - const auto temp = temp_dir() + "/PicturaMediocritas/util/read_file/"s; - make_directory_recursive(temp.c_str()); - - std::ofstream(temp + "file"); - REQUIRE(pictura_mediocritas::read_file((temp + "file").c_str()) == std::vector{}); - - const auto data = R"~~( -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -)~~"s; - std::ofstream(temp + "non_empty_file", std::ios::out | std::ios::binary) << data; - REQUIRE(pictura_mediocritas::read_file((temp + "non_empty_file").c_str()) == (std::vector{data.begin(), data.end()})); -} From 9f19c88b339579303e1055edbd5848b38135daae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Fri, 22 Mar 2024 15:29:28 +0100 Subject: [PATCH 14/29] const_cast to const instead of two identical implementations --- src/average_frame.inc | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/average_frame.inc b/src/average_frame.inc index a1fb540..fc71468 100644 --- a/src/average_frame.inc +++ b/src/average_frame.inc @@ -48,29 +48,22 @@ AccT pictura_mediocritas::average_frame::processed_frames() cons return frames; } -#define PROCESS_FRAME_BODY() \ - do { \ - const auto pixels_len = pixels.size(); \ - for(std::size_t i = 0; i < pixels_len; ++i) \ - pixels[i] += frame[i]; \ - \ - ++frames; \ - } while(0) - template template -void pictura_mediocritas::average_frame::process_frame(const FrameT & frame) { - PROCESS_FRAME_BODY(); +void pictura_mediocritas::average_frame::process_frame(const FrameT & frame, std::size_t frame_ctr) { + const auto pixels_len = pixels.size(); + for(std::size_t i = 0; i < pixels_len; ++i) + pixels[i] += frame[{i, frame_ctr}]; + + ++frames; } template template -void pictura_mediocritas::average_frame::process_frame(FrameT & frame) { - PROCESS_FRAME_BODY(); +void pictura_mediocritas::average_frame::process_frame(FrameT & frame, std::size_t frame_ctr) { + process_frame(const_cast(frame), frame_ctr); } -#undef PROCESS_FRAME_BODY - template AccT pictura_mediocritas::average_frame::operator[](std::size_t idx) const { if(frames == 0) From 44bd384aaebed22d2e3e72bdd7e0d3a77d5b2c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Fri, 22 Mar 2024 15:49:30 +0100 Subject: [PATCH 15/29] -MD deps --- Makefile | 9 ++++++--- configMakefile | 3 +-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 6c5bc71..cf3f820 100644 --- a/Makefile +++ b/Makefile @@ -55,14 +55,17 @@ $(OUTDIR)pictura-mediocritas-tests$(EXE) : $(subst tests/,$(BLDDIR)test_obj/,$(s $(OBJDIR)%$(OBJ) : $(SRCDIR)%.cpp @mkdir -p $(dir $@) - $(CXX) $(CXXAR) $(VERAR) -c -o$@ $^ + $(CXX) $(CXXAR) $(VERAR) -c -o$@ $< $(BLDDIR)test_obj/%$(OBJ) : tests/%.cpp @mkdir -p $(dir $@) - $(CXX) $(CXXAR) -Isrc -c -o$@ $^ + $(CXX) $(CXXAR) -Isrc -c -o$@ $< $(BLDDIR)build_test_obj/%$(OBJ) : build-tests/%.cpp @mkdir -p $(dir $@) - ! $(CXX) $(CXXAR) -Isrc -c -o$@ $^ 2>$(subst $(OBJ),.err_out,$@) + ! $(CXX) $(CXXAR) -Isrc -c -o$@ $< 2>$(subst $(OBJ),.err_out,$@) grep -q "$(shell grep ERROR_MUST_CONTAIN $^ | sed -e 's/#define ERROR_MUST_CONTAIN "//' -e 's/"$$//')" $(subst $(OBJ),.err_out,$@) touch $@ + + +include $(wildcard $(OBJDIR)*/*.d $(OBJDIR)*.d) diff --git a/configMakefile b/configMakefile index 982809b..6e2586a 100644 --- a/configMakefile +++ b/configMakefile @@ -51,8 +51,7 @@ PICTURA_MEDIOCRITAS_VERSION := "0.1.0" OBJ := .o ARCH := .a AR := ar -CXXAR := -O3 -fomit-frame-pointer -std=c++17 -pedantic -Wall -Wextra -pipe $(INCCXXAR) $(PIC) -CCAR := -O3 -fomit-frame-pointer -std=c11 -pipe $(PIC) +CXXAR := -MD -O3 -fomit-frame-pointer -march=native -std=c++20 -pedantic -Wall -Wextra -Wno-missing-field-initializers -pipe $(INCCXXAR) $(PIC) OUTDIR := out/ BLDDIR := out/build/ From 5818899b452080bdd08463784cb7bdd3c2ca0e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Fri, 22 Mar 2024 15:55:38 +0100 Subject: [PATCH 16/29] Add to frames on threads [22FPS -> 45FPS] --- src/average_frame.hpp | 11 +++++--- src/average_frame.inc | 8 ++++++ src/main.cpp | 56 +++++++++++++++++++++++++++++++++++++---- src/parser/ffmpeg.cpp | 35 ++++++++++++++++---------- src/parser/ffmpeg.hpp | 12 ++++++--- tests/average_frame.cpp | 26 ++++++++++++++----- 6 files changed, 117 insertions(+), 31 deletions(-) diff --git a/src/average_frame.hpp b/src/average_frame.hpp index d860f0f..f4d71d7 100644 --- a/src/average_frame.hpp +++ b/src/average_frame.hpp @@ -80,15 +80,18 @@ namespace pictura_mediocritas { /// Get a frame from the specified source. /// - /// The `frame` argument must be indexable by a `std::size_t` in [0; width * height * Channels) ∩ ℤ. + /// The `frame` argument must be indexable by a `{std::size_t, frame_ctr}` where the size_t is in [0; width * height * Channels) ∩ ℤ. template - void process_frame(const FrameT & frame); + void process_frame(const FrameT & frame, std::size_t frame_ctr); /// Get a frame from the specified source. /// - /// The `frame` argument must be indexable by a `std::size_t` in [0; width * height * Channels) ∩ ℤ. + /// The `frame` argument must be indexable by a `{std::size_t, frame_ctr}` where the size_t is in [0; width * height * Channels) ∩ ℤ. template - void process_frame(FrameT & frame); + void process_frame(FrameT & frame, std::size_t frame_ctr); + + /// Add the values from a partial frame to this one. + average_frame & operator+=(const average_frame & partial); /// Get the average channel at the given index. /// diff --git a/src/average_frame.inc b/src/average_frame.inc index fc71468..d9fc6db 100644 --- a/src/average_frame.inc +++ b/src/average_frame.inc @@ -58,12 +58,20 @@ void pictura_mediocritas::average_frame::process_frame(const Fra ++frames; } + template template void pictura_mediocritas::average_frame::process_frame(FrameT & frame, std::size_t frame_ctr) { process_frame(const_cast(frame), frame_ctr); } +template +pictura_mediocritas::average_frame & pictura_mediocritas::average_frame::operator+=(const average_frame & partial) { + std::transform(std::begin(pixels), std::end(pixels), std::begin(partial.pixels), std::begin(pixels), std::plus()); + frames += partial.frames; + return *this; +} + template AccT pictura_mediocritas::average_frame::operator[](std::size_t idx) const { if(frames == 0) diff --git a/src/main.cpp b/src/main.cpp index 23226d5..5e9fe4e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,8 +26,11 @@ #include "parser/ffmpeg.hpp" #include "util.hpp" #include +#include #include #include +#include +#include #include extern "C" { @@ -48,23 +51,66 @@ int main(int argc, const char ** argv) { FreeImage_Initialise(); pictura_mediocritas::quickscope_wrapper freeimage_deinitialiser{FreeImage_DeInitialise}; +#define MAXTHREADS 8u pictura_mediocritas::average_frame_u64 avg_frame(0, 0); + auto thread_cnt = std::clamp(std::thread::hardware_concurrency(), 1u, MAXTHREADS); - pictura_mediocritas::ffmpeg_parser parser(opts.in_video.data(), decltype(avg_frame)::channels); + pictura_mediocritas::ffmpeg_parser parser(opts.in_video.data(), decltype(avg_frame)::channels, thread_cnt); if(parser) { + struct thread { + pictura_mediocritas::average_frame_u64 avg_frame; + std::thread thread; + pthread_barrier_t barrier; + std::atomic cur_frame_num; + std::atomic_flag done; + }; + thread threads[MAXTHREADS] = {{{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}},{{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}}; if(!parser.process([&]() { - if(avg_frame.size().first == 0) + if(avg_frame.size().first == 0) { avg_frame = decltype(avg_frame)(parser.size()); - avg_frame.process_frame(parser); - write(2, ".", 1); + for(auto i = 0u; i < thread_cnt; ++i) { + threads[i].avg_frame = decltype(avg_frame)(parser.size()); + while(pthread_barrier_init(&threads[i].barrier, nullptr, 2)) + ; + threads[i].thread = std::thread{[&, i = i] { + char id = '0' + i; + auto & self = threads[i]; + + pthread_barrier_wait(&self.barrier); + for(;;) { + pthread_barrier_wait(&self.barrier); + if(self.done.test()) + break; + auto frame = self.cur_frame_num.load(std::memory_order_relaxed); + self.avg_frame.process_frame(parser, frame); + write(2, &id, 1); + pthread_barrier_wait(&self.barrier); + } + }}; + } + } + + auto & thread = threads[parser.frame_num % thread_cnt]; + pthread_barrier_wait(&thread.barrier); + thread.cur_frame_num.store(parser.frame_num, std::memory_order_relaxed); + pthread_barrier_wait(&thread.barrier); return true; })) { std::cerr << "\nParsing " << opts.in_video << " failed: " << *parser.error() << '\n'; return 1; - } else + } else { write(2, "\n", 1); + + for(auto i = 0u; i < thread_cnt; ++i) { + threads[i].done.test_and_set(); + pthread_barrier_wait(&threads[i].barrier); + pthread_barrier_wait(&threads[i].barrier); + threads[i].thread.join(); + avg_frame += threads[i].avg_frame; + } + } } else if(parser.error() == "") { std::cerr << "Couldn't open " << opts.in_video << ".\n"; return 1; diff --git a/src/parser/ffmpeg.cpp b/src/parser/ffmpeg.cpp index e7e1ae7..07b8875 100644 --- a/src/parser/ffmpeg.cpp +++ b/src/parser/ffmpeg.cpp @@ -27,7 +27,6 @@ using namespace std::literals; - void pictura_mediocritas::av_format_context_deleter::operator()(AVFormatContext * ctx) const noexcept { avformat_close_input(&ctx); } @@ -65,6 +64,8 @@ bool pictura_mediocritas::ffmpeg_parser::send_packet(AVPacket * pkt) noexcept { bool pictura_mediocritas::ffmpeg_parser::receive_frame(const std::function & callback) noexcept { while((error_value = avcodec_receive_frame(best_codec_ctx.get(), orig_frame.get())) >= 0) { + ++frame_num; + auto & out_frame = out_frames[frame_num % out_frames.size()]; if(out_frame->width == 0) { out_frame->width = orig_frame->width; out_frame->height = orig_frame->height; @@ -124,8 +125,8 @@ std::string pictura_mediocritas::ffmpeg_parser::error_str() const { return buf; } -pictura_mediocritas::ffmpeg_parser::ffmpeg_parser(const char * filename, std::size_t c) - : best_stream(-69), best_codec(nullptr), channels(c), error_class(error_class_t::none), error_value(0) { +pictura_mediocritas::ffmpeg_parser::ffmpeg_parser(const char * filename, std::size_t c, std::size_t runners) + : best_stream(-69), best_codec(nullptr), channels(c), error_class(error_class_t::none), error_value(0), frame_num(-1) { AVFormatContext * container_in = nullptr; if((error_value = avformat_open_input(&container_in, filename, nullptr, nullptr)) != 0) { error_class = error_class_t::open_input; @@ -165,21 +166,26 @@ pictura_mediocritas::ffmpeg_parser::ffmpeg_parser(const char * filename, std::si return; } - out_frame.reset(av_frame_alloc()); - if(!out_frame) - return; + for(std::size_t i = 0; i < runners; ++i) { + out_frames.emplace_back(av_frame_alloc()); + if(!out_frames.back()) + return; + } switch(channels) { case 1: - out_frame->format = AV_PIX_FMT_GRAY8; + for(auto && out_frame : out_frames) + out_frame->format = AV_PIX_FMT_GRAY8; break; case 3: - out_frame->format = AV_PIX_FMT_RGB24; + for(auto && out_frame : out_frames) + out_frame->format = AV_PIX_FMT_RGB24; break; case 4: - out_frame->format = AV_PIX_FMT_RGBA; + for(auto && out_frame : out_frames) + out_frame->format = AV_PIX_FMT_RGBA; break; default: @@ -190,7 +196,9 @@ pictura_mediocritas::ffmpeg_parser::ffmpeg_parser(const char * filename, std::si } pictura_mediocritas::ffmpeg_parser::operator bool() const noexcept { - return packet && orig_frame && best_codec_ctx && out_frame && (error_class == error_class_t::none && error_value >= 0); + return packet && orig_frame && best_codec_ctx && + std::all_of(std::begin(out_frames), std::end(out_frames), [&](auto && out_frame) { return bool(out_frame); }) && + (error_class == error_class_t::none && error_value >= 0); } std::optional pictura_mediocritas::ffmpeg_parser::error() const { @@ -275,7 +283,7 @@ std::optional pictura_mediocritas::ffmpeg_parser::error() const { if(!best_codec_ctx) return "Couldn't allocate codec context."s; - if(!out_frame) + if(!out_frames.back()) return "Couldn't allocate output frame."s; return std::nullopt; @@ -327,9 +335,10 @@ bool pictura_mediocritas::ffmpeg_parser::process(const std::function & c return true; } -std::uint8_t pictura_mediocritas::ffmpeg_parser::operator[](std::size_t idx) const noexcept { +std::uint8_t pictura_mediocritas::ffmpeg_parser::operator[](const deref & idx) const noexcept { + auto & out_frame = out_frames[idx.frame_num % out_frames.size()]; if(out_frame && out_frame->data[0]) - return out_frame->data[0][idx]; + return out_frame->data[0][idx.idx]; else return -1; } diff --git a/src/parser/ffmpeg.hpp b/src/parser/ffmpeg.hpp index 09b1add..1f3ec1e 100644 --- a/src/parser/ffmpeg.hpp +++ b/src/parser/ffmpeg.hpp @@ -82,7 +82,7 @@ namespace pictura_mediocritas { std::unique_ptr orig_frame; std::unique_ptr best_codec_ctx; std::unique_ptr colour_conv_ctx; - std::unique_ptr out_frame; + std::vector> out_frames; int best_stream; const AVCodec * best_codec; @@ -97,7 +97,9 @@ namespace pictura_mediocritas { public: - ffmpeg_parser(const char * filename, std::size_t channels); + std::size_t frame_num; + + ffmpeg_parser(const char * filename, std::size_t channels, std::size_t runners); /// Check if this parser is in a valid state. @@ -121,6 +123,10 @@ namespace pictura_mediocritas { /// /// `idx` is in the format `(y * width + x) * decltype(frame)::channels + channel` /// (i.e. the one required by `average_frame`). - std::uint8_t operator[](std::size_t idx) const noexcept; + struct deref { + std::size_t idx; + std::size_t frame_num; + }; + std::uint8_t operator[](const deref & idx) const noexcept; }; } diff --git a/tests/average_frame.cpp b/tests/average_frame.cpp index 3da1b04..5209cd3 100644 --- a/tests/average_frame.cpp +++ b/tests/average_frame.cpp @@ -160,12 +160,26 @@ TEST_CASE("pictura_mediocritas::average_frame::operator!=()") { REQUIRE_FALSE(pictura_mediocritas::average_frame_u64(1920, 1080) != pictura_mediocritas::average_frame_u64(std::make_pair(1920, 1080))); } +namespace { + template + struct faux { + std::uint8_t data[N]; + + struct idx { + std::size_t i, _; + }; + const std::uint8_t & operator[](const struct idx & idx) const { + return data[idx.i]; + } + }; +} + TEST_CASE("pictura_mediocritas::average_frame::processed_frames()") { pictura_mediocritas::average_frame frame(1, 1); for(int i = 0; i < 10; ++i) { REQUIRE(frame.processed_frames() == i); - const std::uint8_t f[] = {static_cast('0' + i)}; - frame.process_frame(f); + faux<1> f{static_cast('0' + i)}; + frame.process_frame(f, 0); REQUIRE(frame.processed_frames() == i + 1); } } @@ -178,8 +192,8 @@ TEST_CASE("pictura_mediocritas::average_frame::operator[]()") { for(auto i = 0u; i < 256; ++i) { acc += i; - const std::uint8_t f[] = {static_cast(i)}; - frame.process_frame(f); + faux<1> f{static_cast(i)}; + frame.process_frame(f, 0); REQUIRE(frame[0] == acc / (i + 1)); } } @@ -194,8 +208,8 @@ TEST_CASE("pictura_mediocritas::average_frame::pixel()") { acc_inc += i; acc_dec += 255 - i; - const std::uint8_t f[] = {static_cast(i), static_cast(255 - i)}; - frame.process_frame(f); + faux<2> f{static_cast(i), static_cast(255 - i)}; + frame.process_frame(f, 0); REQUIRE(frame.pixel(0) == (std::array{acc_inc / (i + 1), acc_dec / (i + 1)})); } } From 5a20b425b9bafcc0ef080a50210e1d583d8ca6cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Fri, 22 Mar 2024 15:57:00 +0100 Subject: [PATCH 17/29] Index in inlineable [45 -> 49FPS] --- src/parser/ffmpeg.cpp | 8 -------- src/parser/ffmpeg.hpp | 10 +++++++++- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/parser/ffmpeg.cpp b/src/parser/ffmpeg.cpp index 07b8875..eeadd09 100644 --- a/src/parser/ffmpeg.cpp +++ b/src/parser/ffmpeg.cpp @@ -334,11 +334,3 @@ bool pictura_mediocritas::ffmpeg_parser::process(const std::function & c return true; } - -std::uint8_t pictura_mediocritas::ffmpeg_parser::operator[](const deref & idx) const noexcept { - auto & out_frame = out_frames[idx.frame_num % out_frames.size()]; - if(out_frame && out_frame->data[0]) - return out_frame->data[0][idx.idx]; - else - return -1; -} diff --git a/src/parser/ffmpeg.hpp b/src/parser/ffmpeg.hpp index 1f3ec1e..7e6f2a0 100644 --- a/src/parser/ffmpeg.hpp +++ b/src/parser/ffmpeg.hpp @@ -108,7 +108,7 @@ namespace pictura_mediocritas { /// Get the error string, or `nullopt` if conversion to bool is `true`. std::optional error() const; - /// Get current frame's size as `{width, height}`, or `{0, 0}` if noty yet ready. + /// Get current frame's size as `{width, height}`, or `{0, 0}` if not yet ready. std::pair size() const noexcept; /// Get frame count, or `0` if not yet ready. @@ -127,6 +127,14 @@ namespace pictura_mediocritas { std::size_t idx; std::size_t frame_num; }; + template std::uint8_t operator[](const deref & idx) const noexcept; }; + + + template + std::uint8_t pictura_mediocritas::ffmpeg_parser::operator[](const deref & idx) const noexcept { + auto & out_frame = out_frames[idx.frame_num % out_frames.size()]; + return out_frame->data[0][idx.idx]; + } } From 1a9503cacdd00bca6644750ddbc31976649b565d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Fri, 22 Mar 2024 16:53:42 +0100 Subject: [PATCH 18/29] Re-add rudimentary progress bar --- configMakefile | 2 +- src/main.cpp | 34 +++++++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/configMakefile b/configMakefile index 6e2586a..762c607 100644 --- a/configMakefile +++ b/configMakefile @@ -51,7 +51,7 @@ PICTURA_MEDIOCRITAS_VERSION := "0.1.0" OBJ := .o ARCH := .a AR := ar -CXXAR := -MD -O3 -fomit-frame-pointer -march=native -std=c++20 -pedantic -Wall -Wextra -Wno-missing-field-initializers -pipe $(INCCXXAR) $(PIC) +CXXAR := -g -MD -O3 -fomit-frame-pointer -march=native -std=c++20 -pedantic -Wall -Wextra -Wno-missing-field-initializers -pipe $(INCCXXAR) $(PIC) OUTDIR := out/ BLDDIR := out/build/ diff --git a/src/main.cpp b/src/main.cpp index 5e9fe4e..5322353 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -38,6 +38,8 @@ extern "C" { #include } +using namespace std::chrono_literals; + int main(int argc, const char ** argv) { const auto opts_r = pictura_mediocritas::options::parse(argc, argv); @@ -64,7 +66,29 @@ int main(int argc, const char ** argv) { std::atomic cur_frame_num; std::atomic_flag done; }; - thread threads[MAXTHREADS] = {{{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}},{{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}}; + thread threads[MAXTHREADS] = {{{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}}; + const auto start = std::chrono::high_resolution_clock::now(); + std::jthread statussy{[&] { + auto write = [&](std::size_t done) { + const auto now = std::chrono::high_resolution_clock::now(); + const auto len = parser.length(); + std::fprintf(stderr, "\r%*zu/%zu\t%.4f/s", (int)std::log10(len | 1) + 1, done, parser.length(), + (done / static_cast(std::chrono::duration_cast(now - start).count()) * 1000)); + }; + + for(;;) { + for(int i = 0; i < 10; ++i) { + std::this_thread::sleep_for(100ms); + if(threads[0].done.test()) + goto done; + } + if(isatty(2)) + write(threads[0].cur_frame_num.load(std::memory_order_relaxed)); + } + done: + write(parser.length() ? parser.length() : threads[0].cur_frame_num.load(std::memory_order_relaxed)); + std::fputc('\n', stderr); + }}; if(!parser.process([&]() { if(avg_frame.size().first == 0) { avg_frame = decltype(avg_frame)(parser.size()); @@ -74,7 +98,6 @@ int main(int argc, const char ** argv) { while(pthread_barrier_init(&threads[i].barrier, nullptr, 2)) ; threads[i].thread = std::thread{[&, i = i] { - char id = '0' + i; auto & self = threads[i]; pthread_barrier_wait(&self.barrier); @@ -84,7 +107,6 @@ int main(int argc, const char ** argv) { break; auto frame = self.cur_frame_num.load(std::memory_order_relaxed); self.avg_frame.process_frame(parser, frame); - write(2, &id, 1); pthread_barrier_wait(&self.barrier); } }}; @@ -98,11 +120,9 @@ int main(int argc, const char ** argv) { return true; })) { - std::cerr << "\nParsing " << opts.in_video << " failed: " << *parser.error() << '\n'; - return 1; + std::cerr << "Parsing " << opts.in_video << " failed: " << *parser.error() << '\n'; + std::exit(1); } else { - write(2, "\n", 1); - for(auto i = 0u; i < thread_cnt; ++i) { threads[i].done.test_and_set(); pthread_barrier_wait(&threads[i].barrier); From 1cd1ea3eba464ecdafa0d55d9bc11ae5ada38f34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Fri, 22 Mar 2024 17:02:21 +0100 Subject: [PATCH 19/29] Flip once at the end [47.5 -> 88.9FPS] --- src/average_frame.hpp | 3 +++ src/average_frame.inc | 5 +++++ src/main.cpp | 2 ++ src/parser/ffmpeg.cpp | 7 ------- src/parser/ffmpeg.hpp | 17 +++++++++++++++-- 5 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/average_frame.hpp b/src/average_frame.hpp index f4d71d7..943b2a0 100644 --- a/src/average_frame.hpp +++ b/src/average_frame.hpp @@ -108,6 +108,9 @@ namespace pictura_mediocritas { /// /// The behaviour is undefined if `idx` ∉ [0; width * height) ∩ ℤ. std::array pixel(std::size_t idx) const; + + /// Swap pixels at the given coordinates, both within [0; width * height * Channels) ∩ ℤ. + void swap(std::size_t idx1, std::size_t idx2); }; diff --git a/src/average_frame.inc b/src/average_frame.inc index d9fc6db..f8d00bd 100644 --- a/src/average_frame.inc +++ b/src/average_frame.inc @@ -88,6 +88,11 @@ std::array pictura_mediocritas::average_frame::p return res; } +template +void pictura_mediocritas::average_frame::swap(std::size_t idx1, std::size_t idx2) { + std::swap(pixels[idx1], pixels[idx2]); +} + template bool pictura_mediocritas::operator==(const average_frame & lhs, const average_frame & rhs) { diff --git a/src/main.cpp b/src/main.cpp index 5322353..13bba92 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -131,6 +131,8 @@ int main(int argc, const char ** argv) { avg_frame += threads[i].avg_frame; } } + + parser.postprocess(avg_frame); } else if(parser.error() == "") { std::cerr << "Couldn't open " << opts.in_video << ".\n"; return 1; diff --git a/src/parser/ffmpeg.cpp b/src/parser/ffmpeg.cpp index eeadd09..ea0a441 100644 --- a/src/parser/ffmpeg.cpp +++ b/src/parser/ffmpeg.cpp @@ -96,13 +96,6 @@ bool pictura_mediocritas::ffmpeg_parser::receive_frame(const std::functionheight / 2; ++y) - for(auto x = 0; x < out_frame->width; ++x) - for(auto c = 0u; c < channels; ++c) - std::swap(out_frame->data[0][(y * out_frame->width + x) * channels + c], - out_frame->data[0][((out_frame->height - 1 - y) * out_frame->width + x) * channels + c]); - if(!callback()) return true; } diff --git a/src/parser/ffmpeg.hpp b/src/parser/ffmpeg.hpp index 7e6f2a0..5c716f7 100644 --- a/src/parser/ffmpeg.hpp +++ b/src/parser/ffmpeg.hpp @@ -117,6 +117,10 @@ namespace pictura_mediocritas { /// Run through every frame, calling `callback()` after initialising it. bool process(const std::function & callback); + /// Finalise the specified framebuffer, which must have a &-returning operator[] valid in [0; width * height * channels) ∩ ℤ + template + void postprocess(FB & fb); + /// Get the specified colour fragment. /// /// Valid inside [0; width * height * channels) ∩ ℤ, undefined behaviour thereoutside. @@ -127,12 +131,21 @@ namespace pictura_mediocritas { std::size_t idx; std::size_t frame_num; }; - template + template std::uint8_t operator[](const deref & idx) const noexcept; }; - template + template + void pictura_mediocritas::ffmpeg_parser::postprocess(FB & fb) { + for(auto y = 0; y < out_frames[0]->height / 2; ++y) + for(auto x = 0; x < out_frames[0]->width; ++x) + for(auto c = 0u; c < channels; ++c) + fb.swap((y * out_frames[0]->width + x) * channels + c, ((out_frames[0]->height - 1 - y) * out_frames[0]->width + x) * channels + c); + } + + + template std::uint8_t pictura_mediocritas::ffmpeg_parser::operator[](const deref & idx) const noexcept { auto & out_frame = out_frames[idx.frame_num % out_frames.size()]; return out_frame->data[0][idx.idx]; From 4c316698340cb9fd8f62555b9ddb71ef220a208e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Sat, 23 Mar 2024 11:26:48 +0100 Subject: [PATCH 20/29] Ignore AVERROR_INVALIDDATA and disable the log callback (kinda like ffmpeg) --- src/parser/ffmpeg.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/parser/ffmpeg.cpp b/src/parser/ffmpeg.cpp index ea0a441..4d74f43 100644 --- a/src/parser/ffmpeg.cpp +++ b/src/parser/ffmpeg.cpp @@ -55,6 +55,10 @@ bool pictura_mediocritas::ffmpeg_parser::send_packet(AVPacket * pkt) noexcept { case AVERROR_EOF: return false; + case AVERROR_INVALIDDATA: + av_log_set_callback([](void*, int, const char*, va_list) {}); + return false; + default: error_value = err; error_class = error_class_t::send_packet; From 53f62173b901b8b747da5b8e910cdf4ce4c40d63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Sat, 23 Mar 2024 11:27:32 +0100 Subject: [PATCH 21/29] Dump input format like ffmpeg --- src/parser/ffmpeg.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/parser/ffmpeg.cpp b/src/parser/ffmpeg.cpp index 4d74f43..32b8f21 100644 --- a/src/parser/ffmpeg.cpp +++ b/src/parser/ffmpeg.cpp @@ -145,6 +145,8 @@ pictura_mediocritas::ffmpeg_parser::ffmpeg_parser(const char * filename, std::si return; } + av_dump_format(container.get(), 0, filename, false); + orig_frame.reset(av_frame_alloc()); if(!orig_frame) return; From 1fe7fcbd07a32989cb671acba0c4bcde8f9420dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Sat, 23 Mar 2024 11:36:22 +0100 Subject: [PATCH 22/29] No need to do the additional links --- Makefile | 4 +--- configMakefile | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index cf3f820..a1b5360 100644 --- a/Makefile +++ b/Makefile @@ -23,9 +23,7 @@ include configMakefile -# If ffmpeg autodetects something for your system, you can put it here to link to -FFMPEG_PREREQUESITE_LD_LIBS := bz2 lzma z -LDAR := $(PIC) $(LNCXXAR) $(foreach l,swscale avformat avcodec avutil freeimage $(FFMPEG_PREREQUESITE_LD_LIBS) $(OS_LD_LIBS),-l$(l)) +LDAR := $(PIC) $(LNCXXAR) $(foreach l,swscale avformat avcodec avutil freeimage $(OS_LD_LIBS),-l$(l)) VERAR := $(foreach l,PICTURA_MEDIOCRITAS,-D$(l)_VERSION='$($(l)_VERSION)') TEST_SOURCES := $(sort $(wildcard tests/*.cpp tests/**/*.cpp tests/**/**/*.cpp tests/**/**/**/*.cpp)) BUILD_TEST_SOURCES := $(sort $(wildcard build-tests/*.cpp build-tests/**/*.cpp build-tests/**/**/*.cpp build-tests/**/**/**/*.cpp)) diff --git a/configMakefile b/configMakefile index 762c607..4da0498 100644 --- a/configMakefile +++ b/configMakefile @@ -24,12 +24,12 @@ ifeq "$(OS)" "Windows_NT" EXE := .exe PIC := ECHO := /usr/bin/echo - OS_LD_LIBS := ole32 ws2_32 + OS_LD_LIBS := else EXE := PIC := -fPIC ECHO := echo - OS_LD_LIBS := dl pthread + OS_LD_LIBS := pthread endif ifneq "$(ADDITIONAL_INCLUDE_DIR)" "" From faddb62dd5a3d432226048932f7f85a985ea0d6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Sat, 23 Mar 2024 11:47:10 +0100 Subject: [PATCH 23/29] Actually you do need gif-specific processing This also reverts 22261c3e8b8a24833673a517df56fa95901e9846 --- src/average_frame.hpp | 10 +-- src/average_frame.inc | 23 +++--- src/main.cpp | 160 ++++++++++++++++++++----------------- src/parser/multi_image.cpp | 105 ++++++++++++++++++++++++ src/parser/multi_image.hpp | 85 ++++++++++++++++++++ 5 files changed, 294 insertions(+), 89 deletions(-) create mode 100644 src/parser/multi_image.cpp create mode 100644 src/parser/multi_image.hpp diff --git a/src/average_frame.hpp b/src/average_frame.hpp index 943b2a0..b96bfe6 100644 --- a/src/average_frame.hpp +++ b/src/average_frame.hpp @@ -80,15 +80,15 @@ namespace pictura_mediocritas { /// Get a frame from the specified source. /// - /// The `frame` argument must be indexable by a `{std::size_t, frame_ctr}` where the size_t is in [0; width * height * Channels) ∩ ℤ. - template - void process_frame(const FrameT & frame, std::size_t frame_ctr); + /// The `frame` argument must be indexable by a `{std::size_t, Additional...}` where the size_t is in [0; width * height * Channels) ∩ ℤ. + template + void process_frame(const FrameT & frame, Additional &&... frame_ctr); /// Get a frame from the specified source. /// /// The `frame` argument must be indexable by a `{std::size_t, frame_ctr}` where the size_t is in [0; width * height * Channels) ∩ ℤ. - template - void process_frame(FrameT & frame, std::size_t frame_ctr); + template + void process_frame(FrameT & frame, Additional &&... frame_ctr); /// Add the values from a partial frame to this one. average_frame & operator+=(const average_frame & partial); diff --git a/src/average_frame.inc b/src/average_frame.inc index f8d00bd..55f9c6b 100644 --- a/src/average_frame.inc +++ b/src/average_frame.inc @@ -48,21 +48,24 @@ AccT pictura_mediocritas::average_frame::processed_frames() cons return frames; } -template -template -void pictura_mediocritas::average_frame::process_frame(const FrameT & frame, std::size_t frame_ctr) { - const auto pixels_len = pixels.size(); - for(std::size_t i = 0; i < pixels_len; ++i) - pixels[i] += frame[{i, frame_ctr}]; - +#define PROCESS_FRAME_IMPL() \ + const auto pixels_len = pixels.size(); \ + for(std::size_t i = 0; i < pixels_len; ++i) \ + pixels[i] += frame[{i, frame_ctr...}]; \ + \ ++frames; + +template +template +void pictura_mediocritas::average_frame::process_frame(const FrameT & frame, Additional &&... frame_ctr) { + PROCESS_FRAME_IMPL(); } template -template -void pictura_mediocritas::average_frame::process_frame(FrameT & frame, std::size_t frame_ctr) { - process_frame(const_cast(frame), frame_ctr); +template +void pictura_mediocritas::average_frame::process_frame(FrameT & frame, Additional &&... frame_ctr) { + PROCESS_FRAME_IMPL(); } template diff --git a/src/main.cpp b/src/main.cpp index 13bba92..a163d1a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,6 +24,7 @@ #include "options.hpp" #include "output_image.hpp" #include "parser/ffmpeg.hpp" +#include "parser/multi_image.hpp" #include "util.hpp" #include #include @@ -57,88 +58,99 @@ int main(int argc, const char ** argv) { pictura_mediocritas::average_frame_u64 avg_frame(0, 0); auto thread_cnt = std::clamp(std::thread::hardware_concurrency(), 1u, MAXTHREADS); - pictura_mediocritas::ffmpeg_parser parser(opts.in_video.data(), decltype(avg_frame)::channels, thread_cnt); - if(parser) { - struct thread { - pictura_mediocritas::average_frame_u64 avg_frame; - std::thread thread; - pthread_barrier_t barrier; - std::atomic cur_frame_num; - std::atomic_flag done; - }; - thread threads[MAXTHREADS] = {{{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}}; - const auto start = std::chrono::high_resolution_clock::now(); - std::jthread statussy{[&] { - auto write = [&](std::size_t done) { - const auto now = std::chrono::high_resolution_clock::now(); - const auto len = parser.length(); - std::fprintf(stderr, "\r%*zu/%zu\t%.4f/s", (int)std::log10(len | 1) + 1, done, parser.length(), - (done / static_cast(std::chrono::duration_cast(now - start).count()) * 1000)); - }; + if(pictura_mediocritas::has_extension(opts.in_video.data(), "gif")) { + pictura_mediocritas::multi_image_parser parser(FreeImage_OpenMultiBitmap(FIF_GIF, opts.in_video.data(), false, true, true, GIF_LOAD256 | GIF_PLAYBACK), + decltype(avg_frame)::channels); + avg_frame = decltype(avg_frame)(parser.size()); - for(;;) { - for(int i = 0; i < 10; ++i) { - std::this_thread::sleep_for(100ms); - if(threads[0].done.test()) - goto done; + for(auto i = 0u; i < parser.length(); ++i) { + avg_frame.process_frame(parser); + parser.next(); + } + } else { + pictura_mediocritas::ffmpeg_parser parser(opts.in_video.data(), decltype(avg_frame)::channels, thread_cnt); + if(parser) { + struct thread { + pictura_mediocritas::average_frame_u64 avg_frame; + std::thread thread; + pthread_barrier_t barrier; + std::atomic cur_frame_num; + std::atomic_flag done; + }; + thread threads[MAXTHREADS] = {{{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}}; + const auto start = std::chrono::high_resolution_clock::now(); + std::jthread statussy{[&] { + auto write = [&](std::size_t done) { + const auto now = std::chrono::high_resolution_clock::now(); + const auto len = parser.length(); + std::fprintf(stderr, "\r%*zu/%zu\t%.4f/s", (int)std::log10(len | 1) + 1, done, parser.length(), + (done / static_cast(std::chrono::duration_cast(now - start).count()) * 1000)); + }; + + for(;;) { + for(int i = 0; i < 10; ++i) { + std::this_thread::sleep_for(100ms); + if(threads[0].done.test()) + goto done; + } + if(isatty(2)) + write(threads[0].cur_frame_num.load(std::memory_order_relaxed)); } - if(isatty(2)) - write(threads[0].cur_frame_num.load(std::memory_order_relaxed)); - } - done: - write(parser.length() ? parser.length() : threads[0].cur_frame_num.load(std::memory_order_relaxed)); - std::fputc('\n', stderr); - }}; - if(!parser.process([&]() { - if(avg_frame.size().first == 0) { - avg_frame = decltype(avg_frame)(parser.size()); - - for(auto i = 0u; i < thread_cnt; ++i) { - threads[i].avg_frame = decltype(avg_frame)(parser.size()); - while(pthread_barrier_init(&threads[i].barrier, nullptr, 2)) - ; - threads[i].thread = std::thread{[&, i = i] { - auto & self = threads[i]; - - pthread_barrier_wait(&self.barrier); - for(;;) { - pthread_barrier_wait(&self.barrier); - if(self.done.test()) - break; - auto frame = self.cur_frame_num.load(std::memory_order_relaxed); - self.avg_frame.process_frame(parser, frame); + done: + write(parser.length() ? parser.length() : threads[0].cur_frame_num.load(std::memory_order_relaxed)); + std::fputc('\n', stderr); + }}; + if(!parser.process([&]() { + if(avg_frame.size().first == 0) { + avg_frame = decltype(avg_frame)(parser.size()); + + for(auto i = 0u; i < thread_cnt; ++i) { + threads[i].avg_frame = decltype(avg_frame)(parser.size()); + while(pthread_barrier_init(&threads[i].barrier, nullptr, 2)) + ; + threads[i].thread = std::thread{[&, i = i] { + auto & self = threads[i]; + pthread_barrier_wait(&self.barrier); - } - }}; + for(;;) { + pthread_barrier_wait(&self.barrier); + if(self.done.test()) + break; + auto frame = self.cur_frame_num.load(std::memory_order_relaxed); + self.avg_frame.process_frame(parser, frame); + pthread_barrier_wait(&self.barrier); + } + }}; + } } - } - auto & thread = threads[parser.frame_num % thread_cnt]; - pthread_barrier_wait(&thread.barrier); - thread.cur_frame_num.store(parser.frame_num, std::memory_order_relaxed); - pthread_barrier_wait(&thread.barrier); + auto & thread = threads[parser.frame_num % thread_cnt]; + pthread_barrier_wait(&thread.barrier); + thread.cur_frame_num.store(parser.frame_num, std::memory_order_relaxed); + pthread_barrier_wait(&thread.barrier); + + return true; + })) { + std::cerr << "Parsing " << opts.in_video << " failed: " << *parser.error() << '\n'; + std::exit(1); + } else { + for(auto i = 0u; i < thread_cnt; ++i) { + threads[i].done.test_and_set(); + pthread_barrier_wait(&threads[i].barrier); + pthread_barrier_wait(&threads[i].barrier); + threads[i].thread.join(); + avg_frame += threads[i].avg_frame; + } + } - return true; - })) { - std::cerr << "Parsing " << opts.in_video << " failed: " << *parser.error() << '\n'; - std::exit(1); + parser.postprocess(avg_frame); + } else if(parser.error() == "") { + std::cerr << "Couldn't open " << opts.in_video << ".\n"; + return 1; } else { - for(auto i = 0u; i < thread_cnt; ++i) { - threads[i].done.test_and_set(); - pthread_barrier_wait(&threads[i].barrier); - pthread_barrier_wait(&threads[i].barrier); - threads[i].thread.join(); - avg_frame += threads[i].avg_frame; - } + std::cerr << "Could not find codec for " << opts.in_video << ": " << *parser.error() << '\n'; + return 1; } - - parser.postprocess(avg_frame); - } else if(parser.error() == "") { - std::cerr << "Couldn't open " << opts.in_video << ".\n"; - return 1; - } else { - std::cerr << "Could not find codec for " << opts.in_video << ": " << *parser.error() << '\n'; - return 1; } diff --git a/src/parser/multi_image.cpp b/src/parser/multi_image.cpp new file mode 100644 index 0000000..caa8107 --- /dev/null +++ b/src/parser/multi_image.cpp @@ -0,0 +1,105 @@ +// The MIT License (MIT) + +// Copyright (c) 2017 Lounge + +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + + +#include "multi_image.hpp" +#include + + +void pictura_mediocritas::freeimage_multi_bitmap_deleter::operator()(FIMULTIBITMAP * multi_bitmap) const noexcept { + FreeImage_CloseMultiBitmap(multi_bitmap); +} + +void pictura_mediocritas::freeimage_page_unlocker::operator()(FIBITMAP * page) const noexcept { + FreeImage_UnlockPage(owner, page, changed); +} + + +void pictura_mediocritas::multi_image_parser::lock_page() { + cur_page.reset(FreeImage_LockPage(image.get(), cur_page_idx)); + width = 0; + height = 0; +} + +pictura_mediocritas::multi_image_parser::multi_image_parser(FIMULTIBITMAP * i, std::size_t c) + : channels(c), image(i), cur_page(0, freeimage_page_unlocker{i, false}), cur_page_idx(0), pages(0), width(0), height(0), cached(false) { + lock_page(); +} + +std::pair pictura_mediocritas::multi_image_parser::size() noexcept { + if(cur_page && (!width || !height)) { + width = FreeImage_GetWidth(cur_page.get()); + height = FreeImage_GetHeight(cur_page.get()); + } + + return {width, height}; +} + +std::size_t pictura_mediocritas::multi_image_parser::length() noexcept { + if(!pages) + pages = FreeImage_GetPageCount(image.get()); + + return pages; +} + +void pictura_mediocritas::multi_image_parser::next() { + ++cur_page_idx; + lock_page(); +} + +std::uint8_t pictura_mediocritas::multi_image_parser::operator[](std::size_t idx) { + if(!channels) + return -1; + + const auto width = size().first; + if(!width) + return -1; + + const auto channel = idx % channels; // idx = (y * width + x) * decltype(frame)::channels + channel + idx -= channel; // idx = (y * width + x) * decltype(frame)::channels + idx /= channels; // idx = y * width + x + const auto x = idx % width; // + idx -= x; // idx = y * width + idx /= width; // idx = y + const auto y = idx; // + + if(!cached || (cache_x != x || cache_y != y)) { + cached = FreeImage_GetPixelColor(cur_page.get(), x, y, &cache); + cache_x = x; + cache_y = y; + } + + if(cached) + switch(channel) { + case 0: + return cache.rgbRed; + case 1: + return cache.rgbGreen; + case 2: + return cache.rgbBlue; + case 3: + return cache.rgbReserved; + } + + std::cerr << "Failed to get pixel " << x << 'x' << y << " frame #" << cur_page_idx << ".\n"; + return -1; +} diff --git a/src/parser/multi_image.hpp b/src/parser/multi_image.hpp new file mode 100644 index 0000000..7371add --- /dev/null +++ b/src/parser/multi_image.hpp @@ -0,0 +1,85 @@ +// The MIT License (MIT) + +// Copyright (c) 2017 Lounge + +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + + +#pragma once + + +#include +#include +#include + + +namespace pictura_mediocritas { + struct freeimage_multi_bitmap_deleter { + void operator()(FIMULTIBITMAP * multi_bitmap) const noexcept; + }; + + struct freeimage_page_unlocker { + FIMULTIBITMAP * owner; + bool changed; + + void operator()(FIBITMAP * page) const noexcept; + }; + + class multi_image_parser { + private: + std::size_t channels; + + std::unique_ptr image; + std::unique_ptr cur_page; + std::size_t cur_page_idx; + + std::size_t pages; + std::size_t width; + std::size_t height; + + bool cached; + RGBQUAD cache; + std::size_t cache_x; + std::size_t cache_y; + + void lock_page(); + + public: + /// Create a parser from an opened multi-image and the channel count in the target frame. + multi_image_parser(FIMULTIBITMAP * image, std::size_t channels); + + + /// Get current frame's size as `{width, height}`. + std::pair size() noexcept; + + /// Get frame count. + std::size_t length() noexcept; + + /// Load the next frame. + void next(); + + /// Get the specified colour fragment. + /// + /// Valid inside [0; width * height * channels) ∩ ℤ, undefined behaviour thereoutside. + /// + /// `idx` is in the format `(y * width + x) * decltype(frame)::channels + channel` + /// (i.e. the one required by `average_frame`). + std::uint8_t operator[](std::size_t idx); + }; +} From 4a56efaab796b8858b3969619ba7431dc6f62dd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Sat, 23 Mar 2024 12:04:02 +0100 Subject: [PATCH 24/29] templated operator[] --- src/parser/multi_image.cpp | 38 ------------------------------------- src/parser/multi_image.hpp | 39 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 38 deletions(-) diff --git a/src/parser/multi_image.cpp b/src/parser/multi_image.cpp index caa8107..9fc6164 100644 --- a/src/parser/multi_image.cpp +++ b/src/parser/multi_image.cpp @@ -65,41 +65,3 @@ void pictura_mediocritas::multi_image_parser::next() { ++cur_page_idx; lock_page(); } - -std::uint8_t pictura_mediocritas::multi_image_parser::operator[](std::size_t idx) { - if(!channels) - return -1; - - const auto width = size().first; - if(!width) - return -1; - - const auto channel = idx % channels; // idx = (y * width + x) * decltype(frame)::channels + channel - idx -= channel; // idx = (y * width + x) * decltype(frame)::channels - idx /= channels; // idx = y * width + x - const auto x = idx % width; // - idx -= x; // idx = y * width - idx /= width; // idx = y - const auto y = idx; // - - if(!cached || (cache_x != x || cache_y != y)) { - cached = FreeImage_GetPixelColor(cur_page.get(), x, y, &cache); - cache_x = x; - cache_y = y; - } - - if(cached) - switch(channel) { - case 0: - return cache.rgbRed; - case 1: - return cache.rgbGreen; - case 2: - return cache.rgbBlue; - case 3: - return cache.rgbReserved; - } - - std::cerr << "Failed to get pixel " << x << 'x' << y << " frame #" << cur_page_idx << ".\n"; - return -1; -} diff --git a/src/parser/multi_image.hpp b/src/parser/multi_image.hpp index 7371add..fa8d823 100644 --- a/src/parser/multi_image.hpp +++ b/src/parser/multi_image.hpp @@ -80,6 +80,45 @@ namespace pictura_mediocritas { /// /// `idx` is in the format `(y * width + x) * decltype(frame)::channels + channel` /// (i.e. the one required by `average_frame`). + template std::uint8_t operator[](std::size_t idx); }; + + + template + std::uint8_t pictura_mediocritas::multi_image_parser::operator[](std::size_t idx) { + if(!channels) + return -1; + + const auto width = size().first; + if(!width) + return -1; + + const auto channel = idx % channels; // idx = (y * width + x) * decltype(frame)::channels + channel + idx -= channel; // idx = (y * width + x) * decltype(frame)::channels + idx /= channels; // idx = y * width + x + const auto x = idx % width; // + idx -= x; // idx = y * width + idx /= width; // idx = y + const auto y = idx; // + + if(!cached || (cache_x != x || cache_y != y)) { + cached = FreeImage_GetPixelColor(cur_page.get(), x, y, &cache); + cache_x = x; + cache_y = y; + } + + if(cached) [[likely]] + switch(channel) { + case 0: + return cache.rgbRed; + case 1: + return cache.rgbGreen; + case 2: + return cache.rgbBlue; + case 3: + return cache.rgbReserved; + } + std::terminate(); + } } From 3f651ead4555d4ba8a3ed1aacb79acd79a6f3428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Sat, 23 Mar 2024 12:04:28 +0100 Subject: [PATCH 25/29] Progress for GIF too --- src/main.cpp | 57 +++++++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index a163d1a..299d807 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -42,6 +42,33 @@ extern "C" { using namespace std::chrono_literals; +#define STATUSSY(donetest, curframe) \ + const auto start = std::chrono::high_resolution_clock::now(); \ + std::jthread statussy { \ + [&] { \ + auto write = [&](std::size_t done) { \ + const auto now = std::chrono::high_resolution_clock::now(); \ + const auto len = parser.length(); \ + std::fprintf(stderr, "\r%*zu/%zu\t%.4f/s", (int)std::log10(len | 1) + 1, done, len, \ + (done / static_cast(std::chrono::duration_cast(now - start).count()) * 1000)); \ + }; \ + \ + for(;;) { \ + for(int _ = 0; _ < 10; ++_) { \ + std::this_thread::sleep_for(100ms); \ + if(donetest) \ + goto done; \ + } \ + if(isatty(2)) \ + write(curframe); \ + } \ + done: \ + write(parser.length() ? parser.length() : curframe); \ + std::fputc('\n', stderr); \ + } \ + } + + int main(int argc, const char ** argv) { const auto opts_r = pictura_mediocritas::options::parse(argc, argv); if(std::get<1>(opts_r)) { @@ -61,9 +88,10 @@ int main(int argc, const char ** argv) { if(pictura_mediocritas::has_extension(opts.in_video.data(), "gif")) { pictura_mediocritas::multi_image_parser parser(FreeImage_OpenMultiBitmap(FIF_GIF, opts.in_video.data(), false, true, true, GIF_LOAD256 | GIF_PLAYBACK), decltype(avg_frame)::channels); - avg_frame = decltype(avg_frame)(parser.size()); - - for(auto i = 0u; i < parser.length(); ++i) { + avg_frame = decltype(avg_frame)(parser.size()); + std::size_t i = 0; + STATUSSY(i == parser.length(), i); + for(; i < parser.length(); ++i) { avg_frame.process_frame(parser); parser.next(); } @@ -78,28 +106,7 @@ int main(int argc, const char ** argv) { std::atomic_flag done; }; thread threads[MAXTHREADS] = {{{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}}; - const auto start = std::chrono::high_resolution_clock::now(); - std::jthread statussy{[&] { - auto write = [&](std::size_t done) { - const auto now = std::chrono::high_resolution_clock::now(); - const auto len = parser.length(); - std::fprintf(stderr, "\r%*zu/%zu\t%.4f/s", (int)std::log10(len | 1) + 1, done, parser.length(), - (done / static_cast(std::chrono::duration_cast(now - start).count()) * 1000)); - }; - - for(;;) { - for(int i = 0; i < 10; ++i) { - std::this_thread::sleep_for(100ms); - if(threads[0].done.test()) - goto done; - } - if(isatty(2)) - write(threads[0].cur_frame_num.load(std::memory_order_relaxed)); - } - done: - write(parser.length() ? parser.length() : threads[0].cur_frame_num.load(std::memory_order_relaxed)); - std::fputc('\n', stderr); - }}; + STATUSSY(threads[0].done.test(), threads[0].cur_frame_num.load(std::memory_order_relaxed)); if(!parser.process([&]() { if(avg_frame.size().first == 0) { avg_frame = decltype(avg_frame)(parser.size()); From 6571926a659b0c222765274462fbb12ba6327667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Sat, 23 Mar 2024 12:43:43 +0100 Subject: [PATCH 26/29] README cleanup --- README.md | 3 ++- src/main.cpp | 5 ++--- tests/average_frame.cpp | 27 ++++++++++++++++++--------- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 85e710f..18357d7 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,8 @@ Either acquire a binary release from [the releases page](https://github.com/Loun or build it yourself: ```sh -$ git clone --recursive https://github.com/LoungeCPP/PicturaMediocritas +# apt install libfreeimage-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev +$ git clone https://github.com/LoungeCPP/PicturaMediocritas $ cd PicturaMediocritas $ make $ install out/pictura-mediocritas $(wherever) diff --git a/src/main.cpp b/src/main.cpp index 299d807..1381eb4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -81,10 +81,7 @@ int main(int argc, const char ** argv) { FreeImage_Initialise(); pictura_mediocritas::quickscope_wrapper freeimage_deinitialiser{FreeImage_DeInitialise}; -#define MAXTHREADS 8u pictura_mediocritas::average_frame_u64 avg_frame(0, 0); - auto thread_cnt = std::clamp(std::thread::hardware_concurrency(), 1u, MAXTHREADS); - if(pictura_mediocritas::has_extension(opts.in_video.data(), "gif")) { pictura_mediocritas::multi_image_parser parser(FreeImage_OpenMultiBitmap(FIF_GIF, opts.in_video.data(), false, true, true, GIF_LOAD256 | GIF_PLAYBACK), decltype(avg_frame)::channels); @@ -96,6 +93,8 @@ int main(int argc, const char ** argv) { parser.next(); } } else { +#define MAXTHREADS 8u + auto thread_cnt = std::clamp(std::thread::hardware_concurrency(), 1u, MAXTHREADS); pictura_mediocritas::ffmpeg_parser parser(opts.in_video.data(), decltype(avg_frame)::channels, thread_cnt); if(parser) { struct thread { diff --git a/tests/average_frame.cpp b/tests/average_frame.cpp index 5209cd3..c4226cd 100644 --- a/tests/average_frame.cpp +++ b/tests/average_frame.cpp @@ -82,7 +82,7 @@ TEST_CASE("pictura_mediocritas::channels") { } TEST_CASE("pictura_mediocritas::value_type") { -#define TYPES(t) \ +#define TYPES(t) \ REQUIRE((std::is_same::value_type, t>::value)); \ REQUIRE((std::is_same::value_type, t>::value)); \ REQUIRE((std::is_same::value_type, t>::value)); \ @@ -161,16 +161,25 @@ TEST_CASE("pictura_mediocritas::average_frame::operator!=()") { } namespace { - template + template struct faux { std::uint8_t data[N]; struct idx { - std::size_t i, _; + std::size_t i; }; - const std::uint8_t & operator[](const struct idx & idx) const { - return data[idx.i]; - } + const std::uint8_t & operator[](const struct idx & idx) const { return data[idx.i]; } + }; + + template + struct faux2 { + std::uint8_t data[N]; + + struct idx { + std::size_t i; + int _; + }; + const std::uint8_t & operator[](const struct idx & idx) const { return data[idx.i]; } }; } @@ -179,7 +188,7 @@ TEST_CASE("pictura_mediocritas::average_frame::processed_frames()") { for(int i = 0; i < 10; ++i) { REQUIRE(frame.processed_frames() == i); faux<1> f{static_cast('0' + i)}; - frame.process_frame(f, 0); + frame.process_frame(f); REQUIRE(frame.processed_frames() == i + 1); } } @@ -192,7 +201,7 @@ TEST_CASE("pictura_mediocritas::average_frame::operator[]()") { for(auto i = 0u; i < 256; ++i) { acc += i; - faux<1> f{static_cast(i)}; + faux2<1> f{static_cast(i)}; frame.process_frame(f, 0); REQUIRE(frame[0] == acc / (i + 1)); } @@ -209,7 +218,7 @@ TEST_CASE("pictura_mediocritas::average_frame::pixel()") { acc_dec += 255 - i; faux<2> f{static_cast(i), static_cast(255 - i)}; - frame.process_frame(f, 0); + frame.process_frame(f); REQUIRE(frame.pixel(0) == (std::array{acc_inc / (i + 1), acc_dec / (i + 1)})); } } From 7b83aa65e46de4c6f4bcae0bdf75eeb36aae761e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Sat, 23 Mar 2024 13:00:00 +0100 Subject: [PATCH 27/29] README cleanup --- .gitignore | 2 +- Makefile | 6 ++++++ README.md | 29 ++++++++--------------------- pictura-mediocritas.1 | 34 ++++++++++++++++++++++++++++++++++ pictura-mediocritas.md | 40 ---------------------------------------- 5 files changed, 49 insertions(+), 62 deletions(-) create mode 100644 pictura-mediocritas.1 delete mode 100644 pictura-mediocritas.md diff --git a/.gitignore b/.gitignore index 862c206..1f9000f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,4 @@ !gh_rsa.enc !README.md !pictura-mediocritas.rc -!pictura-mediocritas.md +!pictura-mediocritas.1 diff --git a/Makefile b/Makefile index a1b5360..1be6cfc 100644 --- a/Makefile +++ b/Makefile @@ -28,11 +28,17 @@ VERAR := $(foreach l,PICTURA_MEDIOCRITAS,-D$(l)_VERSION='$($(l)_VERSION)') TEST_SOURCES := $(sort $(wildcard tests/*.cpp tests/**/*.cpp tests/**/**/*.cpp tests/**/**/**/*.cpp)) BUILD_TEST_SOURCES := $(sort $(wildcard build-tests/*.cpp build-tests/**/*.cpp build-tests/**/**/*.cpp build-tests/**/**/**/*.cpp)) SOURCES := $(sort $(wildcard src/*.cpp src/**/*.cpp src/**/**/*.cpp src/**/**/**/*.cpp)) +PREFIX ?= /usr/local .PHONY : all clean exe tests no-build-tests run-tests all : exe tests no-build-tests run-tests +install : exe + mkdir -p $(DESTDIR)/$(PREFIX)/bin $(DESTDIR)/$(PREFIX)/share/man/man1 + cp $(OUTDIR)pictura-mediocritas$(EXE) $(DESTDIR)/$(PREFIX)/bin/ + gzip -9 < pictura-mediocritas.1 > $(DESTDIR)/$(PREFIX)/share/man/man1/pictura-mediocritas.1.gz + clean : rm -rf $(OUTDIR) diff --git a/README.md b/README.md index 18357d7..af7a732 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,11 @@ Like aurea mediocritas, but with frames in a video instead. Or, you know, get an average frame from a video. -## [Manpage](https://rawcdn.githack.com/LoungeCPP/PicturaMediocritas/man/pictura-mediocritas.1.html) +## Usage -## Examples +``` +usage: pictura-mediocritas in-video [out-image] +``` ```sh $ pictura-mediocritas "video.mp4" @@ -14,28 +16,13 @@ $ pictura-mediocritas "video.mp4" $ pictura-mediocritas "video.mp4" "video-average.jpg" ``` -For details, see the [manpage](https://rawcdn.githack.com/LoungeCPP/PicturaMediocritas/man/pictura-mediocritas.1.html). - ## Installation -Either acquire a binary release from [the releases page](https://github.com/LoungeCPP/PicturaMediocritas/releases), -or build it yourself: - ```sh -# apt install libfreeimage-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev +# apt install libfreeimage-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev doctest-dev +# or pacman -S mingw-w64-x86_64-ffmpeg mingw-w64-x86_64-doctest mingw-w64-x86_64-freeimage $ git clone https://github.com/LoungeCPP/PicturaMediocritas $ cd PicturaMediocritas -$ make -$ install out/pictura-mediocritas $(wherever) +$ make -j +$ make install ``` - -## Contributing - -Imperative commit messages, format your code with clang-format, you know the drill. - -Branch names *should* be in the format `{issue}-{name}-{desc}`, where -`issue` is the issue # this branch closes, -`name` is your name (or a shorthand, like "nab" for "nabijaczleweli"), and -`desc` is a few-word description of the changes in the branch (like "rework-dir-listing", "second-time"). - -Non-FF PRs (ergo, merge commits) are illegal. diff --git a/pictura-mediocritas.1 b/pictura-mediocritas.1 new file mode 100644 index 0000000..e92054d --- /dev/null +++ b/pictura-mediocritas.1 @@ -0,0 +1,34 @@ +.\" SPDX-License-Identifier: 0BSD +.\" +.Dd March 23, 2024 +.Dt PICTŪRA-MEDIOCRITAS 1 +.Os +. +.Sh NAME +.Nm pictura-mediocritas +.Nd average video frames into single image +.Sh SYNOPSIS +.Nm +.Ar video +.Op Ar image +. +.Sh DESCRIPTION +Like aurea mediocritas, but with frames in a video instead: +averages +.Ar video +.Pq which may be in GIF, or any Xr ffmpeg 1 Ns -understood, format +into +.Ar image +(which defaults to +.Ar video +but with the "extension" replaced with +.Pa .png ) . +. +.Sh AUTHORS +.An наб Aq Mt nabijaczleweli@nabijaczleweli.xyz +. +.Sh REPORTING BUGS +.Lk https://github.com/LoungeCPP/PicturaMediocritas/issues +. +.Sh SEE ALSO +.Lk https://github.com/LoungeCPP/PicturaMediocritas diff --git a/pictura-mediocritas.md b/pictura-mediocritas.md deleted file mode 100644 index f70c473..0000000 --- a/pictura-mediocritas.md +++ /dev/null @@ -1,40 +0,0 @@ -pictura-mediocritas(1) -- Like aurea mediocritas, but with frames in a video instead -==================================================================================== - -## SYNOPSIS - -`pictura-mediocritas` [OUT_IMAGE] - -## DESCRIPTION - -Like aurea mediocritas, but with frames in a video instead. - -Or, you know, get an average frame from a video. - -## OPTIONS - - - - Video to average, must exist. - - Can be in any format supported by ffmpeg and gif. - - [OUT_IMAGE] - - Image to write the averaged frame to. - - The parent directory must exist. - - Default: IN_VIDEO, but the extension is "png" instead. - -## AUTHOR - -Written by nabijaczleweli <> - -## REPORTING BUGS - -<> - -## SEE ALSO - -<> From 16d33e347e97f57d5013c68d4ffab06f8c097a73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Sat, 23 Mar 2024 13:05:41 +0100 Subject: [PATCH 28/29] Makefile detritus --- Makefile | 16 ++++++++-------- configMakefile | 9 --------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index 1be6cfc..f643bf1 100644 --- a/Makefile +++ b/Makefile @@ -47,28 +47,28 @@ run-tests : $(OUTDIR)pictura-mediocritas-tests$(EXE) exe : $(OUTDIR)pictura-mediocritas$(EXE) tests : $(OUTDIR)pictura-mediocritas-tests$(EXE) -no-build-tests : $(subst build-tests/,$(BLDDIR)build_test_obj/,$(subst .cpp,$(OBJ),$(BUILD_TEST_SOURCES))) +no-build-tests : $(subst build-tests/,$(BLDDIR)build_test_obj/,$(subst .cpp,.o,$(BUILD_TEST_SOURCES))) -$(OUTDIR)pictura-mediocritas$(EXE) : $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,$(OBJ),$(SOURCES))) +$(OUTDIR)pictura-mediocritas$(EXE) : $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,.o,$(SOURCES))) $(CXX) $(CXXAR) -o$@ $^ $(PIC) $(LDAR) -$(OUTDIR)pictura-mediocritas-tests$(EXE) : $(subst tests/,$(BLDDIR)test_obj/,$(subst .cpp,$(OBJ),$(TEST_SOURCES))) $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,$(OBJ),$(filter-out src/main.cpp,$(SOURCES)))) +$(OUTDIR)pictura-mediocritas-tests$(EXE) : $(subst tests/,$(BLDDIR)test_obj/,$(subst .cpp,.o,$(TEST_SOURCES))) $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,.o,$(filter-out src/main.cpp,$(SOURCES)))) $(CXX) $(CXXAR) -o$@ $^ $(PIC) $(LDAR) -$(OBJDIR)%$(OBJ) : $(SRCDIR)%.cpp +$(OBJDIR)%.o : $(SRCDIR)%.cpp @mkdir -p $(dir $@) $(CXX) $(CXXAR) $(VERAR) -c -o$@ $< -$(BLDDIR)test_obj/%$(OBJ) : tests/%.cpp +$(BLDDIR)test_obj/%.o : tests/%.cpp @mkdir -p $(dir $@) $(CXX) $(CXXAR) -Isrc -c -o$@ $< -$(BLDDIR)build_test_obj/%$(OBJ) : build-tests/%.cpp +$(BLDDIR)build_test_obj/%.o : build-tests/%.cpp @mkdir -p $(dir $@) - ! $(CXX) $(CXXAR) -Isrc -c -o$@ $< 2>$(subst $(OBJ),.err_out,$@) - grep -q "$(shell grep ERROR_MUST_CONTAIN $^ | sed -e 's/#define ERROR_MUST_CONTAIN "//' -e 's/"$$//')" $(subst $(OBJ),.err_out,$@) + ! $(CXX) $(CXXAR) -Isrc -c -o$@ $< 2>$(subst .o,.err_out,$@) + grep -q "$(shell grep ERROR_MUST_CONTAIN $^ | sed -e 's/#define ERROR_MUST_CONTAIN "//' -e 's/"$$//')" $(subst .o,.err_out,$@) touch $@ diff --git a/configMakefile b/configMakefile index 4da0498..a842fea 100644 --- a/configMakefile +++ b/configMakefile @@ -23,12 +23,10 @@ ifeq "$(OS)" "Windows_NT" EXE := .exe PIC := - ECHO := /usr/bin/echo OS_LD_LIBS := else EXE := PIC := -fPIC - ECHO := echo OS_LD_LIBS := pthread endif @@ -44,13 +42,6 @@ else LNCXXAR := endif -# TODO: uncomment when there's a first tag -# PICTURA_MEDIOCRITAS_VERSION := "$(patsubst v%,%,$(shell git describe --tags --abbrev=0))" -PICTURA_MEDIOCRITAS_VERSION := "0.1.0" - -OBJ := .o -ARCH := .a -AR := ar CXXAR := -g -MD -O3 -fomit-frame-pointer -march=native -std=c++20 -pedantic -Wall -Wextra -Wno-missing-field-initializers -pipe $(INCCXXAR) $(PIC) OUTDIR := out/ From dd57674a4a9d8cdf0169cac1e6fecec15a8476c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Sat, 23 Mar 2024 22:05:58 +0100 Subject: [PATCH 29/29] av_log_set_callback() takes NULL to mean no-log as well --- src/parser/ffmpeg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser/ffmpeg.cpp b/src/parser/ffmpeg.cpp index 32b8f21..8d9c0d1 100644 --- a/src/parser/ffmpeg.cpp +++ b/src/parser/ffmpeg.cpp @@ -56,7 +56,7 @@ bool pictura_mediocritas::ffmpeg_parser::send_packet(AVPacket * pkt) noexcept { return false; case AVERROR_INVALIDDATA: - av_log_set_callback([](void*, int, const char*, va_list) {}); + av_log_set_callback(nullptr); return false; default: