├── .clang-format ├── .github └── workflows │ └── Build.yml ├── .gitignore ├── CITATION.cff ├── CMakeLists.txt ├── COPYING ├── Dockerfile ├── docs ├── api.md ├── build.md ├── cli.md └── index.md ├── readme.rst ├── source ├── apps │ ├── common │ │ ├── EnumMapper.h │ │ ├── common.cpp │ │ ├── common.h │ │ ├── getopt │ │ │ ├── LGPL.txt │ │ │ ├── getopt.c │ │ │ └── getopt.h │ │ ├── input │ │ │ ├── IInputFile.cpp │ │ │ ├── IInputFile.h │ │ │ ├── Y4MInput.cpp │ │ │ ├── Y4MInput.h │ │ │ ├── YUVInput.cpp │ │ │ └── YUVInput.h │ │ └── stats │ │ │ ├── YUViewStatsFile.cpp │ │ │ └── YUViewStatsFile.h │ ├── vca │ │ ├── CMakeLists.txt │ │ ├── vca.cpp │ │ └── vcacli.h │ └── vcaPerformanceTest │ │ ├── CMakeLists.txt │ │ ├── vcaPerformanceTest.cpp │ │ └── vcacli.h └── lib │ ├── CMakeLists.txt │ ├── analyzer │ ├── Analyzer.cpp │ ├── Analyzer.h │ ├── CMakeLists.txt │ ├── DCTTransform.cpp │ ├── DCTTransform.h │ ├── DCTTransformsNative.cpp │ ├── DCTTransformsNative.h │ ├── EnergyCalculation.cpp │ ├── EnergyCalculation.h │ ├── EntropyCalculation.cpp │ ├── EntropyCalculation.h │ ├── EntropyNative.cpp │ ├── EntropyNative.h │ ├── MultiThreadQueue.cpp │ ├── MultiThreadQueue.h │ ├── ProcessingThread.cpp │ ├── ProcessingThread.h │ ├── ShotDetection.cpp │ ├── ShotDetection.h │ ├── common │ │ ├── EnumMapper.h │ │ └── common.h │ └── simd │ │ ├── CMakeLists.txt │ │ ├── README.txt │ │ ├── const-a.asm │ │ ├── cpu-a.asm │ │ ├── cpu.cpp │ │ ├── cpu.h │ │ ├── dct-ssse3.cpp │ │ ├── dct-ssse3.h │ │ ├── dct8.asm │ │ ├── dct8.h │ │ ├── entropy.cpp │ │ ├── entropy.h │ │ ├── noAsmImpl10bit.cpp │ │ ├── noAsmImpl12bit.cpp │ │ ├── noAsmImpl8bit.cpp │ │ ├── x86inc.asm │ │ └── x86util.asm │ ├── test │ ├── CMakeLists.txt │ ├── DCTTestForwardBackwards.cpp │ ├── DCTTestImplementationsIdenticalOutput.cpp │ ├── InverseDCTNative.cpp │ ├── InverseDCTNative.h │ └── common │ │ ├── functions.cpp │ │ └── functions.h │ ├── vcaLib.cpp │ ├── vcaLib.h │ └── vca_config.h.in └── videos └── .gitkeep /.clang-format: -------------------------------------------------------------------------------- 1 | # .clang-format for Qt Creator 2 | # 3 | # This is for clang-format >= 5.0. 4 | # 5 | # The configuration below follows the Qt Creator Coding Rules [1] as closely as 6 | # possible. For documentation of the options, see [2]. 7 | # 8 | # Use ../../tests/manual/clang-format-for-qtc/test.cpp for documenting problems 9 | # or testing changes. 10 | # 11 | # [1] https://doc-snapshots.qt.io/qtcreator-extending/coding-style.html 12 | # [2] https://clang.llvm.org/docs/ClangFormatStyleOptions.html 13 | # 14 | --- 15 | Language: Cpp 16 | AccessModifierOffset: -4 17 | AlignAfterOpenBracket: Align 18 | AlignConsecutiveAssignments: true 19 | AlignConsecutiveDeclarations: false 20 | AlignEscapedNewlines: Left 21 | AlignOperands: true 22 | AlignTrailingComments: true 23 | AllowAllParametersOfDeclarationOnNextLine: false 24 | AllowShortBlocksOnASingleLine: false 25 | AllowShortCaseLabelsOnASingleLine: false 26 | AllowShortFunctionsOnASingleLine: Empty 27 | AllowShortIfStatementsOnASingleLine: false 28 | AllowShortLoopsOnASingleLine: false 29 | AlwaysBreakAfterReturnType: None 30 | AlwaysBreakBeforeMultilineStrings: false 31 | AlwaysBreakTemplateDeclarations: true 32 | BinPackArguments: false 33 | BinPackParameters: false 34 | BraceWrapping: 35 | AfterClass: true 36 | AfterControlStatement: true 37 | AfterEnum: true 38 | AfterFunction: true 39 | AfterNamespace: false 40 | AfterObjCDeclaration: false 41 | AfterStruct: true 42 | AfterUnion: true 43 | BeforeCatch: true 44 | BeforeElse: true 45 | IndentBraces: false 46 | SplitEmptyFunction: false 47 | SplitEmptyRecord: false 48 | SplitEmptyNamespace: false 49 | BreakBeforeBinaryOperators: All 50 | BreakBeforeBraces: Custom 51 | BreakBeforeInheritanceComma: false 52 | BreakBeforeTernaryOperators: true 53 | BreakConstructorInitializersBeforeComma: false 54 | BreakConstructorInitializers: BeforeComma 55 | BreakAfterJavaFieldAnnotations: false 56 | BreakStringLiterals: true 57 | ColumnLimit: 100 58 | CommentPragmas: '^ IWYU pragma:' 59 | CompactNamespaces: false 60 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 61 | ConstructorInitializerIndentWidth: 4 62 | ContinuationIndentWidth: 4 63 | Cpp11BracedListStyle: true 64 | DerivePointerAlignment: false 65 | DisableFormat: false 66 | ExperimentalAutoDetectBinPacking: false 67 | FixNamespaceComments: true 68 | ForEachMacros: 69 | - forever # avoids { wrapped to next line 70 | - foreach 71 | - Q_FOREACH 72 | - BOOST_FOREACH 73 | IncludeCategories: 74 | - Regex: '^ Create a new analyzer handler, all parameters from vca_param are copied. The returned pointer is then passed to all of the functions pertaining to this analyzer. Since `vca_param` is copied internally, the user may release their copy after allocating the analyzer. Changes made to their copy of the param structure have no affect on the analyzer after it has been allocated. 8 | 9 | - `vca_result vca_analyzer_push(vca_analyzer *enc, vca_frame *frame)` 10 | 11 | > Push a frame to the analyzer and start the analysis. Note that only the pointers will be copied but no ownership of the memory is transferred to the library. The caller must make sure that the pointers are valid until the frame was analyzed. Once a results for a frame was pulled the library will not use pointers anymore. This may block until there is a slot available to work on. The number of frames that will be processed in parallel can be set using nrFrameThreads. 12 | 13 | - `bool vca_result_available(vca_analyzer *enc)` 14 | 15 | > Check if a result is available to pull. 16 | 17 | - `vca_result vca_analyzer_pull_frame_result(vca_analyzer *enc, vca_frame_results *result)` 18 | 19 | > Pull a result from the analyzer. This may block until a result is available. Use `vca_result_available()` if you want to only check if a result is ready. 20 | 21 | - `void vca_analyzer_close(vca_analyzer *enc)` 22 | 23 | > Finally, the analyzer must be closed in order to free all of its resources. An analyzer that has been flushed cannot be restarted and reused. Once `vca_analyzer_close()` has been called, the analyzer handle must be discarded. 24 | -------------------------------------------------------------------------------- /docs/build.md: -------------------------------------------------------------------------------- 1 | # Building instructions 2 | 3 | The software is tested mostly in Linux and Windows OS. It requires some pre-requisite software to be installed before compiling. The steps to build the project in Linux and Windows are explained below. 4 | 5 | ## Prerequisites 6 | 7 | 1. [CMake](https://cmake.org) version 3.13 or higher. 8 | 2. [Git](https://git-scm.com/). 9 | 3. C++ compiler with C++11 support 10 | 4. [NASM](https://nasm.us/) assembly compiler (for x86 SIMD support) 11 | 12 | The following C++11 compilers have been known to work: 13 | 14 | * Visual Studio 2015 or later 15 | * GCC 4.8 or later 16 | * Clang 3.3 or later 17 | 18 | ## Execute Build 19 | 20 | The following commands will checkout the project source code and create a directory called 'build' where the compiler output will be placed. CMake is then used for generating build files and compiling the VCA binaries. 21 | 22 | $ git clone https://github.com/cd-athena/VCA.git 23 | $ cd VCA 24 | $ mkdir build 25 | $ cd build 26 | $ cmake ../ 27 | $ cmake --build . 28 | 29 | This will create VCA binaries in the VCA/build/source/apps/ folder. 30 | 31 | ## Docker Build 32 | 33 | VCA can also be used via a Docker container that is build with the `Dockerfile` found in the root directory. 34 | 35 | Simply execute the command `docker build --tag vca .` to build the container. 36 | 37 | Note that the videos that should be analysed already need to be inside the `videos` directory, since they will be copied into the container. 38 | 39 | Afterwards, enter the Docker container in an interactive session via `docker run --rm -it vca`. 40 | The VCA binary and the videos that are to be analysed are found in the directory that is opened with the before mentioned command. 41 | -------------------------------------------------------------------------------- /docs/cli.md: -------------------------------------------------------------------------------- 1 | # Command Line Options 2 | 3 | ## General 4 | 5 | - `--help, -h` 6 | 7 | Display Help Text 8 | 9 | - `--version, -v` 10 | 11 | Display version details 12 | 13 | ## Logging/Statistic Options 14 | 15 | - `--complexity-csv ` 16 | 17 | Write the spatial (E) and temporal complexity (h), epsilon, brightness (L) statistics to a Comma Separated Values log file. Creates the file if it doesn't already exist. The following statistics are available: 18 | 19 | - `POC` Picture Order Count - The display order of the frames 20 | - `E` Spatial complexity of the frame 21 | - `h` Temporal complexity of the frame 22 | - `epsilon` Gradient of the temporal complexity of the frame 23 | - `L` Brightness of the frame 24 | 25 | Unless option:`--no-chroma` is used, the following chroma statistics are also available: 26 | 27 | - `avgU` Average U chroma component of the frame 28 | - `energyU` Average U chroma texture of the frame 29 | - `avgV` Average V chroma component of the frame 30 | - `energyV` Average V chroma texture of the frame 31 | 32 | - `--shot-csv < filename>` 33 | 34 | Write the shot id, the first POC of every shot to a Comma Separated Values log file. Creates the file if it doesn't already exist. 35 | 36 | - `--yuview-stats ` 37 | 38 | Write the per block results (L, E, h) to a stats file that can be visualized using YUView. 39 | 40 | ## Performance Options 41 | 42 | - `--no-lowpass` 43 | 44 | Disable lowpass DCT analysis (which is enabled by default). 45 | 46 | - `--no-chroma` 47 | 48 | Disable analysis of chroma planes (which is enabled by default). 49 | 50 | - `--no-simd` 51 | 52 | VCA will use all detected CPU SIMD architectures by default. This will disable that detection. 53 | 54 | - `--no-dctenergy` 55 | 56 | Disable analysis of DCT-energy-based features (which is enabled by default). 57 | 58 | - `--no-entropy` 59 | 60 | Disable analysis of entropy-based features (which is enabled by default). 61 | 62 | - `--no-edgedensity` 63 | 64 | Disable analysis of edge density (which is enabled by default). 65 | 66 | - `--threads ` 67 | 68 | Specify the number of threads to use. Default: 0 (autodetect). 69 | 70 | ## Input/Output 71 | 72 | - `--input ` 73 | 74 | Input filename. Raw YUV or Y4M supported. Use `stdin` for stdin. For example piping input from ffmpeg works like this: 75 | 76 | ``` 77 | ffmpeg.exe -i Sintel.2010.1080p.mkv -f yuv4mpegpipe - | vca.exe --y4m --input stdin 78 | ``` 79 | 80 | - `--y4m` 81 | 82 | Parse input stream as YUV4MPEG2 regardless of file extension. Primarily intended for use with stdin. This option is implied if the input filename has a ".y4m" extension 83 | 84 | - `--input-depth ` 85 | 86 | Bit-depth of input file or stream. Any value between 8 and 16. Default is 8. For Y4M files, this is read from the Y4M header. 87 | 88 | - `--input-res ` 89 | 90 | Source picture size [w x h]. For Y4M files, this is read from the Y4M header. 91 | 92 | - `--input-csp ` 93 | 94 | Chroma Subsampling. 4:0:0(monochrome), 4:2:0, 4:2:2, and 4:4:4 are supported. For Y4M files, this is read from the Y4M header. 95 | 96 | - `--input-fps ` 97 | 98 | The framerate of the input. For Y4M files, this is read from the Y4M header. 99 | 100 | - `--skip ` 101 | 102 | Number of frames to skip at start of input file. Default 0. 103 | 104 | - `--frames, -f ` 105 | 106 | Number of frames of input sequence to be analyzed. Default 0 (all). 107 | 108 | ## Analyzer Configuration 109 | 110 | - `--block-size <8/16/32>` 111 | 112 | Size of the non-overlapping blocks used to determine the E, h features. Default: 32. 113 | 114 | - `--min-epsthresh ` 115 | 116 | Minimum threshold of epsilon for shot detection. 117 | 118 | - `--max-epsthresh ` 119 | 120 | Maximum threshold of epsilon for shot detection. 121 | 122 | - `--max-sadthresh ` 123 | 124 | Maximum threshold of h for shot detection. 125 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Index 2 | 3 | ## Introduction 4 | 5 | For online prediction in live streaming applications, selecting low-complexity features is critical to ensure low-latency video streaming without disruptions. For each frame/ video/ video segment, two features, i.e., the average texture energy and the average gradient of the texture energy are determined. A DCT-based energy function is introduced to determine the block-wise texture of each frame. The spatial and temporal features of the video/ video segment is derived from the DCT-based energy function. The Video Complexity Analyzer (VCA) project is launched in 2022, aiming to provide the most efficient, highest performance spatial and temporal complexity prediction of each frame/ video/ video segment which can be used for a variety of applications like shot/scene detection, online per-title encoding. 6 | 7 | ## About VCA 8 | 9 | The primary objective of VCA is to become the best spatial and temporal complexity predictor for every video/ video segment which aids in predicting encoding parameters for applications like online per-title encoding. VCA is available as an open source library, published under the GPLv3 license. VCA leverages x86 SIMD and multi-threading optimizations for effective performance. 10 | While VCA is primarily designed as a video complexity analyzer library, a command-line executable is provided to facilitate testing and development. We expect VCA to be utilized in many leading video encoding solutions in the coming years. 11 | 12 | - [How to build?](build.md) 13 | - [CLI options](cli.md) 14 | - [API functions](api.md) 15 | 16 | ![ATHENA](https://athena.wp.itec.aau.at/wp-content/uploads/sites/12/2020/02/athena-logo-notagline-300x51.png) 17 | -------------------------------------------------------------------------------- /readme.rst: -------------------------------------------------------------------------------- 1 | ================= 2 | VCA 3 | ================= 4 | 5 | | **Read:** | `Online documentation `_ 6 | | **Interact:** | `Report an issue `_ 7 | 8 | `VCA `_ is an open source video complexity analyzer. See the online documentation for instructions for building the source. 9 | The primary objective of VCA is to become the best spatial and temporal complexity predictor for every frame/ video segment/ video which aids in predicting encoding parameters for applications like online per-title encoding. 10 | 11 | VCA is available as an open source library, published under the GPLv3 license. 12 | 13 | Please use "Cite this repository" to extract proper citation options for this repository or use the following: 14 | 15 | 1. Vignesh V Menon, Christian Feldmann, Klaus Schoeffmann, Mohammad Ghanbari, and Christian Timmerer. 2023. Green Video Complexity Analysis for Efficient Encoding in Adaptive Video Streaming. In Proceedings of the First International ACM Green Multimedia Systems Workshop (GMSys 2023). Association for Computing Machinery, New York, NY, USA, 259–264. `https://doi.org/10.1145/3593908.3593942 `_ 16 | 17 | 2. Vignesh V Menon, Christian Feldmann, Hadi Amirpour, Mohammad Ghanbari, and Christian Timmerer. 2022. VCA: video complexity analyzer. In Proceedings of the 13th ACM Multimedia Systems Conference (MMSys '22). Association for Computing Machinery, New York, NY, USA, 259–264. `https://doi.org/10.1145/3524273.3532896 `_ 18 | -------------------------------------------------------------------------------- /source/apps/common/EnumMapper.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | /* This class implement mapping of "enum class" values to and from names (string). 28 | */ 29 | template class EnumMapper 30 | { 31 | public: 32 | struct Entry 33 | { 34 | Entry(T value, std::string name) : value(value), name(name) {} 35 | Entry(T value, std::string name, std::string text) : value(value), name(name), text(text) {} 36 | T value; 37 | std::string name; 38 | std::string text; 39 | }; 40 | 41 | using EntryVector = std::vector; 42 | 43 | EnumMapper() = default; 44 | EnumMapper(const EntryVector &entryVector) : entryVector(entryVector){}; 45 | 46 | std::optional getValue(std::string name, bool isText = false) const 47 | { 48 | for (const auto &entry : this->entryVector) 49 | if ((!isText && entry.name == name) || (isText && entry.text == name)) 50 | return entry.value; 51 | return {}; 52 | } 53 | 54 | std::string getName(T value) const 55 | { 56 | for (const auto &entry : this->entryVector) 57 | if (entry.value == value) 58 | return entry.name; 59 | throw std::logic_error( 60 | "The given type T was not registered in the mapper. All possible enums must be mapped."); 61 | } 62 | 63 | std::string getText(T value) const 64 | { 65 | for (const auto &entry : this->entryVector) 66 | if (entry.value == value) 67 | return entry.text; 68 | throw std::logic_error( 69 | "The given type T was not registered in the mapper. All possible enums must be mapped."); 70 | } 71 | 72 | size_t indexOf(T value) const 73 | { 74 | for (size_t i = 0; i < this->entryVector.size(); i++) 75 | if (this->entryVector.at(i).value == value) 76 | return i; 77 | throw std::logic_error( 78 | "The given type T was not registered in the mapper. All possible enums must be mapped."); 79 | } 80 | 81 | std::optional at(size_t index) const 82 | { 83 | if (index >= this->entryVector.size()) 84 | return {}; 85 | return this->entryVector.at(index).value; 86 | } 87 | 88 | std::vector getEnums() const 89 | { 90 | std::vector m; 91 | for (const auto &entry : this->entryVector) 92 | m.push_back(entry.value); 93 | return m; 94 | } 95 | 96 | std::vector getNames() const 97 | { 98 | std::vector l; 99 | for (const auto &entry : this->entryVector) 100 | l.push_back(entry.name); 101 | return l; 102 | } 103 | 104 | std::vector getTextEntries() const 105 | { 106 | std::vector l; 107 | for (const auto &entry : this->entryVector) 108 | l.push_back(entry.text); 109 | return l; 110 | } 111 | 112 | size_t size() const { return this->entryVector.size(); } 113 | 114 | const EntryVector &entries() const { return this->entryVector; } 115 | 116 | private: 117 | EntryVector entryVector; 118 | }; 119 | -------------------------------------------------------------------------------- /source/apps/common/common.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #include "common.h" 20 | 21 | #include 22 | #include 23 | 24 | namespace vca { 25 | 26 | namespace { 27 | 28 | struct vca_cli_csp 29 | { 30 | int planes; 31 | int width[3]; 32 | int height[3]; 33 | }; 34 | 35 | static const std::map vca_cli_csps 36 | = {{vca_colorSpace::YUV400, {1, {0, 0, 0}, {0, 0, 0}}}, 37 | {vca_colorSpace::YUV420, {3, {0, 1, 1}, {0, 1, 1}}}, 38 | {vca_colorSpace::YUV422, {3, {0, 1, 1}, {0, 0, 0}}}, 39 | {vca_colorSpace::YUV444, {3, {0, 0, 0}, {0, 0, 0}}}}; 40 | 41 | } // namespace 42 | 43 | FrameWithData::FrameWithData(const vca_frame_info &frameInfo) 44 | { 45 | this->vcaFrame.info = frameInfo; 46 | 47 | const auto colorspace = frameInfo.colorspace; 48 | auto pixelbytes = frameInfo.bitDepth > 8 ? 2u : 1u; 49 | 50 | size_t planeSizeBytes[3] = {0, 0, 0}; 51 | for (int i = 0; i < vca_cli_csps.at(colorspace).planes; i++) 52 | { 53 | uint32_t w = frameInfo.width >> vca_cli_csps.at(colorspace).width[i]; 54 | uint32_t h = frameInfo.height >> vca_cli_csps.at(colorspace).height[i]; 55 | planeSizeBytes[i] = w * h * pixelbytes; 56 | } 57 | 58 | const auto frameSizeBytes = planeSizeBytes[0] + planeSizeBytes[1] + planeSizeBytes[2]; 59 | if (this->data.size() < frameSizeBytes) 60 | this->data.resize(frameSizeBytes); 61 | 62 | this->vcaFrame.planes[0] = this->data.data(); 63 | this->vcaFrame.stride[0] = frameInfo.width * pixelbytes; 64 | this->vcaFrame.height[0] = frameInfo.height; 65 | 66 | if (vca_cli_csps.at(colorspace).planes > 1) 67 | { 68 | uint32_t widthChroma = frameInfo.width >> vca_cli_csps.at(colorspace).width[1]; 69 | uint32_t heightChroma = frameInfo.height >> vca_cli_csps.at(colorspace).height[1]; 70 | 71 | this->vcaFrame.planes[1] = this->data.data() + planeSizeBytes[0]; 72 | this->vcaFrame.planes[2] = this->data.data() + planeSizeBytes[0] + planeSizeBytes[1]; 73 | this->vcaFrame.stride[1] = widthChroma * pixelbytes; 74 | this->vcaFrame.stride[2] = widthChroma * pixelbytes; 75 | this->vcaFrame.height[1] = heightChroma; 76 | this->vcaFrame.height[2] = heightChroma; 77 | } 78 | } 79 | 80 | void vca_log(LogLevel level, std::string error) 81 | { 82 | static LogLevel appLogLevel = level; 83 | static const auto logLevelToInt = std::map( 84 | {{LogLevel::Debug, 0}, {LogLevel::Info, 1}, {LogLevel::Warning, 2}, {LogLevel::Error, 3}}); 85 | static const auto logLevelName = std::map( 86 | {{LogLevel::Debug, "[Debug] "}, 87 | {LogLevel::Info, "[Info] "}, 88 | {LogLevel::Warning, "[Warning] "}, 89 | {LogLevel::Error, "[Error] "}}); 90 | 91 | if (logLevelToInt.at(level) >= logLevelToInt.at(appLogLevel)) 92 | std::cout << logLevelName.at(level) << error << std::endl; 93 | } 94 | 95 | size_t calculateFrameBytesInInput(const vca_frame_info &frameInfo) 96 | { 97 | size_t framesizeBytes = 0; 98 | const auto colorspace = frameInfo.colorspace; 99 | auto pixelbytes = frameInfo.bitDepth > 8 ? 2u : 1u; 100 | for (int i = 0; i < vca_cli_csps.at(colorspace).planes; i++) 101 | { 102 | uint32_t w = frameInfo.width >> vca_cli_csps.at(colorspace).width[i]; 103 | uint32_t h = frameInfo.height >> vca_cli_csps.at(colorspace).height[i]; 104 | framesizeBytes += w * h * pixelbytes; 105 | } 106 | return framesizeBytes; 107 | } 108 | 109 | } // namespace vca 110 | -------------------------------------------------------------------------------- /source/apps/common/common.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | #pragma once 28 | 29 | namespace vca { 30 | 31 | class FrameWithData 32 | { 33 | public: 34 | FrameWithData() = delete; 35 | FrameWithData(const vca_frame_info &frameInfo); 36 | ~FrameWithData() = default; 37 | 38 | uint8_t *getData() const 39 | { 40 | return (uint8_t *) (this->data.data()); 41 | } 42 | size_t getFrameSize() const 43 | { 44 | return this->data.size(); 45 | } 46 | vca_frame *getFrame() 47 | { 48 | return &this->vcaFrame; 49 | } 50 | 51 | private: 52 | std::vector data; 53 | vca_frame vcaFrame; 54 | }; 55 | 56 | const auto vca_colorSpaceMapper = EnumMapper({{vca_colorSpace::YUV400, "4:0:0"}, 57 | {vca_colorSpace::YUV420, "4:2:0"}, 58 | {vca_colorSpace::YUV422, "4:2:2"}, 59 | {vca_colorSpace::YUV444, "4:4:4"}}); 60 | 61 | void vca_log(LogLevel level, std::string error); 62 | size_t calculateFrameBytesInInput(const vca_frame_info &frameInfo); 63 | 64 | } // namespace vca 65 | -------------------------------------------------------------------------------- /source/apps/common/getopt/getopt.h: -------------------------------------------------------------------------------- 1 | /* Declarations for getopt. 2 | Copyright (C) 1989-1994, 1996-1999, 2001 Free Software Foundation, Inc. 3 | This file is part of the GNU C Library. 4 | 5 | The GNU C Library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | The GNU C Library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with the GNU C Library; if not, write to the Free 17 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 18 | 02111-1307 USA. */ 19 | 20 | #ifndef _GETOPT_H 21 | 22 | #ifndef __need_getopt 23 | # define _GETOPT_H 1 24 | #endif 25 | 26 | #include 27 | 28 | /* If __GNU_LIBRARY__ is not already defined, either we are being used 29 | standalone, or this is the first header included in the source file. 30 | If we are being used with glibc, we need to include , but 31 | that does not exist if we are standalone. So: if __GNU_LIBRARY__ is 32 | not defined, include , which will pull in for us 33 | if it's from glibc. (Why ctype.h? It's guaranteed to exist and it 34 | doesn't flood the namespace with stuff the way some other headers do.) */ 35 | #if !defined __GNU_LIBRARY__ 36 | # include 37 | #endif 38 | 39 | #ifdef __cplusplus 40 | extern "C" { 41 | #endif 42 | 43 | /* For communication from `getopt' to the caller. 44 | When `getopt' finds an option that takes an argument, 45 | the argument value is returned here. 46 | Also, when `ordering' is RETURN_IN_ORDER, 47 | each non-option ARGV-element is returned here. */ 48 | 49 | extern char *optarg; 50 | 51 | /* Index in ARGV of the next element to be scanned. 52 | This is used for communication to and from the caller 53 | and for communication between successive calls to `getopt'. 54 | 55 | On entry to `getopt', zero means this is the first call; initialize. 56 | 57 | When `getopt' returns -1, this is the index of the first of the 58 | non-option elements that the caller should itself scan. 59 | 60 | Otherwise, `optind' communicates from one call to the next 61 | how much of ARGV has been scanned so far. */ 62 | 63 | extern int optind; 64 | 65 | /* Callers store zero here to inhibit the error message `getopt' prints 66 | for unrecognized options. */ 67 | 68 | extern int opterr; 69 | 70 | /* Set to an option character which was unrecognized. */ 71 | 72 | extern int optopt; 73 | 74 | #ifndef __need_getopt 75 | /* Describe the long-named options requested by the application. 76 | The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector 77 | of `struct option' terminated by an element containing a name which is 78 | zero. 79 | 80 | The field `has_arg' is: 81 | no_argument (or 0) if the option does not take an argument, 82 | required_argument (or 1) if the option requires an argument, 83 | optional_argument (or 2) if the option takes an optional argument. 84 | 85 | If the field `flag' is not NULL, it points to a variable that is set 86 | to the value given in the field `val' when the option is found, but 87 | left unchanged if the option is not found. 88 | 89 | To have a long-named option do something other than set an `int' to 90 | a compiled-in constant, such as set a value from `optarg', set the 91 | option's `flag' field to zero and its `val' field to a nonzero 92 | value (the equivalent single-letter option character, if there is 93 | one). For long options that have a zero `flag' field, `getopt' 94 | returns the contents of the `val' field. */ 95 | 96 | struct option 97 | { 98 | # if (defined __STDC__ && __STDC__) || defined __cplusplus 99 | const char *name; 100 | # else 101 | char *name; 102 | # endif 103 | /* has_arg can't be an enum because some compilers complain about 104 | type mismatches in all the code that assumes it is an int. */ 105 | int has_arg; 106 | int32_t *flag; 107 | int val; 108 | }; 109 | 110 | /* Names for the values of the `has_arg' field of `struct option'. */ 111 | 112 | # define no_argument 0 113 | # define required_argument 1 114 | # define optional_argument 2 115 | #endif /* need getopt */ 116 | 117 | 118 | /* Get definitions and prototypes for functions to process the 119 | arguments in ARGV (ARGC of them, minus the program name) for 120 | options given in OPTS. 121 | 122 | Return the option character from OPTS just read. Return -1 when 123 | there are no more options. For unrecognized options, or options 124 | missing arguments, `optopt' is set to the option letter, and '?' is 125 | returned. 126 | 127 | The OPTS string is a list of characters which are recognized option 128 | letters, optionally followed by colons, specifying that that letter 129 | takes an argument, to be placed in `optarg'. 130 | 131 | If a letter in OPTS is followed by two colons, its argument is 132 | optional. This behavior is specific to the GNU `getopt'. 133 | 134 | The argument `--' causes premature termination of argument 135 | scanning, explicitly telling `getopt' that there are no more 136 | options. 137 | 138 | If OPTS begins with `--', then non-option arguments are treated as 139 | arguments to the option '\0'. This behavior is specific to the GNU 140 | `getopt'. */ 141 | 142 | #if (defined __STDC__ && __STDC__) || defined __cplusplus 143 | # ifdef __GNU_LIBRARY__ 144 | /* Many other libraries have conflicting prototypes for getopt, with 145 | differences in the consts, in stdlib.h. To avoid compilation 146 | errors, only prototype getopt for the GNU C library. */ 147 | extern int getopt (int argc, char *const *argv, const char *shortopts); 148 | # else /* not __GNU_LIBRARY__ */ 149 | extern int getopt (); 150 | # endif /* __GNU_LIBRARY__ */ 151 | 152 | # ifndef __need_getopt 153 | extern int getopt_long (int argc, char *const *argv, const char *shortopts, 154 | const struct option *longopts, int32_t *longind); 155 | extern int getopt_long_only (int argc, char *const *argv, 156 | const char *shortopts, 157 | const struct option *longopts, int32_t *longind); 158 | 159 | /* Internal only. Users should not call this directly. */ 160 | extern int _getopt_internal (int argc, char *const *argv, 161 | const char *shortopts, 162 | const struct option *longopts, int32_t *longind, 163 | int longonly); 164 | # endif 165 | #else /* not __STDC__ */ 166 | extern int getopt (); 167 | # ifndef __need_getopt 168 | extern int getopt_long (); 169 | extern int getopt_long_only (); 170 | 171 | extern int _getopt_internal (); 172 | # endif 173 | #endif /* __STDC__ */ 174 | 175 | #ifdef __cplusplus 176 | } 177 | #endif 178 | 179 | /* Make sure we later can get all the definitions and declarations. */ 180 | #undef __need_getopt 181 | 182 | #endif /* getopt.h */ 183 | -------------------------------------------------------------------------------- /source/apps/common/input/IInputFile.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2022 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Steve Borho 5 | * Christian Feldmann 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. 19 | *****************************************************************************/ 20 | 21 | #include "IInputFile.h" 22 | 23 | #include 24 | 25 | #if _WIN32 26 | #include 27 | #include 28 | 29 | #if defined(_MSC_VER) 30 | #pragma warning(disable : 4996) // POSIX setmode and fileno deprecated 31 | #endif 32 | #endif 33 | 34 | namespace vca { 35 | 36 | bool IInputFile::isEof() const 37 | { 38 | return !this->input || this->input->eof(); 39 | } 40 | 41 | bool IInputFile::isFail() const 42 | { 43 | return !this->input || this->input->fail(); 44 | } 45 | 46 | vca_frame_info IInputFile::getFrameInfo() const 47 | { 48 | return this->frameInfo; 49 | } 50 | 51 | bool IInputFile::openInput(std::string &fileName) 52 | { 53 | if (fileName == "stdin") 54 | { 55 | vca_log(LogLevel::Info, "Opening input from pipe"); 56 | this->input = &std::cin; 57 | #if _WIN32 58 | setmode(fileno(stdin), O_BINARY); 59 | #endif 60 | } 61 | else 62 | { 63 | vca_log(LogLevel::Info, "Opening input from file " + fileName); 64 | this->inputFile.open(fileName, std::ios::binary); 65 | this->input = &this->inputFile; 66 | } 67 | 68 | return this->input && this->input->good(); 69 | } 70 | 71 | bool IInputFile::isStdin() const 72 | { 73 | return this->input == &std::cin; 74 | } 75 | 76 | } // namespace vca -------------------------------------------------------------------------------- /source/apps/common/input/IInputFile.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2022 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Steve Borho 5 | * Christian Feldmann 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. 19 | *****************************************************************************/ 20 | 21 | #pragma once 22 | 23 | #include 24 | #include 25 | 26 | #define MIN_FRAME_WIDTH 64 27 | #define MAX_FRAME_WIDTH 16384 28 | #define MIN_FRAME_HEIGHT 64 29 | #define MAX_FRAME_HEIGHT 8640 30 | 31 | #include 32 | 33 | namespace vca { 34 | 35 | class IInputFile 36 | { 37 | protected: 38 | vca_frame_info frameInfo{}; 39 | unsigned frameCount{}; 40 | 41 | std::istream *input{}; 42 | std::ifstream inputFile; 43 | 44 | public: 45 | virtual ~IInputFile() {} 46 | 47 | virtual bool readFrame(FrameWithData &frame) = 0; 48 | 49 | bool isEof() const; 50 | bool isFail() const; 51 | 52 | vca_frame_info getFrameInfo() const; 53 | virtual double getFPS() const = 0; 54 | 55 | bool openInput(std::string &fileName); 56 | bool isStdin() const; 57 | }; 58 | 59 | } // namespace vca 60 | -------------------------------------------------------------------------------- /source/apps/common/input/Y4MInput.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2022 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Steve Borho 5 | * Christian Feldmann 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. 19 | *****************************************************************************/ 20 | 21 | #include "Y4MInput.h" 22 | 23 | #ifdef FILESYSTEM_EXPERIMENTAL 24 | #include 25 | namespace filesystem = std::experimental::filesystem; 26 | #else 27 | #include 28 | namespace filesystem = std::filesystem; 29 | #endif 30 | 31 | #include 32 | #include 33 | namespace vca { 34 | Y4MInput::Y4MInput(std::string &fileName) 35 | { 36 | if (!this->openInput(fileName)) 37 | { 38 | vca_log(LogLevel::Error, "Error opening input " + fileName); 39 | return; 40 | } 41 | 42 | if (!this->parseHeader()) 43 | { 44 | vca_log(LogLevel::Error, "Error parsing Y4M header"); 45 | return; 46 | } 47 | 48 | if (!this->isStdin()) 49 | { 50 | const auto assumedHeaderSize = 6u; 51 | auto estFrameSize = calculateFrameBytesInInput(this->frameInfo) + assumedHeaderSize; 52 | auto fileSize = filesystem::file_size(fileName); 53 | this->frameCount = unsigned(fileSize / estFrameSize); 54 | vca_log(LogLevel::Info, "Detected " + std::to_string(this->frameCount) + " frames in input"); 55 | } 56 | } 57 | 58 | bool Y4MInput::parseHeader() 59 | { 60 | auto it = std::istreambuf_iterator(*this->input); 61 | 62 | auto getNextHeaderField = [&it]() { 63 | if (*it == '\n') 64 | return std::string(); 65 | if (*it == ' ') 66 | it++; 67 | std::string str; 68 | while (it != std::istreambuf_iterator() && *it != ' ' && *it != '\n') 69 | str.push_back(*it++); 70 | return str; 71 | }; 72 | 73 | if (getNextHeaderField() != "YUV4MPEG2") 74 | { 75 | vca_log(LogLevel::Error, "Y4M file must start with YUV4MPEG2"); 76 | return false; 77 | } 78 | 79 | while (true) 80 | { 81 | auto field = getNextHeaderField(); 82 | if (field.empty()) 83 | break; 84 | 85 | auto parameterIndicator = field[0]; 86 | if (parameterIndicator == 'W') 87 | { 88 | auto val = std::stoul(field.substr(1)); 89 | if (val < MIN_FRAME_WIDTH || val > MAX_FRAME_WIDTH) 90 | { 91 | vca_log(LogLevel::Error, "Invalid width value: " + field); 92 | return false; 93 | } 94 | this->frameInfo.width = val; 95 | vca_log(LogLevel::Info, "Y4M read hidth " + std::to_string(val)); 96 | } 97 | else if (parameterIndicator == 'H') 98 | { 99 | auto val = std::stoul(field.substr(1)); 100 | if (val < MIN_FRAME_HEIGHT || val > MAX_FRAME_HEIGHT) 101 | { 102 | vca_log(LogLevel::Error, "Invalid height value: " + field); 103 | return false; 104 | } 105 | this->frameInfo.height = val; 106 | vca_log(LogLevel::Info, "Y4M read height " + std::to_string(val)); 107 | } 108 | else if (parameterIndicator == 'C') 109 | { 110 | auto subsamplingIndicator = field.substr(1, 3); 111 | if (subsamplingIndicator == "420") 112 | this->frameInfo.colorspace = vca_colorSpace::YUV420; 113 | else if (subsamplingIndicator == "422") 114 | this->frameInfo.colorspace = vca_colorSpace::YUV422; 115 | else if (subsamplingIndicator == "444") 116 | this->frameInfo.colorspace = vca_colorSpace::YUV444; 117 | else 118 | { 119 | vca_log(LogLevel::Info, 120 | "Y4M invalid colorspace indicator (" + subsamplingIndicator 121 | + "). Assuming 4:2:0."); 122 | this->frameInfo.colorspace = vca_colorSpace::YUV420; 123 | } 124 | 125 | if (field.size() > 3) 126 | { 127 | auto additionalPart = field.substr(4); 128 | if (additionalPart == "p10") 129 | this->frameInfo.bitDepth = 10; 130 | else if (additionalPart == "p12") 131 | this->frameInfo.bitDepth = 12; 132 | else if (additionalPart == "p14") 133 | this->frameInfo.bitDepth = 14; 134 | else if (additionalPart == "p16") 135 | this->frameInfo.bitDepth = 16; 136 | else 137 | { 138 | vca_log(LogLevel::Info, 139 | "Y4M invalid additional part in colorspace indicator (" + additionalPart 140 | + "). Assuming 8 bit."); 141 | this->frameInfo.bitDepth = 8; 142 | } 143 | } 144 | 145 | vca_log(LogLevel::Info, 146 | "Y4M Detected colorspace " 147 | + vca_colorSpaceMapper.getName(this->frameInfo.colorspace) 148 | + " with bit depth " + std::to_string(this->frameInfo.bitDepth)); 149 | } 150 | else if (parameterIndicator == 'F') 151 | { 152 | auto colonPos = field.find(':'); 153 | if (colonPos == std::string::npos) 154 | { 155 | vca_log(LogLevel::Error, "Invalid fps value: " + field); 156 | return false; 157 | } 158 | 159 | try 160 | { 161 | auto num = std::stoi(field.substr(1, colonPos - 1)); 162 | auto den = std::stoi(field.substr(colonPos + 1)); 163 | this->fps = double(num) / double(den); 164 | vca_log(LogLevel::Info, 165 | "Y4M Detected fps " + std::to_string(num) + "/" + std::to_string(den)); 166 | } 167 | catch (...) 168 | { 169 | vca_log(LogLevel::Error, "Invalid fps value: " + field); 170 | return false; 171 | } 172 | } 173 | } 174 | 175 | return true; 176 | } 177 | 178 | bool Y4MInput::readFrame(FrameWithData &frame) 179 | { 180 | char c = 0; 181 | while (this->input->get(c) && c != 'F') 182 | {} 183 | 184 | if (this->input->eof()) 185 | return false; 186 | 187 | if (!this->input->good()) 188 | throw std::runtime_error("Error reading from file"); 189 | 190 | auto getNextChar = [this]() { 191 | char c; 192 | if (!this->input->get(c)) 193 | return char(0); 194 | return c; 195 | }; 196 | 197 | if (getNextChar() != 'R' || getNextChar() != 'A' || getNextChar() != 'M' || getNextChar() != 'E') 198 | throw std::runtime_error("Error reading FRAME tag"); 199 | 200 | while (this->input->get(c) && c != '\n') 201 | {} 202 | 203 | this->input->read((char *) (frame.getData()), frame.getFrameSize()); 204 | 205 | return true; 206 | } 207 | 208 | double Y4MInput::getFPS() const 209 | { 210 | return this->fps; 211 | } 212 | 213 | } // namespace vca 214 | -------------------------------------------------------------------------------- /source/apps/common/input/Y4MInput.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2022 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Steve Borho 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. 18 | *****************************************************************************/ 19 | 20 | #pragma once 21 | 22 | #include "IInputFile.h" 23 | 24 | namespace vca { 25 | 26 | class Y4MInput : public IInputFile 27 | { 28 | protected: 29 | bool parseHeader(); 30 | 31 | double fps{}; 32 | 33 | public: 34 | Y4MInput() = delete; 35 | Y4MInput(std::string &fileName); 36 | ~Y4MInput() = default; 37 | 38 | bool readFrame(FrameWithData &frame) override; 39 | double getFPS() const override; 40 | }; 41 | 42 | } // namespace vca 43 | -------------------------------------------------------------------------------- /source/apps/common/input/YUVInput.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2022 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Steve Borho 5 | * Christian Feldmann 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. 19 | *****************************************************************************/ 20 | 21 | #include "YUVInput.h" 22 | 23 | #ifdef FILESYSTEM_EXPERIMENTAL 24 | #include 25 | namespace filesystem = std::experimental::filesystem; 26 | #else 27 | #include 28 | namespace filesystem = std::filesystem; 29 | #endif 30 | 31 | #include 32 | 33 | namespace vca { 34 | 35 | YUVInput::YUVInput(std::string &fileName, vca_frame_info &openFrameInfo) 36 | { 37 | frameInfo = openFrameInfo; 38 | 39 | if (frameInfo.width == 0 || frameInfo.height == 0 || frameInfo.bitDepth == 0) 40 | { 41 | vca_log(LogLevel::Error, 42 | "yuv: width, height, and bitDepth must be specified to open a raw YUV file"); 43 | return; 44 | } 45 | 46 | if (!this->openInput(fileName)) 47 | { 48 | vca_log(LogLevel::Error, "Error opening input " + fileName); 49 | return; 50 | } 51 | 52 | auto frameSizeBytes = calculateFrameBytesInInput(this->frameInfo); 53 | 54 | if (!this->isStdin()) 55 | { 56 | auto fileSize = filesystem::file_size(fileName); 57 | this->frameCount = unsigned(fileSize / frameSizeBytes); 58 | vca_log(LogLevel::Info, "Detected " + std::to_string(this->frameCount) + " frames in input"); 59 | } 60 | } 61 | 62 | bool YUVInput::readFrame(FrameWithData &frame) 63 | { 64 | if (!this->input->good() || this->input->eof()) 65 | return false; 66 | 67 | this->input->read((char *) (frame.getData()), frame.getFrameSize()); 68 | if (!this->input->good() || this->input->eof()) 69 | return false; 70 | 71 | if (!this->input) 72 | return false; 73 | 74 | return true; 75 | } 76 | 77 | double YUVInput::getFPS() const 78 | { 79 | return 0.0; 80 | } 81 | 82 | } // namespace vca 83 | -------------------------------------------------------------------------------- /source/apps/common/input/YUVInput.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2022 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Steve Borho 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. 18 | *****************************************************************************/ 19 | 20 | #pragma once 21 | 22 | #include "IInputFile.h" 23 | 24 | namespace vca { 25 | 26 | class YUVInput : public IInputFile 27 | { 28 | public: 29 | YUVInput() = delete; 30 | YUVInput(std::string &fileName, vca_frame_info &openFrameInfo); 31 | ~YUVInput() = default; 32 | 33 | bool readFrame(FrameWithData &frame) override; 34 | double getFPS() const override; 35 | }; 36 | 37 | } // namespace vca 38 | -------------------------------------------------------------------------------- /source/apps/common/stats/YUViewStatsFile.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #include "YUViewStatsFile.h" 20 | 21 | #include "common/common.h" 22 | 23 | namespace vca { 24 | 25 | using namespace std::string_literals; 26 | 27 | YUViewStatsFile::YUViewStatsFile(const std::string &filename, 28 | const std::string &inputFilename, 29 | const vca_frame_info &info) 30 | { 31 | this->info = info; 32 | this->file.open(filename); 33 | 34 | vca_log(LogLevel::Info, "Opened YUView csv file " + filename); 35 | 36 | this->file << "%;syntax-version;v1.22\n"s; 37 | this->file << "%;%;Written by VCA for YUView\n"s; 38 | this->file 39 | << "%;%;POC;X-position of the left top pixel in the block;Y-position of the left top pixel in the block;Width of the block;Height of the block; Type-ID;Type specific value\n"s; 40 | this->file << "%;seq-specs;"s << inputFilename << ";layer0;"s << info.width << ";"s 41 | << info.height << ";24\n"s; 42 | 43 | this->file << "%;type;0;BlockBrightness;range\n"s; 44 | this->file << "%;defaultRange;0;300;heat\n"s; 45 | this->file << "%;type;1;BlockEnergy;range\n"s; 46 | this->file << "%;defaultRange;0;10000;heat\n"s; 47 | this->file << "%;type;2;SAD;range\n"s; 48 | this->file << "%;defaultRange;0;3000;heat\n"s; 49 | } 50 | 51 | void YUViewStatsFile::write(const vca_frame_results &results, 52 | unsigned blockSize, 53 | bool enableDCTenergy, 54 | bool enableEntropy) 55 | { 56 | auto widthInBlocks = (info.width + blockSize - 1) / blockSize; 57 | auto heightInBlock = (info.height + blockSize - 1) / blockSize; 58 | 59 | if (enableDCTenergy) 60 | { 61 | if (auto data = results.brightnessPerBlock) 62 | { 63 | for (unsigned y = 0; y < heightInBlock; y++) 64 | for (unsigned x = 0; x < widthInBlocks; x++) 65 | this->file << results.poc << ";" << x * blockSize << ";" << y * blockSize << ";" 66 | << blockSize << ";" << blockSize << ";0;" << *(data++) << "\n"; 67 | } 68 | if (auto data = results.energyPerBlock) 69 | { 70 | for (unsigned y = 0; y < heightInBlock; y++) 71 | for (unsigned x = 0; x < widthInBlocks; x++) 72 | this->file << results.poc << ";" << x * blockSize << ";" << y * blockSize << ";" 73 | << blockSize << ";" << blockSize << ";1;" << *(data++) << "\n"; 74 | } 75 | if (auto data = results.energyDiffPerBlock) 76 | { 77 | for (unsigned y = 0; y < heightInBlock; y++) 78 | for (unsigned x = 0; x < widthInBlocks; x++) 79 | this->file << results.poc << ";" << x * blockSize << ";" << y * blockSize << ";" 80 | << blockSize << ";" << blockSize << ";2;" << *(data++) << "\n"; 81 | } 82 | } 83 | if (enableEntropy) 84 | { 85 | if (auto data = results.entropyPerBlock) 86 | { 87 | for (unsigned y = 0; y < heightInBlock; y++) 88 | for (unsigned x = 0; x < widthInBlocks; x++) 89 | this->file << results.poc << ";" << x * blockSize << ";" << y * blockSize << ";" 90 | << blockSize << ";" << blockSize << ";1;" << *(data++) << "\n"; 91 | } 92 | if (auto data = results.entropyDiffPerBlock) 93 | { 94 | for (unsigned y = 0; y < heightInBlock; y++) 95 | for (unsigned x = 0; x < widthInBlocks; x++) 96 | this->file << results.poc << ";" << x * blockSize << ";" << y * blockSize << ";" 97 | << blockSize << ";" << blockSize << ";2;" << *(data++) << "\n"; 98 | } 99 | } 100 | } 101 | 102 | } // namespace vca -------------------------------------------------------------------------------- /source/apps/common/stats/YUViewStatsFile.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | namespace vca { 28 | 29 | class YUViewStatsFile 30 | { 31 | public: 32 | YUViewStatsFile(const std::string &filename, 33 | const std::string &inputFilename, 34 | const vca_frame_info &info); 35 | ~YUViewStatsFile() = default; 36 | 37 | void write(const vca_frame_results &results, 38 | unsigned blockSize, 39 | bool enableDCTenergy, 40 | bool enableEntropy); 41 | 42 | private: 43 | std::ofstream file; 44 | 45 | vca_frame_info info; 46 | }; 47 | 48 | } // namespace vca -------------------------------------------------------------------------------- /source/apps/vca/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | include(CheckIncludeFiles) 3 | 4 | file(GLOB_RECURSE vca_apps_common_source ../common/*.cpp) 5 | file(GLOB_RECURSE vca_apps_common_header ../common/*.h) 6 | 7 | check_include_files(getopt.h HAVE_GETOPT_H) 8 | if(NOT HAVE_GETOPT_H) 9 | if(MSVC) 10 | set_source_files_properties(../common/getopt/getopt.c PROPERTIES COMPILE_FLAGS "/wd4100 /wd4131 -DHAVE_STRING_H=1") 11 | endif(MSVC) 12 | include_directories(../common/getopt) 13 | set(GETOPT ../common/getopt/getopt.c ../common/getopt/getopt.h) 14 | message(STATUS "Using compatibility getopt") 15 | endif(NOT HAVE_GETOPT_H) 16 | 17 | if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.1) 18 | add_definitions(-DFILESYSTEM_EXPERIMENTAL=1) 19 | message(STATUS "Including filesystem as experimental because of old GCC") 20 | set(EXPERIMENTAL_FILESYSTEM_LINK stdc++fs) 21 | endif() 22 | 23 | include_directories("${CMAKE_SOURCE_DIR}/source") 24 | include_directories("${CMAKE_SOURCE_DIR}/source/apps") 25 | 26 | add_executable(vca vcacli.h vca.cpp ${vca_apps_common_source} ${vca_apps_common_header} ${GETOPT}) 27 | target_link_libraries (vca vcaLib ${EXPERIMENTAL_FILESYSTEM_LINK}) 28 | 29 | install(TARGETS vca RUNTIME DESTINATION bin COMPONENT applications) 30 | -------------------------------------------------------------------------------- /source/apps/vca/vcacli.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2022 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Vignesh V Menon 5 | * Christian Feldmann 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. 19 | *****************************************************************************/ 20 | 21 | #pragma once 22 | 23 | #include 24 | 25 | #include 26 | 27 | static const char short_options[] = "f:h?"; 28 | static const struct option long_options[] = {{"help", no_argument, NULL, 'h'}, 29 | {"no-simd", no_argument, NULL, 0}, 30 | {"no-dctenergy-chroma", no_argument, NULL, 0}, 31 | {"no-entropy-chroma", no_argument, NULL, 0}, 32 | {"no-lowpass", no_argument, NULL, 0}, 33 | {"input", required_argument, NULL, 0}, 34 | {"y4m", no_argument, NULL, 0}, 35 | {"input-depth", required_argument, NULL, 0}, 36 | {"input-res", required_argument, NULL, 0}, 37 | {"input-csp", required_argument, NULL, 0}, 38 | {"input-fps", required_argument, NULL, 0}, 39 | {"skip", required_argument, NULL, 0}, 40 | {"frames", required_argument, NULL, 'f'}, 41 | {"complexity-csv", required_argument, NULL, 0}, 42 | {"segment-size", required_argument, NULL, 0}, 43 | {"segment-feature-csv", required_argument, NULL, 0}, 44 | {"shot-csv", required_argument, NULL, 0}, 45 | {"yuview-stats", required_argument, NULL, 0}, 46 | {"max-epsthresh", required_argument, NULL, 0}, 47 | {"min-epsthresh", required_argument, NULL, 0}, 48 | {"max-sadthresh", required_argument, NULL, 0}, 49 | {"block-size", required_argument, NULL, 0}, 50 | {"threads", required_argument, NULL, 0}, 51 | {"no-dctenergy", no_argument, 0}, 52 | {"no-entropy", no_argument, 0}, 53 | {"no-edgedensity", no_argument, 0}, 54 | {0, 0, 0, 0}, 55 | {0, 0, 0, 0}, 56 | {0, 0, 0, 0}, 57 | {0, 0, 0, 0}, 58 | {0, 0, 0, 0}}; 59 | 60 | static void showHelp() 61 | { 62 | printf("\nSyntax: vca [options] infile\n"); 63 | printf(" infile can be YUV or Y4M\n"); 64 | printf("\nExecutable Options:\n"); 65 | printf("-h/--help Show this help text and exit\n"); 66 | printf("\nInput Options:\n"); 67 | printf(" --input Raw YUV or Y4M input file name. `stdin` for stdin."); 68 | printf(" --y4m Force parsing of input stream as YUV4MPEG2 regardless " 69 | "of file extension\n"); 70 | printf(" --input-res WxH Source picture size [w x h], auto-detected if Y4M\n"); 71 | printf(" --input-depth Bit-depth of input file. Default 8\n"); 72 | printf(" --input-csp Chroma subsampling, auto-detected if Y4M\n"); 73 | printf(" 400 (4:0:0 monochrome)\n"); 74 | printf(" 420 (4:2:0 default)\n"); 75 | printf(" 422 (4:2:2)\n"); 76 | printf(" 444 (4:4:4)\n"); 77 | printf(" --input-fps Input fps, auto-detected if Y4M. Needed for shot " 78 | "detection.\n"); 79 | printf("-f/--frames Maximum number of frames to analyze. Default all\n"); 80 | printf(" --skip Skip N frames in the input before starting the " 81 | "analysis\n"); 82 | printf(" --segment-size Specifies the size of segment (Example: 1 = fps, 2 = " 83 | "2xfps)"); 84 | printf("\nOutput Options:\n"); 85 | printf(" --complexity-csv Comma separated complexity log file\n"); 86 | printf(" --segment-feature-csv Comma separated segment based complexity log " 87 | "file\n"); 88 | printf(" --shot-csv Comma separated shot detection log file.\n"); 89 | printf(" Specify a filename to enable shot-detection.\n"); 90 | printf(" --yuview-stats Write the per block results (energy, sad) to a stats " 91 | "file\n"); 92 | printf(" that can be visualized using YUView.\n"); 93 | printf("\nOperation Options:\n"); 94 | printf(" --no-simd Disable SIMD. Default: Enabled\n"); 95 | printf(" --no-dctenergy-chroma Disable chroma for DCT energy. Default: Enabled\n"); 96 | printf(" --no-entropy-chroma Disable chroma for entropy. Default: Enabled\n"); 97 | printf(" --no-lowpass Disable lowpass DCT kernels. Default: Enabled\n"); 98 | printf(" --max-epsthresh Maximum threshold of epsilon in shot detection\n"); 99 | printf(" --min-epsthresh Minimum threshold of epsilon in shot detection\n"); 100 | printf(" --min-sadthresh Minimum threshold of h in shot detection\n"); 101 | printf(" --block-size Block size for DCT transform. Must be 8, 16 or 32 " 102 | "(Default).\n"); 103 | printf(" --threads Nr of threads to use. (Default: 0 (autodetect))\n"); 104 | printf(" --no-dctenergy Disable DCT energy features. Default: Enabled\n"); 105 | printf(" --no-entropy Disable entropy features. Default: Enabled\n"); 106 | printf(" -no-edgedensity Disable edge density calculation. Default: Enabled\n"); 107 | } 108 | -------------------------------------------------------------------------------- /source/apps/vcaPerformanceTest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | include(CheckIncludeFiles) 3 | 4 | file(GLOB_RECURSE vca_apps_common_source ../common/*.cpp) 5 | file(GLOB_RECURSE vca_apps_common_header ../common/*.h) 6 | 7 | check_include_files(getopt.h HAVE_GETOPT_H) 8 | if(NOT HAVE_GETOPT_H) 9 | if(MSVC) 10 | set_source_files_properties(../common/getopt/getopt.c PROPERTIES COMPILE_FLAGS "/wd4100 /wd4131 -DHAVE_STRING_H=1") 11 | endif(MSVC) 12 | include_directories(../common/getopt) 13 | set(GETOPT ../common/getopt/getopt.c ../common/getopt/getopt.h) 14 | message(STATUS "Using compatibility getopt") 15 | endif(NOT HAVE_GETOPT_H) 16 | 17 | include_directories("${CMAKE_SOURCE_DIR}/source") 18 | include_directories("${CMAKE_SOURCE_DIR}/source/apps") 19 | 20 | add_executable(vcaPerformanceTest vcacli.h vcaPerformanceTest.cpp ${vca_apps_common_source} ${vca_apps_common_header} ${GETOPT}) 21 | target_link_libraries (vcaPerformanceTest vcaLib) 22 | 23 | install(TARGETS vca RUNTIME DESTINATION bin COMPONENT applications) 24 | -------------------------------------------------------------------------------- /source/apps/vcaPerformanceTest/vcaPerformanceTest.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2022 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Christian Feldmann 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. 18 | *****************************************************************************/ 19 | 20 | #include "vcacli.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #ifdef _WIN32 35 | #include 36 | #pragma warning(disable : 4996) 37 | #endif 38 | 39 | using namespace vca; 40 | using namespace std::string_literals; 41 | 42 | /* Ctrl-C handler */ 43 | static volatile sig_atomic_t b_ctrl_c /* = 0 */; 44 | static void sigint_handler(int) 45 | { 46 | b_ctrl_c = 1; 47 | } 48 | 49 | void logLibraryMessage(void *, LogLevel logLevel, const char *message) 50 | { 51 | vca_log(logLevel, "[LIB] "s + message); 52 | } 53 | 54 | void printStatus(uint32_t frameNum, 55 | unsigned framesToBeAnalyzed, 56 | bool start = false, 57 | bool printSummary = false) 58 | { 59 | char buf[200]; 60 | static auto startTime = std::chrono::high_resolution_clock::now(); 61 | static auto prevUpdateTime = std::chrono::high_resolution_clock::now(); 62 | if (start) 63 | { 64 | startTime = std::chrono::high_resolution_clock::now(); 65 | prevUpdateTime = std::chrono::high_resolution_clock::now(); 66 | } 67 | 68 | const auto now = std::chrono::high_resolution_clock::now(); 69 | const auto duration = std::chrono::duration_cast(now 70 | - prevUpdateTime); 71 | if (!printSummary && duration < std::chrono::milliseconds(250)) 72 | return; 73 | 74 | prevUpdateTime = now; 75 | 76 | const auto elapsedAbsMs = std::chrono::duration_cast(now - startTime) 77 | .count(); 78 | const auto fps = elapsedAbsMs > 0 ? frameNum * 1000. / elapsedAbsMs : 0.0; 79 | 80 | if (printSummary) 81 | { 82 | fprintf(stdout, 83 | "vca - Analyzed %d/%d frames, %.2f fps, time %d:%02d:%02d.%03d\n", 84 | frameNum, 85 | framesToBeAnalyzed, 86 | fps, 87 | int(elapsedAbsMs / 1000 / 3600), 88 | int((elapsedAbsMs / 1000 / 60) % 60), 89 | int((elapsedAbsMs / 1000) % 60), 90 | int(elapsedAbsMs % 1000)); 91 | return; 92 | } 93 | else if (framesToBeAnalyzed > 0) 94 | { 95 | int eta = 0; 96 | if (frameNum > 0) 97 | eta = static_cast(elapsedAbsMs * (framesToBeAnalyzed - frameNum) 98 | / (static_cast(frameNum) * 1000)); 99 | sprintf(buf, 100 | "vca [%.1f%%] %d/%d frames, %.2f fps, eta %d:%02d:%02d", 101 | 100. * frameNum / (framesToBeAnalyzed), 102 | frameNum, 103 | framesToBeAnalyzed, 104 | fps, 105 | eta / 3600, 106 | (eta / 60) % 60, 107 | eta % 60); 108 | } 109 | else 110 | sprintf(buf, "vca %d frames: %.2f fps", frameNum, fps); 111 | 112 | fprintf(stdout, "%s \r", buf); 113 | fflush(stdout); // needed in windows 114 | } 115 | 116 | struct CLIOptions 117 | { 118 | unsigned nrFrames{1000}; 119 | vca_param vcaParam; 120 | }; 121 | 122 | std::optional parseCLIOptions(int argc, char **argv) 123 | { 124 | bool bError = false; 125 | CLIOptions options; 126 | 127 | int long_options_index = -1; 128 | while (true) 129 | { 130 | auto c = getopt_long(argc, argv, short_options, long_options, &long_options_index); 131 | if (c == -1) 132 | break; 133 | 134 | if (c == 'h') 135 | { 136 | showHelp(); 137 | return {}; 138 | } 139 | 140 | if (long_options_index < 0 && c > 0) 141 | { 142 | for (size_t i = 0; i < sizeof(long_options) / sizeof(long_options[0]); i++) 143 | { 144 | if (long_options[i].val == c) 145 | { 146 | long_options_index = (int) i; 147 | break; 148 | } 149 | } 150 | 151 | if (long_options_index < 0) 152 | { 153 | /* getopt_long might have already printed an error message */ 154 | if (c != 63) 155 | vca_log(LogLevel::Warning, 156 | "internal error: short option " + std::string(1, c) 157 | + " has no long option"); 158 | return {}; 159 | } 160 | } 161 | if (long_options_index < 0) 162 | { 163 | vca_log(LogLevel::Warning, "short option " + std::string(1, c) + " unrecognized"); 164 | return {}; 165 | } 166 | 167 | auto name = std::string(long_options[long_options_index].name); 168 | auto arg = std::string(optarg); 169 | if (name == "iterations") 170 | options.nrFrames = std::stoul(optarg); 171 | else if (name == "input-depth") 172 | options.vcaParam.frameInfo.bitDepth = std::stoul(optarg); 173 | else if (name == "input-res") 174 | { 175 | auto posX = arg.find("x"); 176 | if (posX == std::string::npos) 177 | { 178 | vca_log(LogLevel::Error, "Invalid resolution provided. Format WxH."); 179 | return {}; 180 | } 181 | options.vcaParam.frameInfo.width = std::stoul(arg.substr(0, posX)); 182 | options.vcaParam.frameInfo.height = std::stoul(arg.substr(posX + 1)); 183 | } 184 | else if (name == "input-csp") 185 | { 186 | if (arg == "400" || arg == "4:0:0") 187 | options.vcaParam.frameInfo.colorspace = vca_colorSpace::YUV400; 188 | else if (arg == "420" || arg == "4:2:0") 189 | options.vcaParam.frameInfo.colorspace = vca_colorSpace::YUV420; 190 | else if (arg == "422" || arg == "4:2:2") 191 | options.vcaParam.frameInfo.colorspace = vca_colorSpace::YUV422; 192 | else if (arg == "444" || arg == "4:4:4") 193 | options.vcaParam.frameInfo.colorspace = vca_colorSpace::YUV444; 194 | } 195 | } 196 | 197 | return options; 198 | } 199 | 200 | bool checkOptions(CLIOptions options) 201 | { 202 | const auto bitDepth = options.vcaParam.frameInfo.bitDepth; 203 | if (bitDepth != 8 && bitDepth != 10 && bitDepth != 12) 204 | { 205 | vca_log(LogLevel::Error, "Invalid bit depth: " + std::to_string(bitDepth)); 206 | return false; 207 | } 208 | 209 | return true; 210 | } 211 | 212 | void logOptions(CLIOptions options) 213 | { 214 | vca_log(LogLevel::Info, "Options: "s); 215 | vca_log(LogLevel::Info, " Number frames: "s + std::to_string(options.nrFrames)); 216 | } 217 | 218 | std::vector> generateRandomFrames(vca_frame_info frameInfo, 219 | unsigned nrFrames) 220 | { 221 | if (frameInfo.colorspace != vca_colorSpace::YUV420) 222 | throw std::exception("Not implemented yet"); 223 | 224 | std::random_device randomDevice; 225 | std::default_random_engine randomEngine(randomDevice()); 226 | std::uniform_int_distribution uniform_dist(0, 255); 227 | 228 | std::vector> frames; 229 | for (unsigned i = 0; i < nrFrames; i++) 230 | { 231 | auto newFrame = std::make_unique(frameInfo); 232 | auto dataSize = newFrame->getFrameSize(); 233 | auto data = newFrame->getData(); 234 | for (size_t i = 0; i < dataSize; i++) 235 | data[i] = uint8_t(uniform_dist(randomEngine)); 236 | frames.push_back(std::move(newFrame)); 237 | } 238 | return std::move(frames); 239 | } 240 | 241 | #ifdef _WIN32 242 | /* Copy of x264 code, which allows for Unicode characters in the command line. 243 | * Retrieve command line arguments as UTF-8. */ 244 | static int get_argv_utf8(int *argc_ptr, char ***argv_ptr) 245 | { 246 | int ret = 0; 247 | wchar_t **argv_utf16 = CommandLineToArgvW(GetCommandLineW(), argc_ptr); 248 | if (argv_utf16) 249 | { 250 | int argc = *argc_ptr; 251 | int offset = (argc + 1) * sizeof(char *); 252 | int size = offset; 253 | 254 | for (int i = 0; i < argc; i++) 255 | size += WideCharToMultiByte(CP_UTF8, 0, argv_utf16[i], -1, NULL, 0, NULL, NULL); 256 | 257 | char **argv = *argv_ptr = (char **) malloc(size); 258 | if (argv) 259 | { 260 | for (int i = 0; i < argc; i++) 261 | { 262 | argv[i] = (char *) argv + offset; 263 | offset += WideCharToMultiByte(CP_UTF8, 264 | 0, 265 | argv_utf16[i], 266 | -1, 267 | argv[i], 268 | size - offset, 269 | NULL, 270 | NULL); 271 | } 272 | argv[argc] = NULL; 273 | ret = 1; 274 | } 275 | LocalFree(argv_utf16); 276 | } 277 | return ret; 278 | } 279 | #endif 280 | 281 | void runTest(CLIOptions &options, std::vector> &pushFrames) 282 | { 283 | auto analyzer = vca_analyzer_open(options.vcaParam); 284 | if (analyzer == nullptr) 285 | { 286 | vca_log(LogLevel::Error, "Error opening analyzer"); 287 | return; 288 | } 289 | 290 | printStatus(0, options.nrFrames, true); 291 | 292 | using framePtr = std::unique_ptr; 293 | auto frameIt = pushFrames.begin(); 294 | unsigned pushedFrames = 0; 295 | unsigned resultsCounter = 0; 296 | for (; pushedFrames < options.nrFrames; pushedFrames++) 297 | { 298 | auto vcaFrame = (*frameIt)->getFrame(); 299 | vcaFrame->stats.poc = pushedFrames; 300 | 301 | vca_log(LogLevel::Debug, 302 | "Start push frame " + std::to_string(pushedFrames) + " to analyzer"); 303 | 304 | auto ret = vca_analyzer_push(analyzer, vcaFrame); 305 | if (ret == VCA_ERROR) 306 | { 307 | vca_log(LogLevel::Error, "Error pushing frame to lib"); 308 | return; 309 | } 310 | 311 | vca_log(LogLevel::Debug, "Pushed frame " + std::to_string(pushedFrames) + " to analyzer"); 312 | 313 | while (vca_result_available(analyzer)) 314 | { 315 | vca_frame_results result; 316 | 317 | vca_log(LogLevel::Debug, "Result available. Pulling it"); 318 | 319 | if (vca_analyzer_pull_frame_result(analyzer, &result) == VCA_ERROR) 320 | { 321 | vca_log(LogLevel::Error, "Error pulling frame result"); 322 | return; 323 | } 324 | 325 | vca_log(LogLevel::Debug, 326 | "Got results POC " + std::to_string(result.poc) + " averageEnergy " 327 | + std::to_string(result.averageEnergy) + " sad " 328 | + std::to_string(result.sad)); 329 | 330 | resultsCounter++; 331 | } 332 | 333 | printStatus(resultsCounter, options.nrFrames); 334 | 335 | frameIt++; 336 | if (frameIt == pushFrames.end()) 337 | frameIt = pushFrames.begin(); 338 | } 339 | 340 | while (resultsCounter < pushedFrames) 341 | { 342 | vca_frame_results result; 343 | 344 | vca_log(LogLevel::Debug, "Result available. Pulling it"); 345 | 346 | if (vca_analyzer_pull_frame_result(analyzer, &result) == VCA_ERROR) 347 | { 348 | vca_log(LogLevel::Error, "Error pulling frame result"); 349 | return; 350 | } 351 | 352 | vca_log(LogLevel::Debug, 353 | "Got results POC " + std::to_string(result.poc) + " averageEnergy " 354 | + std::to_string(result.averageEnergy) + " sad " + std::to_string(result.sad)); 355 | 356 | resultsCounter++; 357 | } 358 | 359 | vca_analyzer_close(analyzer); 360 | printStatus(options.nrFrames, options.nrFrames, false, true); 361 | } 362 | 363 | int main(int argc, char **argv) 364 | { 365 | #if _WIN32 366 | char **orgArgv = argv; 367 | get_argv_utf8(&argc, &argv); 368 | #endif 369 | 370 | vca_log(LogLevel::Info, "VCA - Video Complexity Analyzer"); 371 | 372 | CLIOptions options; 373 | if (auto cliOptions = parseCLIOptions(argc, argv)) 374 | options = *cliOptions; 375 | else 376 | { 377 | vca_log(LogLevel::Error, "Error parsing parameters"); 378 | return 1; 379 | } 380 | 381 | if (options.vcaParam.frameInfo.width == 0 && options.vcaParam.frameInfo.height == 0) 382 | { 383 | options.vcaParam.frameInfo.width = 1920; 384 | options.vcaParam.frameInfo.height = 1080; 385 | } 386 | 387 | logOptions(options); 388 | 389 | if (!checkOptions(options)) 390 | { 391 | vca_log(LogLevel::Error, "Error checking parameters"); 392 | return 1; 393 | } 394 | 395 | options.vcaParam.logFunction = logLibraryMessage; 396 | 397 | /* Control-C handler */ 398 | if (signal(SIGINT, sigint_handler) == SIG_ERR) 399 | vca_log(LogLevel::Error, 400 | "Unable to register CTRL+C handler: " + std::string(strerror(errno))); 401 | 402 | auto nrFramesToAllocate = options.vcaParam.nrFrameThreads; 403 | if (nrFramesToAllocate == 0) 404 | nrFramesToAllocate = std::thread::hardware_concurrency(); 405 | 406 | const auto frameInfo = options.vcaParam.frameInfo; 407 | auto pushFrames = generateRandomFrames(frameInfo, nrFramesToAllocate + 1); 408 | vca_log(LogLevel::Info, 409 | "Generated " + std::to_string(pushFrames.size()) + " random frames (" 410 | + std::to_string(frameInfo.width) + "x" + std::to_string(frameInfo.height) + " " 411 | + vca_colorSpaceMapper.getName(frameInfo.colorspace) + " " 412 | + std::to_string(frameInfo.bitDepth) + "bit)"); 413 | 414 | const std::map cpuSimdNames = {{CpuSimd::None, "None"}, 415 | {CpuSimd::SSE2, "SSE2"}, 416 | {CpuSimd::SSSE3, "SSSE3"}, 417 | {CpuSimd::SSE4, "SSE4"}, 418 | {CpuSimd::AVX2, "AVX2"}}; 419 | 420 | for (auto &simd : cpuSimdNames) 421 | { 422 | for (unsigned blocksize : {8, 16, 32}) 423 | { 424 | std::cout << " [Run test 0 - " << simd.second << " - " << blocksize << "x" << blocksize 425 | << " " << options.vcaParam.frameInfo.bitDepth << "bit]\n"; 426 | options.vcaParam.cpuSimd = simd.first; 427 | options.vcaParam.blockSize = blocksize; 428 | 429 | runTest(options, pushFrames); 430 | std::cout << "\n"; 431 | } 432 | } 433 | 434 | return 0; 435 | } 436 | -------------------------------------------------------------------------------- /source/apps/vcaPerformanceTest/vcacli.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2022 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Christian Feldmann 5 | * Vignesh V Menon 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. 19 | *****************************************************************************/ 20 | 21 | #pragma once 22 | 23 | #include 24 | 25 | #include 26 | 27 | static const char short_options[] = "f:h?"; 28 | static const struct option long_options[] = {{"help", no_argument, NULL, 'h'}, 29 | {"iterations", required_argument, NULL, 'N'}, 30 | {"input-res", required_argument, NULL, 0}, 31 | {"input-depth", required_argument, NULL, 0}, 32 | {"input-csp", required_argument, NULL, 0}, 33 | {0, 0, 0, 0}, 34 | {0, 0, 0, 0}, 35 | {0, 0, 0, 0}, 36 | {0, 0, 0, 0}, 37 | {0, 0, 0, 0}, 38 | {0, 0, 0, 0}, 39 | {0, 0, 0, 0}}; 40 | 41 | static void showHelp() 42 | { 43 | printf("\nSyntax: vcaPerformanceTest [options]\n"); 44 | printf(" Run a test benchmark for the tool.\n"); 45 | printf("\nExecutable Options:\n"); 46 | printf("-h/--help Show this help text and exit\n"); 47 | printf("\nOptions:\n"); 48 | printf("-N/--iterations How many frames should be pushed in each test. " 49 | "(Default 1000)\n"); 50 | printf(" --input-res WxH Test picture size [w x h] (Default 1920x1080)\n"); 51 | printf(" --input-depth Bit-depth of test input. Default 8\n"); 52 | printf(" --input-csp Chroma subsampling for test\n"); 53 | printf(" 400 (4:0:0 monochrome)\n"); 54 | printf(" 420 (4:2:0 default)\n"); 55 | printf(" 422 (4:2:2)\n"); 56 | printf(" 444 (4:4:4)\n"); 57 | } 58 | -------------------------------------------------------------------------------- /source/lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | configure_file("${PROJECT_SOURCE_DIR}/source/lib/vca_config.h.in" 4 | "${PROJECT_BINARY_DIR}/vca_config.h") 5 | 6 | find_package(Git QUIET) # present in 2.8.8 7 | 8 | # defaults, in case everything below fails 9 | set(VCA_VERSION "unknown") 10 | set(VCA_LATEST_TAG "0.0") 11 | set(VCA_TAG_DISTANCE "0") 12 | 13 | set(LIB_SOURCE_DIR ${CMAKE_SOURCE_DIR}/source/lib) 14 | 15 | if(GIT_EXECUTABLE AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../../.git) 16 | execute_process( 17 | COMMAND 18 | ${GIT_EXECUTABLE} rev-list --tags --max-count=1 19 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 20 | OUTPUT_VARIABLE VCA_LATEST_TAG_COMMIT 21 | ERROR_QUIET 22 | OUTPUT_STRIP_TRAILING_WHITESPACE 23 | ) 24 | execute_process( 25 | COMMAND 26 | ${GIT_EXECUTABLE} describe --tags ${VCA_LATEST_TAG_COMMIT} 27 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 28 | OUTPUT_VARIABLE VCA_LATEST_TAG 29 | ERROR_QUIET 30 | OUTPUT_STRIP_TRAILING_WHITESPACE 31 | ) 32 | execute_process( 33 | COMMAND 34 | ${GIT_EXECUTABLE} rev-list ${VCA_LATEST_TAG}.. --count --first-parent 35 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 36 | OUTPUT_VARIABLE VCA_TAG_DISTANCE 37 | ERROR_QUIET 38 | OUTPUT_STRIP_TRAILING_WHITESPACE 39 | ) 40 | execute_process( 41 | COMMAND 42 | ${GIT_EXECUTABLE} log -1 --format=g%h 43 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 44 | OUTPUT_VARIABLE VCA_REVISION_ID 45 | ERROR_QUIET 46 | OUTPUT_STRIP_TRAILING_WHITESPACE 47 | ) 48 | endif() 49 | 50 | if(VCA_TAG_DISTANCE STREQUAL "0") 51 | set(VCA_VERSION "${VCA_LATEST_TAG}") 52 | else() 53 | set(VCA_VERSION "${VCA_LATEST_TAG}+${VCA_TAG_DISTANCE}-${VCA_REVISION_ID}") 54 | endif() 55 | 56 | message(STATUS "VCA version ${VCA_VERSION}") 57 | list(APPEND VFLAG "-DVCA_VERSION=${VCA_VERSION}") 58 | set_source_files_properties(vcaLib.cpp PROPERTIES COMPILE_FLAGS ${VFLAG}) 59 | 60 | find_package(Threads REQUIRED) 61 | 62 | # System architecture detection 63 | string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" SYSPROC) 64 | set(X86_ALIASES x86 i386 i686 x86_64 amd64) 65 | set(ARM_ALIASES armv6l armv7l aarch64) 66 | set(POWER_ALIASES ppc64 ppc64le) 67 | list(FIND X86_ALIASES "${SYSPROC}" X86MATCH) 68 | list(FIND ARM_ALIASES "${SYSPROC}" ARMMATCH) 69 | list(FIND POWER_ALIASES "${SYSPROC}" POWERMATCH) 70 | 71 | if("${SYSPROC}" STREQUAL "" OR X86MATCH GREATER "-1") 72 | add_definitions(-DX86_64=1) 73 | add_definitions(-DVCA_ARCH_X86=1) 74 | message(STATUS "Detected x86 target processor") 75 | elseif(POWERMATCH GREATER "-1") 76 | message(STATUS "Detected POWER target processor") 77 | elseif(ARMMATCH GREATER "-1") 78 | message(STATUS "Detected ARM target processor") 79 | else() 80 | message(STATUS "CMAKE_SYSTEM_PROCESSOR value `${CMAKE_SYSTEM_PROCESSOR}` is unknown") 81 | message(STATUS "Please add this value near ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}") 82 | endif() 83 | 84 | add_library(vcaLib "") 85 | target_sources(vcaLib 86 | PRIVATE 87 | vcaLib.cpp 88 | PUBLIC 89 | vcaLib.h 90 | ) 91 | 92 | if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") 93 | set(GCC 1) 94 | endif() 95 | 96 | add_subdirectory(analyzer) 97 | if (ENABLE_TEST) 98 | message(STATUS "Enable building of unit tests") 99 | add_subdirectory(test) 100 | else() 101 | message(STATUS "Not building unit tests") 102 | endif() 103 | 104 | target_include_directories(vcaInternal PRIVATE ${LIB_SOURCE_DIR}) 105 | target_include_directories(vcaLib PRIVATE ${LIB_SOURCE_DIR}) 106 | 107 | target_link_libraries(vcaLib vcaInternal) 108 | set_target_properties(vcaLib PROPERTIES OUTPUT_NAME vcaLib) 109 | set_target_properties(vcaLib PROPERTIES PUBLIC_HEADER "vcaLib.h") 110 | 111 | install(TARGETS vcaLib LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries PUBLIC_HEADER DESTINATION include) 112 | -------------------------------------------------------------------------------- /source/lib/analyzer/Analyzer.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | namespace vca { 29 | 30 | Analyzer::Analyzer(vca_param cfg) 31 | { 32 | this->cfg = cfg; 33 | this->jobs.setMaximumQueueSize(5); 34 | 35 | const auto blockSize = this->cfg.blockSize; 36 | if (blockSize != 8 && blockSize != 16 && blockSize != 32) 37 | { 38 | log(cfg, LogLevel::Error, "Invalid block size: " + std::to_string(this->cfg.blockSize)); 39 | throw std::invalid_argument("Invalid block size"); 40 | } 41 | log(cfg, LogLevel::Info, "Block size: " + std::to_string(this->cfg.blockSize)); 42 | 43 | const auto bitDepth = this->cfg.frameInfo.bitDepth; 44 | if (bitDepth != 8 && bitDepth != 10 && bitDepth != 12) 45 | { 46 | log(cfg, LogLevel::Error, "Invalid bit depth: " + std::to_string(bitDepth)); 47 | throw std::invalid_argument("Invalid bit depth"); 48 | } 49 | 50 | if (this->cfg.cpuSimd == CpuSimd::Autodetect) 51 | { 52 | this->cfg.cpuSimd = cpuDetectMaxSimd(); 53 | log(cfg, LogLevel::Info, "Autodetected SIMD."); 54 | } 55 | else if (this->cfg.cpuSimd != CpuSimd::None) 56 | { 57 | if (!isSimdSupported(this->cfg.cpuSimd)) 58 | { 59 | this->cfg.cpuSimd = cpuDetectMaxSimd(); 60 | log(cfg, 61 | LogLevel::Warning, 62 | "The selected SIMD is not available on this CPU (). Lowering it."); 63 | } 64 | } 65 | log(cfg, LogLevel::Info, "Using SIMD " + CpuSimdMapper.getName(this->cfg.cpuSimd)); 66 | 67 | if (cfg.nrFrameThreads == 0) 68 | { 69 | cfg.nrFrameThreads = std::thread::hardware_concurrency(); 70 | log(cfg, LogLevel::Info, "Autodetect nr threads " + std::to_string(cfg.nrFrameThreads)); 71 | } 72 | 73 | auto nrThreads = cfg.nrFrameThreads; 74 | log(cfg, LogLevel::Info, "Starting " + std::to_string(nrThreads) + " threads"); 75 | for (unsigned i = 0; i < nrThreads; i++) 76 | { 77 | auto newThread = std::make_unique(this->cfg, this->jobs, this->results, i); 78 | this->threadPool.push_back(std::move(newThread)); 79 | } 80 | } 81 | 82 | Analyzer::~Analyzer() 83 | { 84 | for (auto &thread : this->threadPool) 85 | thread->abort(); 86 | this->jobs.abort(); 87 | this->results.abort(); 88 | for (auto &thread : this->threadPool) 89 | thread->join(); 90 | } 91 | 92 | vca_result Analyzer::pushFrame(vca_frame *frame) 93 | { 94 | if (!this->checkFrame(frame)) 95 | return vca_result::VCA_ERROR; 96 | 97 | Job job; 98 | job.frame = frame; 99 | job.jobID = this->frameCounter; 100 | // job.macroblockRange = TODO 101 | 102 | this->jobs.waitAndPush(job); 103 | this->frameCounter++; 104 | 105 | return vca_result::VCA_OK; 106 | } 107 | 108 | bool Analyzer::resultAvailable() 109 | { 110 | return !this->results.empty(); 111 | } 112 | 113 | vca_result Analyzer::pullResult(vca_frame_results *outputResult) 114 | { 115 | auto result = this->results.waitAndPop(); 116 | if (!result) 117 | return vca_result::VCA_ERROR; 118 | 119 | if (this->previousResult) 120 | { 121 | if (this->cfg.enableDCTenergy) 122 | { 123 | computeTextureSAD(*result, *this->previousResult); 124 | if (this->previousResult->energyDiff > 0) 125 | { 126 | computeTextureEpsilon(*result, *this->previousResult); 127 | } 128 | } 129 | if (this->cfg.enableEntropy) 130 | { 131 | computeEntropySAD(*result, *this->previousResult); 132 | auto entropyDiff = result->entropyDiff; 133 | auto entropyDiffPrev = this->previousResult->entropyDiff; 134 | if (this->previousResult->entropyDiff > 0) 135 | result->entropyEpsilon = abs(entropyDiffPrev - entropyDiff); 136 | } 137 | } 138 | 139 | outputResult->poc = result->poc; 140 | outputResult->jobID = result->jobID; 141 | 142 | if (this->cfg.enableDCTenergy) 143 | { 144 | outputResult->averageBrightness = result->averageBrightness; 145 | outputResult->averageEnergy = result->averageEnergy; 146 | outputResult->energyDiff = result->energyDiff; 147 | outputResult->energyEpsilon = result->energyEpsilon; 148 | 149 | if (outputResult->brightnessPerBlock) 150 | std::memcpy(outputResult->brightnessPerBlock, 151 | result->brightnessPerBlock.data(), 152 | result->brightnessPerBlock.size() * sizeof(uint32_t)); 153 | if (outputResult->energyPerBlock) 154 | std::memcpy(outputResult->energyPerBlock, 155 | result->energyPerBlock.data(), 156 | result->energyPerBlock.size() * sizeof(uint32_t)); 157 | if (outputResult->energyDiffPerBlock) 158 | std::memcpy(outputResult->energyDiffPerBlock, 159 | result->energyDiffPerBlock.data(), 160 | result->energyDiffPerBlock.size() * sizeof(uint32_t)); 161 | if (this->cfg.enableEnergyChroma) 162 | { 163 | outputResult->averageU = result->averageU; 164 | outputResult->averageV = result->averageV; 165 | outputResult->energyU = result->energyU; 166 | outputResult->energyV = result->energyV; 167 | if (outputResult->averageUPerBlock) 168 | std::memcpy(outputResult->averageUPerBlock, 169 | result->averageUPerBlock.data(), 170 | result->averageUPerBlock.size() * sizeof(uint32_t)); 171 | if (outputResult->averageVPerBlock) 172 | std::memcpy(outputResult->averageVPerBlock, 173 | result->averageVPerBlock.data(), 174 | result->averageVPerBlock.size() * sizeof(uint32_t)); 175 | if (outputResult->energyUPerBlock) 176 | std::memcpy(outputResult->energyUPerBlock, 177 | result->energyUPerBlock.data(), 178 | result->energyUPerBlock.size() * sizeof(uint32_t)); 179 | if (outputResult->energyVPerBlock) 180 | std::memcpy(outputResult->energyVPerBlock, 181 | result->energyVPerBlock.data(), 182 | result->energyVPerBlock.size() * sizeof(uint32_t)); 183 | } 184 | } 185 | if (this->cfg.enableEntropy) 186 | { 187 | outputResult->averageEntropy = result->entropyY; 188 | outputResult->entropyDiff = result->entropyDiff; 189 | outputResult->entropyEpsilon = result->entropyEpsilon; 190 | 191 | if (outputResult->entropyPerBlock) 192 | std::memcpy(outputResult->entropyPerBlock, 193 | result->entropyPerBlock.data(), 194 | result->entropyPerBlock.size() * sizeof(double)); 195 | if (outputResult->entropyDiffPerBlock) 196 | std::memcpy(outputResult->entropyDiffPerBlock, 197 | result->entropyDiffPerBlock.data(), 198 | result->entropyDiffPerBlock.size() * sizeof(double)); 199 | if (this->cfg.enableEntropyChroma) 200 | { 201 | outputResult->entropyU = result->entropyU; 202 | outputResult->entropyV = result->entropyV; 203 | if (outputResult->entropyUPerBlock) 204 | std::memcpy(outputResult->entropyUPerBlock, 205 | result->entropyUPerBlock.data(), 206 | result->entropyUPerBlock.size() * sizeof(double)); 207 | if (outputResult->entropyVPerBlock) 208 | std::memcpy(outputResult->entropyVPerBlock, 209 | result->entropyVPerBlock.data(), 210 | result->entropyVPerBlock.size() * sizeof(double)); 211 | } 212 | 213 | } 214 | if (this->cfg.enableEdgeDensity) 215 | { 216 | outputResult->averageEdgeDensity = result->averageEdgeDensity; 217 | if (outputResult->edgeDensityPerBlock) 218 | std::memcpy(outputResult->edgeDensityPerBlock, 219 | result->edgeDensityPerBlock.data(), 220 | result->edgeDensityPerBlock.size() * sizeof(double)); 221 | } 222 | 223 | this->previousResult = result; 224 | 225 | return vca_result::VCA_OK; 226 | } 227 | 228 | bool Analyzer::checkFrame(const vca_frame *frame) 229 | { 230 | if (frame == nullptr) 231 | { 232 | log(this->cfg, LogLevel::Error, "Nullptr pushed"); 233 | return false; 234 | } 235 | 236 | if (frame->planes[0] == nullptr || frame->stride[0] == 0) 237 | { 238 | log(this->cfg, LogLevel::Error, "No luma data provided"); 239 | return false; 240 | } 241 | 242 | const auto &info = frame->info; 243 | 244 | if (!this->frameInfo) 245 | { 246 | if (info.bitDepth != 8 && info.bitDepth != 10 && info.bitDepth != 12) 247 | { 248 | log(this->cfg, 249 | LogLevel::Error, 250 | "Frame with invalid bit " + std::to_string(info.bitDepth) 251 | + " depth provided. Must be 8, 10, or 12."); 252 | return false; 253 | } 254 | if (info.width == 0 || info.width % 2 != 0 || info.height == 0 || info.height % 2 != 0) 255 | { 256 | log(this->cfg, 257 | LogLevel::Error, 258 | "Frame with invalid size " + std::to_string(info.width) + "x" 259 | + std::to_string(info.height) + " depth provided"); 260 | return false; 261 | } 262 | this->frameInfo = info; 263 | } 264 | 265 | if (info.bitDepth != this->frameInfo->bitDepth || info.width != this->frameInfo->width 266 | || info.height != this->frameInfo->height || info.colorspace != this->frameInfo->colorspace) 267 | { 268 | log(this->cfg, 269 | LogLevel::Error, 270 | "Frame settings differ from the settings that the library was configured with"); 271 | return false; 272 | } 273 | 274 | return true; 275 | } 276 | 277 | } // namespace vca 278 | -------------------------------------------------------------------------------- /source/lib/analyzer/Analyzer.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | namespace vca { 32 | 33 | class Analyzer 34 | { 35 | public: 36 | Analyzer(vca_param cfg); 37 | ~Analyzer(); 38 | 39 | vca_result pushFrame(vca_frame *frame); 40 | bool resultAvailable(); 41 | vca_result pullResult(vca_frame_results *result); 42 | 43 | private: 44 | vca_param cfg{}; 45 | bool checkFrame(const vca_frame *frame); 46 | std::optional frameInfo; 47 | unsigned frameCounter{0}; 48 | 49 | std::vector> threadPool; 50 | 51 | MultiThreadQueue jobs; 52 | MultiThreadQueue results; 53 | 54 | std::optional previousResult; 55 | }; 56 | 57 | } // namespace vca 58 | -------------------------------------------------------------------------------- /source/lib/analyzer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | add_library(vcaInternal STATIC 4 | common/common.h 5 | common/EnumMapper.h 6 | Analyzer.h 7 | Analyzer.cpp 8 | DCTTransform.h 9 | DCTTransform.cpp 10 | DCTTransformsNative.h 11 | DCTTransformsNative.cpp 12 | EnergyCalculation.h 13 | EnergyCalculation.cpp 14 | EntropyNative.h 15 | EntropyNative.cpp 16 | EntropyCalculation.h 17 | EntropyCalculation.cpp 18 | MultiThreadQueue.h 19 | MultiThreadQueue.cpp 20 | ProcessingThread.h 21 | ProcessingThread.cpp 22 | ShotDetection.h 23 | ShotDetection.cpp 24 | simd/cpu.h 25 | simd/cpu.cpp 26 | simd/dct8.h 27 | simd/entropy.h 28 | ) 29 | 30 | if(ENABLE_NASM) 31 | enable_language(ASM_NASM) 32 | 33 | if(CMAKE_ASM_NASM_COMPILER_LOADED) 34 | message(STATUS "Nasm found. Activating nasm assembly.") 35 | set(BUILD_WITH_NASM 1) 36 | else() 37 | message(STATUS "Nasm could not be found. Disabling nasm assembly.") 38 | endif(CMAKE_ASM_NASM_COMPILER_LOADED) 39 | 40 | set_source_files_properties(simd/cpu.cpp PROPERTIES COMPILE_FLAGS -DENABLE_NASM=1) 41 | else() 42 | message(STATUS "Nasm disabled. Not looking for it or using it.") 43 | endif(ENABLE_NASM) 44 | 45 | target_include_directories(vcaInternal PRIVATE ${LIB_SOURCE_DIR}) 46 | 47 | add_subdirectory(simd) 48 | 49 | target_link_libraries(vcaInternal Threads::Threads vcaLibSimd8bit vcaLibSimd10bit vcaLibSimd12bit) 50 | -------------------------------------------------------------------------------- /source/lib/analyzer/DCTTransform.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Mandar Gurav 5 | * Deepthi Devaki Akkoorath 6 | * Mahesh Pittala 7 | * Rajesh Paulraj 8 | * Min Chen 9 | * Praveen Kumar Tiwari 10 | * Nabajit Deka 11 | * 12 | * This program is free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 3 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * This program is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU General Public License 23 | * along with this program. 24 | *****************************************************************************/ 25 | 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | namespace vca { 36 | 37 | void performDCTBlockSize32(const unsigned bitDepth, 38 | int16_t *pixelBuffer, 39 | int16_t *coeffBuffer, 40 | CpuSimd cpuSimd) 41 | { 42 | if (cpuSimd == CpuSimd::AVX2) 43 | { 44 | if (bitDepth == 8) 45 | vca_dct32_8bit_avx2(pixelBuffer, coeffBuffer, 32); 46 | else if (bitDepth == 10) 47 | vca_dct32_10bit_avx2(pixelBuffer, coeffBuffer, 32); 48 | else if (bitDepth == 12) 49 | vca_dct32_12bit_avx2(pixelBuffer, coeffBuffer, 32); 50 | } 51 | else if (cpuSimd == CpuSimd::SSSE3) 52 | { 53 | if (bitDepth == 8) 54 | vca_dct32_8bit_ssse3(pixelBuffer, coeffBuffer, 32); 55 | else if (bitDepth == 10) 56 | vca_dct32_10bit_ssse3(pixelBuffer, coeffBuffer, 32); 57 | else if (bitDepth == 12) 58 | vca_dct32_12bit_ssse3(pixelBuffer, coeffBuffer, 32); 59 | } 60 | else 61 | vca::dct32_c(pixelBuffer, coeffBuffer, 32, bitDepth); 62 | } 63 | 64 | void performDCTBlockSize16(const unsigned bitDepth, 65 | int16_t *pixelBuffer, 66 | int16_t *coeffBuffer, 67 | CpuSimd cpuSimd) 68 | { 69 | if (cpuSimd == CpuSimd::AVX2) 70 | { 71 | if (bitDepth == 8) 72 | vca_dct16_8bit_avx2(pixelBuffer, coeffBuffer, 16); 73 | else if (bitDepth == 10) 74 | vca_dct16_10bit_avx2(pixelBuffer, coeffBuffer, 16); 75 | else if (bitDepth == 12) 76 | vca_dct16_12bit_avx2(pixelBuffer, coeffBuffer, 16); 77 | } 78 | else if (cpuSimd == CpuSimd::SSSE3) 79 | { 80 | if (bitDepth == 8) 81 | vca_dct16_8bit_ssse3(pixelBuffer, coeffBuffer, 16); 82 | else if (bitDepth == 10) 83 | vca_dct16_10bit_ssse3(pixelBuffer, coeffBuffer, 16); 84 | else if (bitDepth == 12) 85 | vca_dct16_12bit_ssse3(pixelBuffer, coeffBuffer, 16); 86 | } 87 | else 88 | vca::dct16_c(pixelBuffer, coeffBuffer, 16, bitDepth); 89 | } 90 | 91 | void performDCTBlockSize8(const unsigned bitDepth, 92 | int16_t *pixelBuffer, 93 | int16_t *coeffBuffer, 94 | CpuSimd cpuSimd) 95 | { 96 | if (cpuSimd == CpuSimd::AVX2) 97 | { 98 | if (bitDepth == 8) 99 | vca_dct8_8bit_avx2(pixelBuffer, coeffBuffer, 8); 100 | else if (bitDepth == 10) 101 | vca_dct8_10bit_avx2(pixelBuffer, coeffBuffer, 8); 102 | else if (bitDepth == 12) 103 | vca_dct8_12bit_avx2(pixelBuffer, coeffBuffer, 8); 104 | } 105 | else if (cpuSimd == CpuSimd::SSE4) 106 | { 107 | if (bitDepth == 8) 108 | vca_dct8_8bit_sse4(pixelBuffer, coeffBuffer, 8); 109 | else if (bitDepth == 10) 110 | vca_dct8_10bit_sse4(pixelBuffer, coeffBuffer, 8); 111 | else if (bitDepth == 12) 112 | vca_dct8_12bit_sse4(pixelBuffer, coeffBuffer, 8); 113 | } 114 | else if (cpuSimd == CpuSimd::SSE2) 115 | { 116 | if (bitDepth == 8) 117 | vca_dct8_8bit_sse2(pixelBuffer, coeffBuffer, 8); 118 | else if (bitDepth == 10) 119 | vca_dct8_10bit_sse2(pixelBuffer, coeffBuffer, 8); 120 | else if (bitDepth == 12) 121 | vca_dct8_12bit_sse2(pixelBuffer, coeffBuffer, 8); 122 | } 123 | else 124 | vca::dct8_c(pixelBuffer, coeffBuffer, 8, bitDepth); 125 | } 126 | 127 | void performLowpassDCTBlockSize16(const unsigned bitDepth, 128 | const int16_t *src, 129 | int16_t *dst, 130 | CpuSimd cpuSimd) 131 | { 132 | ALIGN_VAR_32(int16_t, coef[8 * 8]); 133 | ALIGN_VAR_32(int16_t, avgBlock[8 * 8]); 134 | int32_t totalSum = 0; 135 | int16_t sum = 0; 136 | for (int i = 0; i < 8; i++) 137 | { 138 | for (int j = 0; j < 8; j++) 139 | { 140 | sum = src[2 * i * 16 + 2 * j] + src[2 * i * 16 + 2 * j + 1] 141 | + src[(2 * i + 1) * 16 + 2 * j] + src[(2 * i + 1) * 16 + 2 * j + 1]; 142 | avgBlock[i * 8 + j] = sum >> 2; 143 | totalSum += sum; 144 | } 145 | } 146 | 147 | if (cpuSimd == CpuSimd::AVX2) 148 | { 149 | if (bitDepth == 8) 150 | vca_dct8_8bit_avx2(avgBlock, coef, 8); 151 | else if (bitDepth == 10) 152 | vca_dct8_10bit_avx2(avgBlock, coef, 8); 153 | else if (bitDepth == 12) 154 | vca_dct8_12bit_avx2(avgBlock, coef, 8); 155 | } 156 | else if (cpuSimd == CpuSimd::SSE4) 157 | { 158 | if (bitDepth == 8) 159 | vca_dct8_8bit_sse4(avgBlock, coef, 8); 160 | else if (bitDepth == 10) 161 | vca_dct8_10bit_sse4(avgBlock, coef, 8); 162 | else if (bitDepth == 12) 163 | vca_dct8_12bit_sse4(avgBlock, coef, 8); 164 | } 165 | else if (cpuSimd == CpuSimd::SSE2) 166 | { 167 | if (bitDepth == 8) 168 | vca_dct8_8bit_sse2(avgBlock, coef, 8); 169 | else if (bitDepth == 10) 170 | vca_dct8_10bit_sse2(avgBlock, coef, 8); 171 | else if (bitDepth == 12) 172 | vca_dct8_12bit_sse2(avgBlock, coef, 8); 173 | } 174 | else 175 | vca::dct8_c(avgBlock, coef, 8, bitDepth); 176 | 177 | std::memset(dst, 0, 256 * sizeof(int16_t)); 178 | for (int i = 0; i < 8; i++) 179 | { 180 | std::memcpy(&dst[i * 16], &coef[i * 8], 8 * sizeof(int16_t)); 181 | } 182 | dst[0] = static_cast(totalSum >> 1); 183 | } 184 | 185 | void performLowpassDCTBlockSize32(const unsigned bitDepth, 186 | const int16_t *src, 187 | int16_t *dst, 188 | CpuSimd cpuSimd) 189 | { 190 | ALIGN_VAR_32(int16_t, coef[16 * 16]); 191 | ALIGN_VAR_32(int16_t, avgBlock[16 * 16]); 192 | int32_t totalSum = 0; 193 | int16_t sum = 0; 194 | for (int i = 0; i < 16; i++) 195 | for (int j = 0; j < 16; j++) 196 | { 197 | sum = src[2 * i * 32 + 2 * j] + src[2 * i * 32 + 2 * j + 1] 198 | + src[(2 * i + 1) * 32 + 2 * j] + src[(2 * i + 1) * 32 + 2 * j + 1]; 199 | avgBlock[i * 16 + j] = sum >> 2; 200 | totalSum += sum; 201 | } 202 | 203 | if (cpuSimd == CpuSimd::AVX2) 204 | { 205 | if (bitDepth == 8) 206 | vca_dct16_8bit_avx2(avgBlock, coef, 16); 207 | else if (bitDepth == 10) 208 | vca_dct16_10bit_avx2(avgBlock, coef, 16); 209 | else if (bitDepth == 12) 210 | vca_dct16_12bit_avx2(avgBlock, coef, 16); 211 | } 212 | else if (cpuSimd == CpuSimd::SSSE3) 213 | { 214 | if (bitDepth == 8) 215 | vca_dct16_8bit_ssse3(avgBlock, coef, 16); 216 | else if (bitDepth == 10) 217 | vca_dct16_10bit_ssse3(avgBlock, coef, 16); 218 | else if (bitDepth == 12) 219 | vca_dct16_12bit_ssse3(avgBlock, coef, 16); 220 | } 221 | else 222 | vca::dct16_c(avgBlock, coef, 16, bitDepth); 223 | std::memset(dst, 0, 1024 * sizeof(int16_t)); 224 | for (int i = 0; i < 16; i++) 225 | { 226 | std::memcpy(&dst[i * 32], &coef[i * 16], 16 * sizeof(int16_t)); 227 | } 228 | dst[0] = static_cast(totalSum >> 3); 229 | } 230 | 231 | void performDCT(const unsigned blockSize, 232 | const unsigned bitDepth, 233 | int16_t *pixelBuffer, 234 | int16_t *coeffBuffer, 235 | CpuSimd cpuSimd, 236 | bool enableLowpassDCT) 237 | { 238 | if (bitDepth != 8 && bitDepth != 10 && bitDepth != 12) 239 | throw std::invalid_argument("Invalid bit depth " + std::to_string(bitDepth)); 240 | 241 | switch (blockSize) 242 | { 243 | case 32: 244 | if (enableLowpassDCT) 245 | performLowpassDCTBlockSize32(bitDepth, pixelBuffer, coeffBuffer, cpuSimd); 246 | else 247 | performDCTBlockSize32(bitDepth, pixelBuffer, coeffBuffer, cpuSimd); 248 | break; 249 | case 16: 250 | if (enableLowpassDCT) 251 | performLowpassDCTBlockSize16(bitDepth, pixelBuffer, coeffBuffer, cpuSimd); 252 | else 253 | performDCTBlockSize16(bitDepth, pixelBuffer, coeffBuffer, cpuSimd); 254 | break; 255 | case 8: 256 | performDCTBlockSize8(bitDepth, pixelBuffer, coeffBuffer, cpuSimd); 257 | break; 258 | default: 259 | throw std::invalid_argument("Invalid block size " + std::to_string(blockSize)); 260 | } 261 | } 262 | 263 | } // namespace vca 264 | -------------------------------------------------------------------------------- /source/lib/analyzer/DCTTransform.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Mandar Gurav 5 | * Deepthi Devaki Akkoorath 6 | * Mahesh Pittala 7 | * Rajesh Paulraj 8 | * Min Chen 9 | * Praveen Kumar Tiwari 10 | * Nabajit Deka 11 | * 12 | * This program is free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 3 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * This program is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU General Public License 23 | * along with this program. 24 | *****************************************************************************/ 25 | 26 | #pragma once 27 | 28 | #include 29 | 30 | namespace vca { 31 | 32 | void performDCT(const unsigned blockSize, 33 | const unsigned bitDepth, 34 | int16_t *pixelBuffer, 35 | int16_t *coeffBuffer, 36 | CpuSimd cpuSimd, 37 | bool enableLowpassDCT); 38 | 39 | } // namespace vca 40 | -------------------------------------------------------------------------------- /source/lib/analyzer/DCTTransformsNative.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Mandar Gurav 5 | * Deepthi Devaki Akkoorath 6 | * Mahesh Pittala 7 | * Rajesh Paulraj 8 | * Min Chen 9 | * Praveen Kumar Tiwari 10 | * Nabajit Deka 11 | * 12 | * This program is free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 3 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * This program is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU General Public License 23 | * along with this program. 24 | *****************************************************************************/ 25 | 26 | #pragma once 27 | 28 | #include 29 | 30 | namespace vca { 31 | 32 | typedef void (*dct_t)(const int16_t *src, int16_t *dst, intptr_t srcStride); 33 | 34 | void dct8_c(const int16_t *src, int16_t *dst, intptr_t srcStride, const unsigned bitDepth); 35 | void dct16_c(const int16_t *src, int16_t *dst, intptr_t srcStride, const unsigned bitDepth); 36 | void dct32_c(const int16_t *src, int16_t *dst, intptr_t srcStride, const unsigned bitDepth); 37 | 38 | } // namespace vca -------------------------------------------------------------------------------- /source/lib/analyzer/EnergyCalculation.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Vignesh V Menon 5 | * Christian Feldmann 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. 19 | *****************************************************************************/ 20 | 21 | #pragma once 22 | 23 | #include 24 | 25 | namespace vca { 26 | 27 | void computeWeightedDCTEnergy(const Job &job, 28 | Result &result, 29 | const unsigned blockSize, 30 | CpuSimd cpuSimd, 31 | bool enableChroma, 32 | bool enableLowpass); 33 | void computeTextureSAD(Result &results, const Result &resultsPreviousFrame); 34 | void computeTextureEpsilon(Result &results, const Result &resultsPreviousFrame); 35 | void computeEntropy(const Job &job, 36 | Result &result, 37 | const unsigned blockSize, 38 | CpuSimd cpuSimd, 39 | bool enableLowpass, 40 | bool enableChroma); 41 | void computeEntropySAD(Result &results, const Result &resultsPreviousFrame); 42 | void computeEdgeDensity(const Job &job, 43 | Result &result, 44 | const unsigned blockSize, 45 | CpuSimd cpuSimd, 46 | bool enableLowpass); 47 | 48 | } // namespace vca 49 | -------------------------------------------------------------------------------- /source/lib/analyzer/EntropyCalculation.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Amritha Premkumar 5 | * Prajit T Rajendran 6 | * Vignesh V Menon 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. 20 | *****************************************************************************/ 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | namespace vca { 29 | 30 | double performEntropy(const unsigned blockSize, 31 | const unsigned bitDepth, 32 | const int16_t *pixelBuffer, 33 | CpuSimd cpuSimd, 34 | bool enableLowpass) 35 | { 36 | std::vector block(blockSize * blockSize); 37 | 38 | // Copy pixels from pixelBuffer to block 39 | for (uint32_t i = 0; i < blockSize; ++i) 40 | { 41 | for (uint32_t j = 0; j < blockSize; ++j) 42 | { 43 | block[i * blockSize + j] = pixelBuffer[i * blockSize + j]; 44 | } 45 | } 46 | 47 | double entropy = 0; 48 | // Calculate entropy 49 | //if (cpuSimd == CpuSimd::AVX2) 50 | //{ 51 | // entropy = entropy_avx2(block); 52 | //} 53 | //else 54 | if (enableLowpass) 55 | entropy = vca::entropy_lowpass_c(block, blockSize); 56 | else 57 | entropy = vca::entropy_c(block); 58 | 59 | return entropy; 60 | } 61 | 62 | double performEdgeDensity(const unsigned blockSize, 63 | const unsigned bitDepth, 64 | const int16_t *pixelBuffer, 65 | CpuSimd cpuSimd, 66 | bool enableLowpass) 67 | { 68 | // Calculate the total number of pixels in the block 69 | unsigned blockSizeSq = blockSize * blockSize; 70 | 71 | // Threshold for edge detection based on bit depth 72 | int threshold = (1 << (bitDepth - 1)) - 1; 73 | 74 | // Initialize edge count to 0 75 | unsigned edgeCount = 0; 76 | 77 | // Iterate through the pixel buffer 78 | for (unsigned i = 0; i < blockSizeSq; ++i) 79 | { 80 | // Check edge conditions for pixels in the buffer 81 | if (i % blockSize < blockSize - 1 && abs(pixelBuffer[i] - pixelBuffer[i + 1]) > threshold) 82 | { 83 | // Horizontal edge detected 84 | edgeCount++; 85 | } 86 | if (i / blockSize < blockSize - 1 87 | && abs(pixelBuffer[i] - pixelBuffer[i + blockSize]) > threshold) 88 | { 89 | // Vertical edge detected 90 | edgeCount++; 91 | } 92 | } 93 | 94 | // Calculate edge density 95 | double density = static_cast(edgeCount) / (2 * blockSize * (blockSize - 1)); 96 | 97 | return density; 98 | } 99 | 100 | } // namespace vca 101 | -------------------------------------------------------------------------------- /source/lib/analyzer/EntropyCalculation.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Amritha Premkumar 5 | * Prajit T Rajendran 6 | * Vignesh V Menon 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. 20 | *****************************************************************************/ 21 | 22 | #pragma once 23 | 24 | #include 25 | 26 | namespace vca { 27 | 28 | double performEntropy(const unsigned blockSize, 29 | const unsigned bitDepth, 30 | const int16_t *pixelBuffer, 31 | CpuSimd cpuSimd, 32 | bool enableLowpass); 33 | 34 | double performEdgeDensity(const unsigned blockSize, 35 | const unsigned bitDepth, 36 | const int16_t *pixelBuffer, 37 | CpuSimd cpuSimd, 38 | bool enableLowpass); 39 | 40 | } // namespace vca 41 | -------------------------------------------------------------------------------- /source/lib/analyzer/EntropyNative.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Amritha Premkumar 5 | * Prajit T Rajendran 6 | * Vignesh V Menon 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. 20 | *****************************************************************************/ 21 | 22 | #include "EntropyNative.h" 23 | 24 | namespace vca { 25 | 26 | double entropy_c(const std::vector &block) 27 | { 28 | std::unordered_map pixelCounts; 29 | int totalPixels = static_cast(block.size()); 30 | 31 | // Count occurrences of each pixel value 32 | for (int pixel : block) 33 | { 34 | pixelCounts[pixel]++; 35 | } 36 | 37 | // Calculate probability of each pixel value 38 | std::vector probabilities; 39 | for (const auto &pair : pixelCounts) 40 | { 41 | double probability = static_cast(pair.second) / totalPixels; 42 | probabilities.push_back(probability); 43 | } 44 | 45 | // Calculate entropy 46 | double entropy = 0.0; 47 | for (double probability : probabilities) 48 | { 49 | entropy -= probability * log2(probability); 50 | } 51 | 52 | return entropy; 53 | } 54 | 55 | double entropy_lowpass_c(const std::vector &block, int width) 56 | { 57 | // Check if width and height are divisible by 2 58 | if (width % 2 != 0) 59 | { 60 | return -1.0; // Error: Width is not divisible by 2 61 | } 62 | 63 | std::unordered_map pixelCounts; 64 | 65 | // Downscale the block by averaging 2x2 blocks of pixels into a single pixel 66 | int downscaledWidth = width >> 1; 67 | std::vector downscaledBlock(downscaledWidth * downscaledWidth, 0); 68 | 69 | for (int i = 0; i < width; i += 2) 70 | { 71 | for (int j = 0; j < width; j += 2) 72 | { 73 | // Compute average pixel value of 2x2 block 74 | int sum = block[i * width + j] + block[i * width + j + 1] + block[(i + 1) * width + j] 75 | + block[(i + 1) * width + j + 1]; 76 | int averagePixel = sum >> 2; 77 | 78 | // Store the average pixel value in the downscaled block 79 | downscaledBlock[(i / 2) * downscaledWidth + (j / 2)] = averagePixel; 80 | 81 | // Count occurrences of the average pixel value 82 | pixelCounts[averagePixel]++; 83 | } 84 | } 85 | 86 | // Calculate probability of each pixel value in the downscaled block 87 | int totalPixels = downscaledWidth * downscaledWidth; 88 | std::vector probabilities; 89 | for (const auto &pair : pixelCounts) 90 | { 91 | double probability = static_cast(pair.second) / totalPixels; 92 | probabilities.push_back(probability); 93 | } 94 | 95 | // Calculate entropy of the downscaled block 96 | double entropy = 0.0; 97 | for (double probability : probabilities) 98 | { 99 | if (probability > 0.0) 100 | { 101 | entropy -= probability * log2(probability); 102 | } 103 | } 104 | 105 | return entropy; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /source/lib/analyzer/EntropyNative.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Amritha Premkumar 5 | * Prajit T Rajendran 6 | * Vignesh V Menon 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. 20 | *****************************************************************************/ 21 | 22 | #pragma once 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | namespace vca { 30 | 31 | double entropy_c(const std::vector &block); 32 | double entropy_lowpass_c(const std::vector &block, int blocksize); 33 | 34 | } // namespace vca 35 | 36 | -------------------------------------------------------------------------------- /source/lib/analyzer/MultiThreadQueue.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #include "MultiThreadQueue.h" 20 | 21 | #include 22 | 23 | namespace vca { 24 | 25 | template 26 | void MultiThreadQueue::abort() 27 | { 28 | this->aborted = true; 29 | this->pushJobCV.notify_all(); 30 | this->popJobCV.notify_all(); 31 | } 32 | 33 | template 34 | void MultiThreadQueue::waitAndPush(T item) 35 | { 36 | if (this->aborted) 37 | return; 38 | 39 | std::unique_lock lock(this->accessMutex); 40 | this->popJobCV.wait(lock, [this]() { 41 | return this->maximumQueueSize == 0 || this->items.size() < this->maximumQueueSize 42 | || this->aborted; 43 | }); 44 | 45 | if (this->aborted) 46 | return; 47 | 48 | this->items.push(std::move(item)); 49 | this->pushJobCV.notify_one(); 50 | } 51 | 52 | template 53 | void MultiThreadQueue::waitAndPushInOrder(T item, size_t orderCounter) 54 | { 55 | if (this->aborted) 56 | return; 57 | 58 | std::unique_lock lock(this->accessMutex); 59 | this->popJobCV.wait(lock, [this, orderCounter]() { 60 | if (this->aborted) 61 | return true; 62 | auto slotFree = this->maximumQueueSize == 0 || this->items.size() < this->maximumQueueSize; 63 | return slotFree && orderCounter == this->pushCounter; 64 | }); 65 | 66 | if (this->aborted) 67 | return; 68 | 69 | this->items.push(std::move(item)); 70 | this->pushCounter++; 71 | this->popJobCV.notify_all(); 72 | this->pushJobCV.notify_one(); 73 | } 74 | 75 | template 76 | std::optional MultiThreadQueue::waitAndPop() 77 | { 78 | std::unique_lock lock(this->accessMutex); 79 | this->pushJobCV.wait(lock, [this]() { return !this->items.empty() || this->aborted; }); 80 | 81 | if (this->aborted) 82 | return {}; 83 | 84 | auto item = this->items.front(); 85 | this->items.pop(); 86 | this->popJobCV.notify_one(); 87 | return item; 88 | } 89 | 90 | template 91 | bool MultiThreadQueue::empty() 92 | { 93 | if (this->aborted) 94 | return false; 95 | 96 | std::unique_lock lock(this->accessMutex); 97 | return this->items.empty(); 98 | } 99 | 100 | template 101 | void MultiThreadQueue::setMaximumQueueSize(size_t max) 102 | { 103 | this->maximumQueueSize = max; 104 | } 105 | 106 | template class MultiThreadQueue; 107 | template class MultiThreadQueue; 108 | 109 | } // namespace vca 110 | -------------------------------------------------------------------------------- /source/lib/analyzer/MultiThreadQueue.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | namespace vca { 27 | 28 | template 29 | class MultiThreadQueue 30 | { 31 | public: 32 | // Push an item to the queue. Wait if the queue reached a maximum size. 33 | // Wake one waiting thread. 34 | void waitAndPush(T item); 35 | // Push items in order. Increase the counter by one in the order of items. 36 | // Pushing threads will be paused until the pushs are in order. 37 | // Don't mix calls to these two push functions. 38 | void waitAndPushInOrder(T item, size_t counter); 39 | 40 | // Get an item. If the queue is empty, wait until an item is pushed. 41 | // Will return empty opt if abort is called. 42 | std::optional waitAndPop(); 43 | 44 | void abort(); 45 | bool empty(); 46 | 47 | // If the queue is fuller then this limit, the push function will wait until 48 | // there is enought space. 0 means no limit. 49 | void setMaximumQueueSize(size_t max); 50 | 51 | private: 52 | std::queue items; 53 | std::mutex accessMutex; 54 | std::condition_variable pushJobCV; 55 | std::condition_variable popJobCV; 56 | 57 | bool aborted{}; 58 | size_t maximumQueueSize{}; 59 | size_t pushCounter{}; 60 | }; 61 | 62 | } // namespace vca 63 | -------------------------------------------------------------------------------- /source/lib/analyzer/ProcessingThread.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #include "ProcessingThread.h" 20 | 21 | #include 22 | #include 23 | 24 | namespace vca { 25 | 26 | ProcessingThread::ProcessingThread(vca_param cfg, 27 | MultiThreadQueue &jobs, 28 | MultiThreadQueue &results, 29 | unsigned id) 30 | { 31 | this->cfg = cfg; 32 | this->id = id; 33 | 34 | this->thread = std::thread(&ProcessingThread::threadFunction, 35 | this, 36 | std::ref(jobs), 37 | std::ref(results)); 38 | } 39 | 40 | void ProcessingThread::threadFunction(MultiThreadQueue &jobQueue, 41 | MultiThreadQueue &results) 42 | { 43 | while (!this->aborted) 44 | { 45 | auto job = jobQueue.waitAndPop(); 46 | if (!job) 47 | break; 48 | 49 | log(this->cfg, 50 | LogLevel::Debug, 51 | "Thread " + std::to_string(this->id) + ": Start work on job " + job->infoString()); 52 | 53 | Result result; 54 | result.poc = job->frame->stats.poc; 55 | result.jobID = job->jobID; 56 | if (this->cfg.enableDCTenergy) 57 | { 58 | computeWeightedDCTEnergy(*job, 59 | result, 60 | this->cfg.blockSize, 61 | this->cfg.cpuSimd, 62 | this->cfg.enableEnergyChroma, 63 | this->cfg.enableLowpass); 64 | } 65 | if (this->cfg.enableEntropy) 66 | { 67 | computeEntropy(*job, 68 | result, 69 | this->cfg.blockSize, 70 | this->cfg.cpuSimd, 71 | this->cfg.enableLowpass, 72 | this->cfg.enableEntropyChroma); 73 | } 74 | if (this->cfg.enableEdgeDensity) 75 | { 76 | computeEdgeDensity(*job, 77 | result, 78 | this->cfg.blockSize, 79 | this->cfg.cpuSimd, 80 | this->cfg.enableLowpass); 81 | } 82 | log(this->cfg, 83 | LogLevel::Debug, 84 | "Thread " + std::to_string(this->id) + ": Finished work on job " + job->infoString()); 85 | 86 | results.waitAndPushInOrder(result, result.jobID); 87 | } 88 | 89 | log(this->cfg, LogLevel::Debug, "Thread " + std::to_string(this->id) + " quit"); 90 | } 91 | 92 | void ProcessingThread::abort() 93 | { 94 | this->aborted = true; 95 | } 96 | 97 | void ProcessingThread::join() 98 | { 99 | this->aborted = true; 100 | this->thread.join(); 101 | } 102 | 103 | } // namespace vca 104 | -------------------------------------------------------------------------------- /source/lib/analyzer/ProcessingThread.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | namespace vca { 28 | 29 | class ProcessingThread 30 | { 31 | public: 32 | ProcessingThread() = delete; 33 | ProcessingThread(ProcessingThread &&o) = delete; 34 | ProcessingThread(vca_param cfg, 35 | MultiThreadQueue &jobs, 36 | MultiThreadQueue &results, 37 | unsigned id); 38 | ~ProcessingThread() = default; 39 | 40 | void abort(); 41 | void join(); 42 | 43 | private: 44 | void threadFunction(MultiThreadQueue &jobQueue, MultiThreadQueue &results); 45 | 46 | std::thread thread; 47 | bool aborted{}; 48 | unsigned id{}; 49 | vca_param cfg; 50 | }; 51 | 52 | } // namespace vca 53 | -------------------------------------------------------------------------------- /source/lib/analyzer/ShotDetection.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #include "ShotDetection.h" 20 | 21 | #include 22 | #include 23 | 24 | namespace { 25 | 26 | inline void log(const vca_shot_detection_param &cfg, LogLevel level, const std::string &message) 27 | { 28 | if (cfg.logFunction) 29 | cfg.logFunction(cfg.logFunctionPrivateData, level, message.c_str()); 30 | } 31 | 32 | void detect(const vca_shot_detection_param ¶m, vca_frame_results *frames, size_t num_frames) 33 | { 34 | struct UnsureFrame 35 | { 36 | size_t index{}; 37 | size_t previousShotDistance{}; 38 | }; 39 | std::vector unsureFrames; 40 | 41 | unsigned numDetectedShots = 0; 42 | 43 | /* First pass */ 44 | size_t prevShotPos = 0; 45 | size_t not_sure_count = 0; 46 | for (size_t i = 2; i < num_frames; i++) 47 | { 48 | if (frames[i].energyEpsilon > param.maxEpsilonThresh) 49 | { 50 | frames[i].isNewShot = true; 51 | prevShotPos = i; 52 | numDetectedShots++; 53 | } 54 | else 55 | { 56 | frames[i].isNewShot = false; 57 | if (frames[i].energyEpsilon >= param.minEpsilonThresh 58 | && frames[i].energyDiff >= param.maxSadThresh) 59 | { 60 | auto previousShotDistance = i - prevShotPos; 61 | unsureFrames.push_back({i, previousShotDistance}); 62 | } 63 | } 64 | } 65 | 66 | log(param, 67 | LogLevel::Debug, 68 | "First pass complete. " + std::to_string(unsureFrames.size()) + " frames not decided yet."); 69 | 70 | for (auto it = unsureFrames.begin(); it != unsureFrames.end(); it++) 71 | { 72 | auto itNext = it + 1; 73 | if (itNext != unsureFrames.end() && it->previousShotDistance >= param.fps 74 | && (itNext->index - it->index) >= param.fps) 75 | { 76 | frames[it->index].isNewShot = true; 77 | numDetectedShots++; 78 | } 79 | 80 | if (it->index == unsureFrames.back().index && it->previousShotDistance >= param.fps 81 | && (it->index + param.fps) <= num_frames) 82 | { 83 | frames[it->index].isNewShot = true; 84 | numDetectedShots++; 85 | } 86 | } 87 | 88 | log(param, LogLevel::Debug, "Detected " + std::to_string(numDetectedShots) + " shots."); 89 | } 90 | 91 | } // namespace 92 | 93 | namespace vca { 94 | 95 | vca_result shot_detection(const vca_shot_detection_param ¶m, 96 | vca_frame_results *frames, 97 | size_t num_frames) 98 | { 99 | log(param, 100 | LogLevel::Info, 101 | "Starting shot detection for " + std::to_string(num_frames) + " frames"); 102 | 103 | frames[0].isNewShot = true; 104 | 105 | if (num_frames < 3) 106 | return vca_result::VCA_OK; 107 | 108 | try 109 | { 110 | detect(param, frames, num_frames); 111 | } 112 | catch (const std::exception &e) 113 | { 114 | std::string exception_str = e.what(); 115 | log(param, LogLevel::Error, "Exception " + exception_str); 116 | return vca_result::VCA_ERROR; 117 | } 118 | 119 | return vca_result::VCA_OK; 120 | } 121 | 122 | } // namespace vca 123 | -------------------------------------------------------------------------------- /source/lib/analyzer/ShotDetection.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | namespace vca { 24 | 25 | vca_result shot_detection(const vca_shot_detection_param ¶m, 26 | vca_frame_results *frames, 27 | size_t num_frames); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /source/lib/analyzer/common/EnumMapper.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | /* This class implement mapping of "enum class" values to and from names (string). 28 | */ 29 | template class EnumMapper 30 | { 31 | public: 32 | struct Entry 33 | { 34 | Entry(T value, std::string name) : value(value), name(name) {} 35 | Entry(T value, std::string name, std::string text) : value(value), name(name), text(text) {} 36 | T value; 37 | std::string name; 38 | std::string text; 39 | }; 40 | 41 | using EntryVector = std::vector; 42 | 43 | EnumMapper() = default; 44 | EnumMapper(const EntryVector &entryVector) : entryVector(entryVector){}; 45 | 46 | std::optional getValue(std::string name, bool isText = false) const 47 | { 48 | for (const auto &entry : this->entryVector) 49 | if ((!isText && entry.name == name) || (isText && entry.text == name)) 50 | return entry.value; 51 | return {}; 52 | } 53 | 54 | std::string getName(T value) const 55 | { 56 | for (const auto &entry : this->entryVector) 57 | if (entry.value == value) 58 | return entry.name; 59 | throw std::logic_error( 60 | "The given type T was not registered in the mapper. All possible enums must be mapped."); 61 | } 62 | 63 | std::string getText(T value) const 64 | { 65 | for (const auto &entry : this->entryVector) 66 | if (entry.value == value) 67 | return entry.text; 68 | throw std::logic_error( 69 | "The given type T was not registered in the mapper. All possible enums must be mapped."); 70 | } 71 | 72 | size_t indexOf(T value) const 73 | { 74 | for (size_t i = 0; i < this->entryVector.size(); i++) 75 | if (this->entryVector.at(i).value == value) 76 | return i; 77 | throw std::logic_error( 78 | "The given type T was not registered in the mapper. All possible enums must be mapped."); 79 | } 80 | 81 | std::optional at(size_t index) const 82 | { 83 | if (index >= this->entryVector.size()) 84 | return {}; 85 | return this->entryVector.at(index).value; 86 | } 87 | 88 | std::vector getEnums() const 89 | { 90 | std::vector m; 91 | for (const auto &entry : this->entryVector) 92 | m.push_back(entry.value); 93 | return m; 94 | } 95 | 96 | std::vector getNames() const 97 | { 98 | std::vector l; 99 | for (const auto &entry : this->entryVector) 100 | l.push_back(entry.name); 101 | return l; 102 | } 103 | 104 | std::vector getTextEntries() const 105 | { 106 | std::vector l; 107 | for (const auto &entry : this->entryVector) 108 | l.push_back(entry.text); 109 | return l; 110 | } 111 | 112 | size_t size() const { return this->entryVector.size(); } 113 | 114 | const EntryVector &entries() const { return this->entryVector; } 115 | 116 | private: 117 | EntryVector entryVector; 118 | }; 119 | -------------------------------------------------------------------------------- /source/lib/analyzer/common/common.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | namespace vca { 30 | 31 | #if defined(__GNUC__) 32 | #define ALIGN_VAR_32(T, var) T var __attribute__((aligned(32))) 33 | #elif defined(_MSC_VER) 34 | #define ALIGN_VAR_32(T, var) __declspec(align(32)) T var 35 | #endif 36 | 37 | const auto CpuSimdMapper = EnumMapper({{CpuSimd::None, "NoSimd"}, 38 | {CpuSimd::SSE2, "SSE2"}, 39 | {CpuSimd::SSSE3, "SSSE3"}, 40 | {CpuSimd::SSE4, "SSE4"}, 41 | {CpuSimd::AVX2, "AVX2"}}); 42 | 43 | inline void log(const vca_param &cfg, LogLevel level, const std::string &message) 44 | { 45 | static std::mutex loggingMutex; 46 | std::unique_lock lock(loggingMutex); 47 | if (cfg.logFunction) 48 | cfg.logFunction(cfg.logFunctionPrivateData, level, message.c_str()); 49 | } 50 | 51 | inline std::pair getFrameSizeInBlocks(unsigned blockSize, 52 | const vca_frame_info &info) 53 | { 54 | auto widthInBlocks = (info.width + blockSize - 1) / blockSize; 55 | auto heightInBlock = (info.height + blockSize - 1) / blockSize; 56 | return {widthInBlocks, heightInBlock}; 57 | } 58 | 59 | inline std::pair getChromaFrameSizeInBlocks(unsigned blockSize, 60 | int width, 61 | int height) 62 | { 63 | auto widthInBlocks = (width + blockSize - 1) / blockSize; 64 | auto heightInBlock = (height + blockSize - 1) / blockSize; 65 | return {widthInBlocks, heightInBlock}; 66 | } 67 | 68 | struct MacroblockRange 69 | { 70 | unsigned start{}; 71 | unsigned end{}; 72 | }; 73 | 74 | struct Job 75 | { 76 | vca_frame *frame; 77 | MacroblockRange macroblockRange; 78 | unsigned jobID; 79 | 80 | std::string infoString() 81 | { 82 | return "Job " + std::to_string(this->jobID) + " POC " 83 | + std::to_string(this->frame->stats.poc) + " MB " 84 | + std::to_string(macroblockRange.start) + "-" + std::to_string(macroblockRange.end); 85 | } 86 | }; 87 | 88 | struct Result 89 | { 90 | std::vector brightnessPerBlock; 91 | std::vector energyPerBlock; 92 | std::vector energyDiffPerBlock; 93 | std::vector energyEpsilonPerBlock; 94 | std::vector averageUPerBlock; 95 | std::vector averageVPerBlock; 96 | std::vector energyUPerBlock; 97 | std::vector energyVPerBlock; 98 | uint32_t averageBrightness{}; 99 | uint32_t averageEnergy{}; 100 | uint32_t averageU{}; 101 | uint32_t averageV{}; 102 | uint32_t energyU{}; 103 | uint32_t energyV{}; 104 | double energyDiff{}; 105 | double energyEpsilon{}; 106 | 107 | std::vector entropyPerBlock; 108 | std::vector entropyDiffPerBlock; 109 | std::vector entropyUPerBlock; 110 | std::vector entropyVPerBlock; 111 | double entropyY{}; 112 | double entropyU{}; 113 | double entropyV{}; 114 | double entropyDiff{}; 115 | double entropyEpsilon{}; 116 | 117 | std::vector edgeDensityPerBlock; 118 | double averageEdgeDensity{}; 119 | 120 | int poc{}; 121 | unsigned jobID{}; 122 | }; 123 | 124 | } // namespace vca 125 | -------------------------------------------------------------------------------- /source/lib/analyzer/simd/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # We assemble the same code 3 times with different settings for BIT_DEPTH 2 | # Each compilation will create functions with different names that we can 3 | # then all link to the library 4 | 5 | add_library(vcaLibSimd8bit STATIC "") 6 | add_library(vcaLibSimd10bit STATIC "") 7 | add_library(vcaLibSimd12bit STATIC "") 8 | 9 | target_include_directories(vcaLibSimd8bit PRIVATE ${LIB_SOURCE_DIR}) 10 | target_include_directories(vcaLibSimd10bit PRIVATE ${LIB_SOURCE_DIR}) 11 | target_include_directories(vcaLibSimd12bit PRIVATE ${LIB_SOURCE_DIR}) 12 | 13 | if(BUILD_WITH_NASM) 14 | enable_language(ASM_NASM) 15 | 16 | target_sources(vcaLibSimd8bit 17 | PRIVATE 18 | dct8.asm 19 | const-a.asm 20 | cpu-a.asm 21 | dct-ssse3.cpp 22 | entropy.cpp 23 | ) 24 | target_sources(vcaLibSimd10bit 25 | PRIVATE 26 | dct8.asm 27 | const-a.asm 28 | cpu-a.asm 29 | dct-ssse3.cpp 30 | entropy.cpp 31 | ) 32 | target_sources(vcaLibSimd12bit 33 | PRIVATE 34 | dct8.asm 35 | const-a.asm 36 | cpu-a.asm 37 | dct-ssse3.cpp 38 | entropy.cpp 39 | ) 40 | 41 | set_property(TARGET vcaLibSimd8bit PROPERTY COMPILE_FLAGS -DBIT_DEPTH=8) 42 | set_property(TARGET vcaLibSimd10bit PROPERTY COMPILE_FLAGS -DBIT_DEPTH=10) 43 | set_property(TARGET vcaLibSimd12bit PROPERTY COMPILE_FLAGS -DBIT_DEPTH=12) 44 | 45 | if(APPLE) 46 | set(CMAKE_ASM_NASM_FLAGS "-I\"${CMAKE_CURRENT_SOURCE_DIR}/\" -DPIC -DARCH_X86_64=1 -DPREFIX -DVCA_NS=vca") 47 | else() 48 | set(CMAKE_ASM_NASM_FLAGS "-I\"${CMAKE_CURRENT_SOURCE_DIR}/\" -DPIC -DARCH_X86_64=1 -DVCA_NS=vca") 49 | endif() 50 | 51 | if(GCC) 52 | set_source_files_properties(dct-ssse3.cpp PROPERTIES COMPILE_FLAGS "-mssse3") 53 | set_source_files_properties(entropy.cpp PROPERTIES COMPILE_FLAGS "-mssse3") 54 | endif(GCC) 55 | else() 56 | target_sources(vcaLibSimd8bit 57 | PRIVATE 58 | noAsmImpl8bit.cpp 59 | ) 60 | target_sources(vcaLibSimd10bit 61 | PRIVATE 62 | noAsmImpl10bit.cpp 63 | ) 64 | target_sources(vcaLibSimd12bit 65 | PRIVATE 66 | noAsmImpl12bit.cpp 67 | ) 68 | endif(BUILD_WITH_NASM) 69 | -------------------------------------------------------------------------------- /source/lib/analyzer/simd/README.txt: -------------------------------------------------------------------------------- 1 | The ASM source here is directly pulled from the x265 project. 2 | -------------------------------------------------------------------------------- /source/lib/analyzer/simd/const-a.asm: -------------------------------------------------------------------------------- 1 | ;***************************************************************************** 2 | ;* const-a.asm: x86 global constants 3 | ;***************************************************************************** 4 | ;* Copyright (C) 2003-2013 x264 project 5 | ;* Copyright (C) 2024 Christian Doppler Laboratory ATHENA 6 | ;* 7 | ;* Authors: Loren Merritt 8 | ;* Fiona Glaser 9 | ;* Min Chen 10 | ;* Praveen Kumar Tiwari 11 | ;* This program is free software; you can redistribute it and/or modify 12 | ;* it under the terms of the GNU General Public License as published by 13 | ;* the Free Software Foundation; either version 3 of the License, or 14 | ;* (at your option) any later version. 15 | ;* 16 | ;* This program is distributed in the hope that it will be useful, 17 | ;* but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | ;* GNU General Public License for more details. 20 | ;* 21 | ;* You should have received a copy of the GNU General Public License 22 | ;* along with this program. 23 | ;***************************************************************************** 24 | 25 | %include "x86inc.asm" 26 | 27 | SECTION_RODATA 64 28 | 29 | ;; 8-bit constants 30 | 31 | const pb_0, times 32 db 0 32 | const pb_1, times 32 db 1 33 | const pb_2, times 32 db 2 34 | const pb_3, times 32 db 3 35 | const pb_4, times 32 db 4 36 | const pb_8, times 32 db 8 37 | const pb_15, times 32 db 15 38 | const pb_16, times 32 db 16 39 | const pb_31, times 32 db 31 40 | const pb_32, times 32 db 32 41 | const pb_64, times 32 db 64 42 | const pb_124, times 32 db 124 43 | const pb_128, times 32 db 128 44 | const pb_a1, times 16 db 0xa1 45 | 46 | const pb_01, times 8 db 0, 1 47 | const pb_0123, times 4 db 0, 1 48 | times 4 db 2, 3 49 | const hsub_mul, times 16 db 1, -1 50 | const pw_swap, times 2 db 6, 7, 4, 5, 2, 3, 0, 1 51 | const pb_unpackbd1, times 2 db 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3 52 | const pb_unpackbd2, times 2 db 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7 53 | const pb_unpackwq1, times 1 db 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3 54 | const pb_unpackwq2, times 1 db 4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7 55 | const pb_shuf8x8c, times 1 db 0, 0, 0, 0, 2, 2, 2, 2, 4, 4, 4, 4, 6, 6, 6, 6 56 | const pb_movemask, times 16 db 0x00 57 | times 16 db 0xFF 58 | 59 | const pb_movemask_32, times 32 db 0x00 60 | times 32 db 0xFF 61 | times 32 db 0x00 62 | 63 | const pb_0000000000000F0F, times 2 db 0xff, 0x00 64 | times 12 db 0x00 65 | const pb_000000000000000F, db 0xff 66 | times 15 db 0x00 67 | const pb_shuf_off4, times 2 db 0, 4, 1, 5, 2, 6, 3, 7 68 | const pw_shuf_off4, times 1 db 0, 1, 8, 9, 2, 3, 10, 11, 4, 5, 12, 13, 6, 7, 14, 15 69 | 70 | ;; 16-bit constants 71 | 72 | const pw_n1, times 16 dw -1 73 | const pw_1, times 16 dw 1 74 | const pw_2, times 16 dw 2 75 | const pw_3, times 16 dw 3 76 | const pw_7, times 16 dw 7 77 | const pw_m2, times 8 dw -2 78 | const pw_4, times 8 dw 4 79 | const pw_8, times 8 dw 8 80 | const pw_16, times 16 dw 16 81 | const pw_15, times 16 dw 15 82 | const pw_31, times 16 dw 31 83 | const pw_32, times 16 dw 32 84 | const pw_64, times 8 dw 64 85 | const pw_128, times 16 dw 128 86 | const pw_256, times 16 dw 256 87 | const pw_257, times 16 dw 257 88 | const pw_512, times 16 dw 512 89 | const pw_1023, times 16 dw 1023 90 | const pw_1024, times 16 dw 1024 91 | const pw_2048, times 16 dw 2048 92 | const pw_4096, times 16 dw 4096 93 | const pw_8192, times 8 dw 8192 94 | const pw_00ff, times 16 dw 0x00ff 95 | const pw_ff00, times 8 dw 0xff00 96 | const pw_2000, times 16 dw 0x2000 97 | const pw_8000, times 8 dw 0x8000 98 | const pw_3fff, times 16 dw 0x3fff 99 | const pw_32_0, times 4 dw 32, 100 | times 4 dw 0 101 | const pw_pixel_max, times 16 dw ((1 << BIT_DEPTH)-1) 102 | 103 | const pw_0_7, times 2 dw 0, 1, 2, 3, 4, 5, 6, 7 104 | const pw_ppppmmmm, times 1 dw 1, 1, 1, 1, -1, -1, -1, -1 105 | const pw_ppmmppmm, times 1 dw 1, 1, -1, -1, 1, 1, -1, -1 106 | const pw_pmpmpmpm, times 16 dw 1, -1, 1, -1, 1, -1, 1, -1 107 | const pw_pmmpzzzz, times 1 dw 1, -1, -1, 1, 0, 0, 0, 0 108 | const multi_2Row, times 1 dw 1, 2, 3, 4, 1, 2, 3, 4 109 | const multiH, times 1 dw 9, 10, 11, 12, 13, 14, 15, 16 110 | const multiH3, times 1 dw 25, 26, 27, 28, 29, 30, 31, 32 111 | const multiL, times 1 dw 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 112 | const multiH2, times 1 dw 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 113 | const pw_planar16_mul, times 1 dw 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 114 | const pw_planar32_mul, times 1 dw 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16 115 | const pw_FFFFFFFFFFFFFFF0, dw 0x00 116 | times 7 dw 0xff 117 | const hmul_16p, times 16 db 1 118 | times 8 db 1, -1 119 | const pw_exp2_0_15, dw 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 120 | const pw_1_ffff, times 4 dw 1 121 | times 4 dw 0xFFFF 122 | 123 | 124 | ;; 32-bit constants 125 | 126 | const pd_1, times 8 dd 1 127 | const pd_2, times 8 dd 2 128 | const pd_4, times 4 dd 4 129 | const pd_8, times 4 dd 8 130 | const pd_15, times 8 dd 15 131 | const pd_16, times 8 dd 16 132 | const pd_31, times 8 dd 31 133 | const pd_32, times 8 dd 32 134 | const pd_64, times 4 dd 64 135 | const pd_128, times 4 dd 128 136 | const pd_256, times 4 dd 256 137 | const pd_512, times 4 dd 512 138 | const pd_1024, times 4 dd 1024 139 | const pd_2048, times 4 dd 2048 140 | const pd_ffff, times 4 dd 0xffff 141 | const pd_32767, times 4 dd 32767 142 | const pd_524416, times 4 dd 524416 143 | const pd_n32768, times 8 dd 0xffff8000 144 | const pd_n131072, times 4 dd 0xfffe0000 145 | const pd_0000ffff, times 8 dd 0x0000FFFF 146 | const pd_planar16_mul0, times 1 dd 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 147 | const pd_planar16_mul1, times 1 dd 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 148 | const pd_planar32_mul1, times 1 dd 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16 149 | const pd_planar32_mul2, times 1 dd 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 150 | const pd_planar16_mul2, times 1 dd 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 151 | const trans8_shuf, times 1 dd 0, 4, 1, 5, 2, 6, 3, 7 152 | 153 | ;; 64-bit constants 154 | 155 | const pq_1, times 1 dq 1 156 | -------------------------------------------------------------------------------- /source/lib/analyzer/simd/cpu-a.asm: -------------------------------------------------------------------------------- 1 | ;***************************************************************************** 2 | ;* cpu-a.asm: x86 cpu utilities 3 | ;***************************************************************************** 4 | ;* Copyright (C) 2003-2013 x264 project 5 | ;* Copyright (C) 2024 Christian Doppler Laboratory ATHENA 6 | ;* 7 | ;* Authors: Laurent Aimar 8 | ;* Loren Merritt 9 | ;* Fiona Glaser 10 | ;* 11 | ;* This program is free software; you can redistribute it and/or modify 12 | ;* it under the terms of the GNU General Public License as published by 13 | ;* the Free Software Foundation; either version 3 of the License, or 14 | ;* (at your option) any later version. 15 | ;* 16 | ;* This program is distributed in the hope that it will be useful, 17 | ;* but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | ;* GNU General Public License for more details. 20 | ;* 21 | ;* You should have received a copy of the GNU General Public License 22 | ;* along with this program. 23 | ;***************************************************************************** 24 | 25 | %include "x86inc.asm" 26 | 27 | SECTION .text 28 | 29 | ;----------------------------------------------------------------------------- 30 | ; void cpu_cpuid( int op, int *eax, int *ebx, int *ecx, int *edx ) 31 | ;----------------------------------------------------------------------------- 32 | cglobal cpu_cpuid, 5,7 33 | push rbx 34 | push r4 35 | push r3 36 | push r2 37 | push r1 38 | mov eax, r0d 39 | xor ecx, ecx 40 | cpuid 41 | pop r4 42 | mov [r4], eax 43 | pop r4 44 | mov [r4], ebx 45 | pop r4 46 | mov [r4], ecx 47 | pop r4 48 | mov [r4], edx 49 | pop rbx 50 | RET 51 | 52 | ;----------------------------------------------------------------------------- 53 | ; uint64_t cpu_xgetbv( int xcr ) 54 | ;----------------------------------------------------------------------------- 55 | cglobal cpu_xgetbv 56 | movifnidn ecx, r0m 57 | xgetbv 58 | %if ARCH_X86_64 59 | shl rdx, 32 60 | or rax, rdx 61 | %endif 62 | ret 63 | 64 | %if ARCH_X86_64 65 | 66 | ;----------------------------------------------------------------------------- 67 | ; void stack_align( void (*func)(void*), void *arg ); 68 | ;----------------------------------------------------------------------------- 69 | cglobal stack_align 70 | push rbp 71 | mov rbp, rsp 72 | %if WIN64 73 | sub rsp, 32 ; shadow space 74 | %endif 75 | and rsp, ~(STACK_ALIGNMENT - 1) 76 | mov rax, r0 77 | mov r0, r1 78 | mov r1, r2 79 | mov r2, r3 80 | call rax 81 | leave 82 | ret 83 | 84 | %else 85 | 86 | ;----------------------------------------------------------------------------- 87 | ; int cpu_cpuid_test( void ) 88 | ; return 0 if unsupported 89 | ;----------------------------------------------------------------------------- 90 | cglobal cpu_cpuid_test 91 | pushfd 92 | push ebx 93 | push ebp 94 | push esi 95 | push edi 96 | pushfd 97 | pop eax 98 | mov ebx, eax 99 | xor eax, 0x200000 100 | push eax 101 | popfd 102 | pushfd 103 | pop eax 104 | xor eax, ebx 105 | pop edi 106 | pop esi 107 | pop ebp 108 | pop ebx 109 | popfd 110 | ret 111 | 112 | cglobal stack_align 113 | push ebp 114 | mov ebp, esp 115 | sub esp, 12 116 | and esp, ~(STACK_ALIGNMENT - 1) 117 | mov ecx, [ebp+8] 118 | mov edx, [ebp+12] 119 | mov [esp], edx 120 | mov edx, [ebp+16] 121 | mov [esp+4], edx 122 | mov edx, [ebp+20] 123 | mov [esp+8], edx 124 | call ecx 125 | leave 126 | ret 127 | 128 | %endif 129 | 130 | ;----------------------------------------------------------------------------- 131 | ; void cpu_emms( void ) 132 | ;----------------------------------------------------------------------------- 133 | cglobal cpu_emms 134 | emms 135 | ret 136 | 137 | ;----------------------------------------------------------------------------- 138 | ; void cpu_sfence( void ) 139 | ;----------------------------------------------------------------------------- 140 | cglobal cpu_sfence 141 | sfence 142 | ret 143 | 144 | cextern intel_cpu_indicator_init 145 | 146 | ;----------------------------------------------------------------------------- 147 | ; void safe_intel_cpu_indicator_init( void ); 148 | ;----------------------------------------------------------------------------- 149 | cglobal safe_intel_cpu_indicator_init 150 | push r0 151 | push r1 152 | push r2 153 | push r3 154 | push r4 155 | push r5 156 | push r6 157 | %if ARCH_X86_64 158 | push r7 159 | push r8 160 | push r9 161 | push r10 162 | push r11 163 | push r12 164 | push r13 165 | push r14 166 | %endif 167 | push rbp 168 | mov rbp, rsp 169 | %if WIN64 170 | sub rsp, 32 ; shadow space 171 | %endif 172 | and rsp, ~31 173 | call intel_cpu_indicator_init 174 | leave 175 | %if ARCH_X86_64 176 | pop r14 177 | pop r13 178 | pop r12 179 | pop r11 180 | pop r10 181 | pop r9 182 | pop r8 183 | pop r7 184 | %endif 185 | pop r6 186 | pop r5 187 | pop r4 188 | pop r3 189 | pop r2 190 | pop r1 191 | pop r0 192 | ret 193 | -------------------------------------------------------------------------------- /source/lib/analyzer/simd/cpu.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Loren Merritt 5 | * Laurent Aimar 6 | * Fiona Glaser 7 | * Steve Borho 8 | * 9 | * This program is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation; either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. 21 | *****************************************************************************/ 22 | 23 | #include "cpu.h" 24 | 25 | #include 26 | 27 | #if MACOS || SYS_FREEBSD 28 | #include 29 | #include 30 | 31 | #endif 32 | #if SYS_OPENBSD 33 | #include 34 | #include 35 | #include 36 | 37 | #endif 38 | 39 | namespace vca { 40 | 41 | #if VCA_ARCH_X86 42 | 43 | extern "C" { 44 | #ifdef __INTEL_COMPILER 45 | 46 | /* Agner's patch to Intel's CPU dispatcher from pages 131-132 of 47 | * http://agner.org/optimize/optimizing_cpp.pdf (2011-01-30) 48 | * adapted to vca's cpu schema. */ 49 | 50 | // Global variable indicating cpu 51 | int __intel_cpu_indicator = 0; 52 | // CPU dispatcher function 53 | void vca_intel_cpu_indicator_init(void) 54 | { 55 | uint32_t cpu = vca::cpu_detect(); 56 | if (cpu & VCA_CPU_AVX) 57 | __intel_cpu_indicator = 0x20000; 58 | else if (cpu & VCA_CPU_SSE42) 59 | __intel_cpu_indicator = 0x8000; 60 | else if (cpu & VCA_CPU_SSE4) 61 | __intel_cpu_indicator = 0x2000; 62 | else if (cpu & VCA_CPU_SSSE3) 63 | __intel_cpu_indicator = 0x1000; 64 | else if (cpu & VCA_CPU_SSE3) 65 | __intel_cpu_indicator = 0x800; 66 | else if (cpu & VCA_CPU_SSE2 && !(cpu & VCA_CPU_SSE2_IS_SLOW)) 67 | __intel_cpu_indicator = 0x200; 68 | else if (cpu & VCA_CPU_SSE) 69 | __intel_cpu_indicator = 0x80; 70 | else if (cpu & VCA_CPU_MMX2) 71 | __intel_cpu_indicator = 8; 72 | else 73 | __intel_cpu_indicator = 1; 74 | } 75 | 76 | /* __intel_cpu_indicator_init appears to have a non-standard calling convention that 77 | * assumes certain registers aren't preserved, so we'll route it through a function 78 | * that backs up all the registers. */ 79 | void __intel_cpu_indicator_init(void) 80 | { 81 | vca_safe_intel_cpu_indicator_init(); 82 | } 83 | 84 | #else // ifdef __INTEL_COMPILER 85 | void vca_intel_cpu_indicator_init(void) {} 86 | 87 | #endif // ifdef __INTEL_COMPILER 88 | } 89 | 90 | #if ENABLE_NASM 91 | extern "C" { 92 | /* cpu-a.asm */ 93 | int vca_cpu_cpuid_test(void); 94 | void vca_cpu_cpuid(uint32_t op, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); 95 | uint64_t vca_cpu_xgetbv(int xcr); 96 | } 97 | #endif 98 | 99 | #if defined(_MSC_VER) 100 | #pragma warning(disable : 4309) // truncation of constant value 101 | #endif 102 | 103 | bool isSimdSupported(CpuSimd simd) 104 | { 105 | const auto simdLevelIndex = CpuSimdMapper.indexOf(simd); 106 | const auto maxSupportedSimdLevelIndex = CpuSimdMapper.indexOf(cpuDetectMaxSimd()); 107 | return maxSupportedSimdLevelIndex >= simdLevelIndex; 108 | } 109 | 110 | CpuSimd cpuDetectMaxSimd() 111 | { 112 | auto cpu = CpuSimd::SSSE3; 113 | #if ENABLE_NASM 114 | uint32_t eax, ebx, ecx, edx; 115 | uint32_t vendor[4] = {0}; 116 | uint32_t max_basic_cap; 117 | uint64_t xcr0 = 0; 118 | 119 | #if !X86_64 120 | if (!vca_cpu_cpuid_test()) 121 | return 0; 122 | #endif 123 | 124 | vca_cpu_cpuid(0, &max_basic_cap, vendor + 0, vendor + 2, vendor + 1); 125 | if (max_basic_cap == 0) 126 | return cpu; 127 | 128 | vca_cpu_cpuid(1, &eax, &ebx, &ecx, &edx); 129 | if (!(edx & 0x00800000)) 130 | // Not even mmx supported 131 | return cpu; 132 | if (edx & 0x04000000) 133 | cpu = CpuSimd::SSE2; 134 | if (ecx & 0x00000200) 135 | cpu = CpuSimd::SSSE3; 136 | if (ecx & 0x00080000) 137 | cpu = CpuSimd::SSE4; 138 | 139 | if (max_basic_cap >= 7) 140 | { 141 | xcr0 = vca_cpu_xgetbv(0); 142 | vca_cpu_cpuid(7, &eax, &ebx, &ecx, &edx); 143 | 144 | if ((xcr0 & 0x6) == 0x6) /* XMM/YMM state */ 145 | { 146 | if (ebx & 0x00000020) 147 | cpu = CpuSimd::AVX2; 148 | } 149 | } 150 | #endif 151 | return cpu; 152 | } 153 | 154 | #else 155 | CpuSimd cpuDetectMaxSimd() 156 | { 157 | return CpuSimd::None; 158 | } 159 | #endif // if VCA_ARCH_X86 160 | } // namespace vca 161 | -------------------------------------------------------------------------------- /source/lib/analyzer/simd/cpu.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Loren Merritt 5 | * Steve Borho 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. 19 | *****************************************************************************/ 20 | 21 | #pragma once 22 | 23 | #include 24 | 25 | #define VCA_CPU_SSE2 (1 << 0) 26 | #define VCA_CPU_SSSE3 (1 << 1) 27 | #define VCA_CPU_SSE4 (1 << 2) 28 | #define VCA_CPU_AVX2 (1 << 3) 29 | 30 | // from primitives.cpp 31 | #if ENABLE_NASM 32 | extern "C" void vca_cpu_emms(void); 33 | #endif 34 | 35 | #if _MSC_VER 36 | #include 37 | #define vca_emms() _mm_empty() 38 | #elif __GNUC__ 39 | // Cannot use _mm_empty() directly without compiling all the source with 40 | // a fixed CPU arch, which we would like to avoid at the moment 41 | #define vca_emms() vca_cpu_emms() 42 | #else 43 | #define vca_emms() vca_cpu_emms() 44 | #endif 45 | 46 | namespace vca { 47 | 48 | CpuSimd cpuDetectMaxSimd(); 49 | 50 | bool isSimdSupported(CpuSimd simd); 51 | 52 | struct cpu_name_t 53 | { 54 | char name[16]; 55 | uint32_t flags; 56 | }; 57 | 58 | } // namespace vca 59 | -------------------------------------------------------------------------------- /source/lib/analyzer/simd/dct-ssse3.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Vignesh V Menon 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. 18 | *****************************************************************************/ 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | void vca_dct16_8bit_ssse3(const int16_t *src, int16_t *dst, intptr_t srcStride); 25 | void vca_dct16_10bit_ssse3(const int16_t *src, int16_t *dst, intptr_t srcStride); 26 | void vca_dct16_12bit_ssse3(const int16_t *src, int16_t *dst, intptr_t srcStride); 27 | 28 | void vca_dct32_8bit_ssse3(const int16_t *src, int16_t *dst, intptr_t stride); 29 | void vca_dct32_10bit_ssse3(const int16_t *src, int16_t *dst, intptr_t stride); 30 | void vca_dct32_12bit_ssse3(const int16_t *src, int16_t *dst, intptr_t stride); 31 | -------------------------------------------------------------------------------- /source/lib/analyzer/simd/dct8.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Vignesh V Menon 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. 18 | *****************************************************************************/ 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | extern "C" { 25 | 26 | void vca_dct8_8bit_sse2(const int16_t *src, int16_t *dst, intptr_t srcStride); 27 | void vca_dct8_10bit_sse2(const int16_t *src, int16_t *dst, intptr_t srcStride); 28 | void vca_dct8_12bit_sse2(const int16_t *src, int16_t *dst, intptr_t srcStride); 29 | 30 | void vca_dct8_8bit_sse4(const int16_t *src, int16_t *dst, intptr_t srcStride); 31 | void vca_dct8_10bit_sse4(const int16_t *src, int16_t *dst, intptr_t srcStride); 32 | void vca_dct8_12bit_sse4(const int16_t *src, int16_t *dst, intptr_t srcStride); 33 | 34 | void vca_dct8_8bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride); 35 | void vca_dct8_10bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride); 36 | void vca_dct8_12bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride); 37 | 38 | void vca_dct16_8bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride); 39 | void vca_dct16_10bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride); 40 | void vca_dct16_12bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride); 41 | 42 | void vca_dct32_8bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride); 43 | void vca_dct32_10bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride); 44 | void vca_dct32_12bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride); 45 | } 46 | -------------------------------------------------------------------------------- /source/lib/analyzer/simd/entropy.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Amritha Premkumar 5 | * Prajit T Rajendran 6 | * Vignesh V Menon 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. 20 | *****************************************************************************/ 21 | 22 | #include // Include SIMD intrinsics header for x86 architecture 23 | #include 24 | #include 25 | #include 26 | 27 | // x86 SIMD optimized entropy function 28 | 29 | double entropy_avx2(const std::vector &block) 30 | { 31 | //TODO 32 | double entropy = 0.0; 33 | return entropy; 34 | } 35 | -------------------------------------------------------------------------------- /source/lib/analyzer/simd/entropy.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Amritha Premkumar 5 | * Prajit T Rajendran 6 | * Vignesh V Menon 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. 20 | *****************************************************************************/ 21 | 22 | #pragma once 23 | 24 | #include 25 | 26 | double entropy_avx2(const std::vector &block); 27 | -------------------------------------------------------------------------------- /source/lib/analyzer/simd/noAsmImpl10bit.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Vignesh V Menon 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. 18 | *****************************************************************************/ 19 | 20 | #include "dct8.h" 21 | 22 | #include 23 | 24 | /// In case we have no NASM available or disabled, we use these dummy functions to link (which 25 | /// should never be called) 26 | 27 | extern "C" { 28 | 29 | void vca_dct4_10bit_sse2(const int16_t *src, int16_t *dst, intptr_t srcStride) 30 | { 31 | assert(false); 32 | } 33 | void vca_dct8_10bit_sse2(const int16_t *src, int16_t *dst, intptr_t srcStride) 34 | { 35 | assert(false); 36 | } 37 | void vca_dct8_10bit_sse4(const int16_t *src, int16_t *dst, intptr_t srcStride) 38 | { 39 | assert(false); 40 | } 41 | void vca_dct4_10bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride) 42 | { 43 | assert(false); 44 | } 45 | void vca_dct8_10bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride) 46 | { 47 | assert(false); 48 | } 49 | void vca_dct16_10bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride) 50 | { 51 | assert(false); 52 | } 53 | void vca_dct32_10bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride) 54 | { 55 | assert(false); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /source/lib/analyzer/simd/noAsmImpl12bit.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Vignesh V Menon 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. 18 | *****************************************************************************/ 19 | 20 | #include "dct8.h" 21 | 22 | #include 23 | 24 | /// In case we have no NASM available or disabled, we use these dummy functions to link (which 25 | /// should never be called) 26 | 27 | extern "C" { 28 | 29 | void vca_dct4_12bit_sse2(const int16_t *src, int16_t *dst, intptr_t srcStride) 30 | { 31 | assert(false); 32 | } 33 | void vca_dct8_12bit_sse2(const int16_t *src, int16_t *dst, intptr_t srcStride) 34 | { 35 | assert(false); 36 | } 37 | void vca_dct8_12bit_sse4(const int16_t *src, int16_t *dst, intptr_t srcStride) 38 | { 39 | assert(false); 40 | } 41 | void vca_dct4_12bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride) 42 | { 43 | assert(false); 44 | } 45 | void vca_dct8_12bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride) 46 | { 47 | assert(false); 48 | } 49 | void vca_dct16_12bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride) 50 | { 51 | assert(false); 52 | } 53 | void vca_dct32_12bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride) 54 | { 55 | assert(false); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /source/lib/analyzer/simd/noAsmImpl8bit.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Vignesh V Menon 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. 18 | *****************************************************************************/ 19 | 20 | #include "dct8.h" 21 | 22 | #include 23 | 24 | /// In case we have no NASM available or disabled, we use these dummy functions to link (which 25 | /// should never be called) 26 | 27 | extern "C" { 28 | 29 | void vca_dct4_8bit_sse2(const int16_t *src, int16_t *dst, intptr_t srcStride) 30 | { 31 | assert(false); 32 | } 33 | void vca_dct8_8bit_sse2(const int16_t *src, int16_t *dst, intptr_t srcStride) 34 | { 35 | assert(false); 36 | } 37 | void vca_dct8_8bit_sse4(const int16_t *src, int16_t *dst, intptr_t srcStride) 38 | { 39 | assert(false); 40 | } 41 | void vca_dct4_8bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride) 42 | { 43 | assert(false); 44 | } 45 | void vca_dct8_8bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride) 46 | { 47 | assert(false); 48 | } 49 | void vca_dct16_8bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride) 50 | { 51 | assert(false); 52 | } 53 | void vca_dct32_8bit_avx2(const int16_t *src, int16_t *dst, intptr_t srcStride) 54 | { 55 | assert(false); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /source/lib/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | message(STATUS "Fetching googletest") 4 | 5 | include(FetchContent) 6 | FetchContent_Declare( 7 | googletest 8 | URL https://github.com/google/googletest/archive/58d77fa8070e8cec2dc1ed015d66b454c8d78850.zip 9 | ) 10 | 11 | # For Windows: Prevent overriding the parent project's compiler/linker settings 12 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 13 | FetchContent_MakeAvailable(googletest) 14 | 15 | enable_testing() 16 | 17 | file(GLOB_RECURSE testSourceFiles *.cpp) 18 | 19 | add_executable( 20 | unitTestSuite 21 | ${testSourceFiles} 22 | ) 23 | 24 | target_include_directories(unitTestSuite PRIVATE ${LIB_SOURCE_DIR}) 25 | 26 | target_link_libraries( 27 | unitTestSuite 28 | vcaInternal 29 | GTest::gtest_main 30 | ) 31 | 32 | include(GoogleTest) 33 | gtest_discover_tests(unitTestSuite) 34 | -------------------------------------------------------------------------------- /source/lib/test/DCTTestForwardBackwards.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | namespace { 30 | 31 | constexpr auto MAX_BLOCKSIZE_SAMPLES = 32 * 32; 32 | constexpr auto MAX_BLOCKSIZE_BYTES = MAX_BLOCKSIZE_SAMPLES * 2; 33 | 34 | void assertUnusedValuesAreZero(int16_t *data, const unsigned blockSize) 35 | { 36 | const auto nrUsedPixels = blockSize * blockSize; 37 | for (unsigned i = nrUsedPixels; i < MAX_BLOCKSIZE_SAMPLES; i++) 38 | ASSERT_EQ(data[i], 0); 39 | } 40 | 41 | void assertUsedValuesContainNonZeroValues(int16_t *data, const unsigned blockSize) 42 | { 43 | const auto nrUsedPixels = blockSize * blockSize; 44 | for (unsigned i = 0; i < nrUsedPixels; i++) 45 | if (data[i] != 0) 46 | return; 47 | FAIL(); 48 | } 49 | 50 | std::tuple calculateMeanSquareErrorAndMaxDiff(int16_t *data1, 51 | int16_t *data2, 52 | const unsigned blockSize) 53 | { 54 | double sumOfSquaredError = 0.0; 55 | const auto nrUsedPixels = blockSize * blockSize; 56 | int maxDiff = 0; 57 | for (unsigned i = 0; i < nrUsedPixels; i++) 58 | { 59 | const auto diff = data2[i] - data1[i]; 60 | if (diff > maxDiff) 61 | maxDiff = diff; 62 | sumOfSquaredError += static_cast(diff * diff); 63 | } 64 | return {sumOfSquaredError / nrUsedPixels, maxDiff}; 65 | } 66 | 67 | } // namespace 68 | 69 | using BlockSize = unsigned; 70 | using BitDepth = unsigned; 71 | using MSE = double; 72 | using MaxDiff = int; 73 | using TestCase = std::tuple; 74 | 75 | class DCTTestForwardBackwardsFixture : public testing::TestWithParam 76 | { 77 | public: 78 | static std::string generateName(const ::testing::TestParamInfo &info) 79 | { 80 | const auto blockSize = std::get<0>(info.param); 81 | const auto bitDepth = std::get<1>(info.param); 82 | const auto cpuSimd = std::get<2>(info.param); 83 | return "BlockSize" + std::to_string(blockSize) + "_BitDpeht" + std::to_string(bitDepth) 84 | + "_" + vca::CpuSimdMapper.getName(cpuSimd); 85 | } 86 | }; 87 | 88 | TEST_P(DCTTestForwardBackwardsFixture, TransformTest) 89 | { 90 | const auto param = GetParam(); 91 | 92 | const auto blockSize = std::get<0>(param); 93 | const auto bitDepth = std::get<1>(param); 94 | const auto cpuSimd = std::get<2>(param); 95 | const auto enableLowpassDCT = false; 96 | 97 | if (!vca::isSimdSupported(cpuSimd)) 98 | GTEST_SKIP() << "Skipping testing of " << vca::CpuSimdMapper.getName(cpuSimd) 99 | << " because it is not supported on this platform."; 100 | 101 | ALIGN_VAR_32(int16_t, pixelBuffer[MAX_BLOCKSIZE_SAMPLES]); 102 | ALIGN_VAR_32(int16_t, coeffBuffer[MAX_BLOCKSIZE_SAMPLES]); 103 | ALIGN_VAR_32(int16_t, reconstructedPixels[MAX_BLOCKSIZE_SAMPLES]); 104 | 105 | std::memset(pixelBuffer, 0, MAX_BLOCKSIZE_BYTES); 106 | std::memset(coeffBuffer, 0, MAX_BLOCKSIZE_BYTES); 107 | std::memset(reconstructedPixels, 0, MAX_BLOCKSIZE_BYTES); 108 | 109 | test::fillBlockWithRandomData(pixelBuffer, blockSize, bitDepth); 110 | assertUnusedValuesAreZero(pixelBuffer, blockSize); 111 | 112 | vca::performDCT(blockSize, bitDepth, pixelBuffer, coeffBuffer, cpuSimd, enableLowpassDCT); 113 | assertUnusedValuesAreZero(coeffBuffer, blockSize); 114 | assertUsedValuesContainNonZeroValues(coeffBuffer, blockSize); 115 | 116 | test::performIDCT(blockSize, bitDepth, coeffBuffer, reconstructedPixels); 117 | assertUnusedValuesAreZero(coeffBuffer, blockSize); 118 | const auto [mse, maxDiff] = calculateMeanSquareErrorAndMaxDiff(pixelBuffer, 119 | reconstructedPixels, 120 | blockSize); 121 | 122 | // I got this table by experimentation (see commented code below). 123 | // There is probably a theoretical maximum error value one can calculate for this 124 | // forward/bacward transform combination. 125 | using BlockSizeAndBitDepth = std::tuple; 126 | using MseAndDiff = std::tuple; 127 | const std::map ExpectedMaximumValues( 128 | {{{BlockSize(8u), BitDepth(8u)}, {MSE(0.2), MaxDiff(1)}}, 129 | {{BlockSize(8u), BitDepth(10u)}, {MSE(2.0), MaxDiff(3)}}, 130 | {{BlockSize(8u), BitDepth(12u)}, {MSE(25.0), MaxDiff(13)}}, 131 | {{BlockSize(16u), BitDepth(8u)}, {MSE(0.5), MaxDiff(2)}}, 132 | {{BlockSize(16u), BitDepth(10u)}, {MSE(7.0), MaxDiff(8)}}, 133 | {{BlockSize(16u), BitDepth(12u)}, {MSE(90.0), MaxDiff(32)}}, 134 | {{BlockSize(32u), BitDepth(8u)}, {MSE(0.5), MaxDiff(3)}}, 135 | {{BlockSize(32u), BitDepth(10u)}, {MSE(6.0), MaxDiff(10)}}, 136 | {{BlockSize(32u), BitDepth(12u)}, {MSE(90.0), MaxDiff(41)}}}); 137 | 138 | const auto [maxExpectedMSE, maxExpectedDiff] = ExpectedMaximumValues.at({blockSize, bitDepth}); 139 | ASSERT_LE(maxDiff, maxExpectedDiff); 140 | ASSERT_LE(mse, maxExpectedMSE); 141 | } 142 | 143 | INSTANTIATE_TEST_SUITE_P( 144 | DCRTransformTest, 145 | DCTTestForwardBackwardsFixture, 146 | testing::Combine( 147 | testing::ValuesIn({BlockSize(8u), BlockSize(16u), BlockSize(32u)}), 148 | testing::ValuesIn({BitDepth(8u), BitDepth(10u), BitDepth(12u)}), 149 | testing::ValuesIn( 150 | {CpuSimd::None, CpuSimd::SSE2, CpuSimd::SSSE3, CpuSimd::SSE4, CpuSimd::AVX2})), 151 | &DCTTestForwardBackwardsFixture::generateName); 152 | 153 | // This code was used to get the results table above. 154 | // TEST(TransformTest, TestMaxMSE) 155 | // { 156 | // const auto cpuSimd = CpuSimd::None; 157 | // const auto enableLowpassDCT = false; 158 | 159 | // ALIGN_VAR_32(int16_t, pixelBuffer[MAX_BLOCKSIZE_SAMPLES]); 160 | // ALIGN_VAR_32(int16_t, coeffBuffer[MAX_BLOCKSIZE_SAMPLES]); 161 | // ALIGN_VAR_32(int16_t, reconstructedPixels[MAX_BLOCKSIZE_SAMPLES]); 162 | 163 | // for (const auto blockSize : {8, 16, 32}) 164 | // { 165 | // for (const auto bitDepth : {8, 10, 12}) 166 | // { 167 | // const auto cpuSimd = CpuSimd::None; 168 | // auto maxMSE = 0.0; 169 | // auto maxDiff = 0; 170 | // for (int i = 0; i < 10000; i++) 171 | // { 172 | // std::memset(pixelBuffer, 0, MAX_BLOCKSIZE_BYTES); 173 | // std::memset(coeffBuffer, 0, MAX_BLOCKSIZE_BYTES); 174 | // std::memset(reconstructedPixels, 0, MAX_BLOCKSIZE_BYTES); 175 | 176 | // test::fillBlockWithRandomData(pixelBuffer, blockSize, bitDepth); 177 | // assertUnusedValuesAreZero(pixelBuffer, blockSize); 178 | 179 | // vca::performDCT(blockSize, 180 | // bitDepth, 181 | // pixelBuffer, 182 | // coeffBuffer, 183 | // cpuSimd, 184 | // enableLowpassDCT); 185 | // assertUnusedValuesAreZero(coeffBuffer, blockSize); 186 | // assertUsedValuesContainNonZeroValues(coeffBuffer, blockSize); 187 | 188 | // test::performIDCT(blockSize, bitDepth, coeffBuffer, reconstructedPixels); 189 | // assertUnusedValuesAreZero(coeffBuffer, blockSize); 190 | // const auto [mse, maxDiffForIteration] 191 | // = calculateMeanSquareErrorAndMaxDiff(pixelBuffer, 192 | // reconstructedPixels, 193 | // blockSize); 194 | // if (mse > maxMSE) 195 | // maxMSE = mse; 196 | // if (maxDiffForIteration > maxDiff) 197 | // maxDiff = maxDiffForIteration; 198 | // } 199 | 200 | // std::cout << "BlockSize " << blockSize << " BitDepth " << bitDepth << " " 201 | // << vca::CpuSimdMapper.getName(cpuSimd) << " MaxMSE " << maxMSE << " maxDiff " 202 | // << maxDiff << "\n"; 203 | // } 204 | // } 205 | // } 206 | -------------------------------------------------------------------------------- /source/lib/test/DCTTestImplementationsIdenticalOutput.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | namespace { 29 | 30 | constexpr auto MAX_BLOCKSIZE_SAMPLES = 32 * 32; 31 | constexpr auto MAX_BLOCKSIZE_BYTES = MAX_BLOCKSIZE_SAMPLES * 2; 32 | 33 | void assertUsedValuesAreIdentical(int16_t *data1, int16_t *data2, const unsigned blockSize) 34 | { 35 | const auto nrUsedPixels = blockSize * blockSize; 36 | for (unsigned i = 0; i < nrUsedPixels; i++) 37 | ASSERT_EQ(data1[i], data2[i]); 38 | } 39 | 40 | } // namespace 41 | 42 | using BlockSize = unsigned; 43 | using BitDepth = unsigned; 44 | using TestCase = std::tuple; 45 | 46 | class DCTTestImplementationsIdenticalOutputFixture : public testing::TestWithParam 47 | { 48 | public: 49 | static std::string generateName(const ::testing::TestParamInfo &info) 50 | { 51 | const auto blockSize = std::get<0>(info.param); 52 | const auto bitDepth = std::get<1>(info.param); 53 | return "BlockSize" + std::to_string(blockSize) + "_BitDpeht" + std::to_string(bitDepth); 54 | } 55 | }; 56 | 57 | TEST_P(DCTTestImplementationsIdenticalOutputFixture, 58 | TestThatAllImplementationsProduceIdenticalResults) 59 | { 60 | const auto param = GetParam(); 61 | 62 | const auto blockSize = std::get<0>(param); 63 | const auto bitDepth = std::get<1>(param); 64 | const auto enableLowpassDCT = false; 65 | 66 | ALIGN_VAR_32(int16_t, pixelBuffer[MAX_BLOCKSIZE_SAMPLES]); 67 | ALIGN_VAR_32(int16_t, coeffBufferNative[MAX_BLOCKSIZE_SAMPLES]); 68 | ALIGN_VAR_32(int16_t, coeffBufferTest[MAX_BLOCKSIZE_SAMPLES]); 69 | 70 | std::memset(pixelBuffer, 0, MAX_BLOCKSIZE_BYTES); 71 | std::memset(coeffBufferNative, 0, MAX_BLOCKSIZE_BYTES); 72 | std::memset(coeffBufferTest, 0, MAX_BLOCKSIZE_BYTES); 73 | 74 | test::fillBlockWithRandomData(pixelBuffer, blockSize, bitDepth); 75 | vca::performDCT(blockSize, 76 | bitDepth, 77 | pixelBuffer, 78 | coeffBufferNative, 79 | CpuSimd::None, 80 | enableLowpassDCT); 81 | 82 | for (const auto cpuSimd : {CpuSimd::SSE2, CpuSimd::SSSE3, CpuSimd::SSE4, CpuSimd::AVX2}) 83 | { 84 | if (!vca::isSimdSupported(cpuSimd)) 85 | { 86 | std::cout << "Skipping testing of " << vca::CpuSimdMapper.getName(cpuSimd) 87 | << " because it is not supported on this platform."; 88 | continue; 89 | } 90 | 91 | vca::performDCT(blockSize, bitDepth, pixelBuffer, coeffBufferTest, cpuSimd, enableLowpassDCT); 92 | assertUsedValuesAreIdentical(coeffBufferNative, coeffBufferTest, blockSize); 93 | } 94 | } 95 | 96 | INSTANTIATE_TEST_SUITE_P( 97 | DCRTransformTest, 98 | DCTTestImplementationsIdenticalOutputFixture, 99 | testing::Combine(testing::ValuesIn({BlockSize(8u), BlockSize(16u), BlockSize(32u)}), 100 | testing::ValuesIn({BitDepth(8u), BitDepth(10u), BitDepth(12u)})), 101 | &DCTTestImplementationsIdenticalOutputFixture::generateName); 102 | -------------------------------------------------------------------------------- /source/lib/test/InverseDCTNative.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | namespace test { 24 | 25 | void performIDCT(const unsigned blockSize, 26 | const unsigned bitDepth, 27 | int16_t *coeffBuffer, 28 | int16_t *pixelBuffer); 29 | 30 | } // namespace test -------------------------------------------------------------------------------- /source/lib/test/common/functions.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #include "functions.h" 20 | 21 | #include 22 | 23 | namespace test { 24 | 25 | void fillBlockWithRandomData(int16_t *data, const unsigned blockSize, const unsigned bitDepth) 26 | { 27 | const auto nrPixels = blockSize * blockSize; 28 | const auto maxValue = (1 << bitDepth) - 1; 29 | 30 | static std::random_device randomDevice; 31 | static std::default_random_engine randomEngine(randomDevice()); 32 | 33 | std::uniform_int_distribution uniform_dist(0, maxValue); 34 | 35 | for (size_t i = 0; i < nrPixels; i++) 36 | data[i] = int16_t(uniform_dist(randomEngine)); 37 | } 38 | 39 | } // namespace test -------------------------------------------------------------------------------- /source/lib/test/common/functions.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2024 Christian Doppler Laboratory ATHENA 2 | * 3 | * Authors: Christian Feldmann 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. 17 | *****************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | namespace test { 27 | 28 | void fillBlockWithRandomData(int16_t *data, const unsigned blockSize, const unsigned bitDepth); 29 | 30 | } // namespace test 31 | -------------------------------------------------------------------------------- /source/lib/vcaLib.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Vignesh V Menon 5 | * Christian Feldmann 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. 19 | *****************************************************************************/ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #define XSTR(x) STR(x) 26 | #define STR(x) #x 27 | 28 | DLL_PUBLIC vca_analyzer *vca_analyzer_open(vca_param param) 29 | { 30 | try 31 | { 32 | auto newAnalyzer = new vca::Analyzer(param); 33 | return newAnalyzer; 34 | } 35 | catch (const std::exception &) 36 | { 37 | return nullptr; 38 | } 39 | } 40 | 41 | DLL_PUBLIC vca_result vca_analyzer_push(vca_analyzer *enc, vca_frame *frame) 42 | { 43 | if (enc == nullptr) 44 | return vca_result::VCA_ERROR; 45 | 46 | auto analyzer = (vca::Analyzer *) enc; 47 | if (analyzer == nullptr) 48 | return vca_result::VCA_ERROR; 49 | 50 | return analyzer->pushFrame(frame); 51 | } 52 | 53 | DLL_PUBLIC bool vca_result_available(vca_analyzer *enc) 54 | { 55 | auto analyzer = (vca::Analyzer *) (enc); 56 | return analyzer->resultAvailable(); 57 | } 58 | 59 | DLL_PUBLIC vca_result vca_analyzer_pull_frame_result(vca_analyzer *enc, vca_frame_results *result) 60 | { 61 | if (enc == nullptr || result == nullptr) 62 | return vca_result::VCA_ERROR; 63 | 64 | auto analyzer = (vca::Analyzer *) (enc); 65 | if (analyzer == nullptr) 66 | return vca_result::VCA_ERROR; 67 | 68 | return analyzer->pullResult(result); 69 | } 70 | 71 | DLL_PUBLIC void vca_analyzer_close(vca_analyzer *enc) 72 | { 73 | auto analyzer = (vca::Analyzer *) enc; 74 | delete analyzer; 75 | } 76 | 77 | DLL_PUBLIC vca_result vca_shot_detection(const vca_shot_detection_param ¶m, 78 | vca_frame_results *frames, 79 | size_t num_frames) 80 | { 81 | if (frames == nullptr) 82 | return vca_result::VCA_ERROR; 83 | 84 | if (num_frames == 0) 85 | return vca_result::VCA_OK; 86 | 87 | return vca::shot_detection(param, frames, num_frames); 88 | } 89 | 90 | const char *vca_version_str = XSTR(VCA_VERSION); 91 | -------------------------------------------------------------------------------- /source/lib/vcaLib.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Vignesh V Menon 5 | * Christian Feldmann 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. 19 | *****************************************************************************/ 20 | 21 | /// This is the public interface for the bitstream lib 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | #if defined(_MSC_VER) && !defined(VCA_STATIC_BUILD) 29 | #define DLL_PUBLIC __declspec(dllexport) 30 | #else 31 | #define DLL_PUBLIC __attribute__((__visibility__("default"))) 32 | #endif 33 | 34 | extern "C" { 35 | 36 | /* vca_analyzer: 37 | * opaque handler for analyzer */ 38 | typedef void vca_analyzer; 39 | 40 | /* vca_picyuv: 41 | * opaque handler for PicYuv */ 42 | typedef struct vca_picyuv vca_picyuv; 43 | 44 | enum class LogLevel 45 | { 46 | Error, 47 | Warning, 48 | Info, 49 | Debug 50 | }; 51 | 52 | enum class CpuSimd 53 | { 54 | Autodetect, 55 | None, 56 | SSE2, 57 | SSSE3, 58 | SSE4, 59 | AVX2 60 | }; 61 | 62 | enum class vca_colorSpace 63 | { 64 | YUV400, 65 | YUV420, 66 | YUV422, 67 | YUV444 68 | }; 69 | 70 | /* Frame level statistics */ 71 | struct vca_frame_stats 72 | { 73 | int poc; 74 | uint32_t l_value; 75 | uint32_t e_value; 76 | double h_value; 77 | double epsilon; 78 | }; 79 | 80 | struct vca_frame_results 81 | { 82 | /* The pointers are pointers to memory for storage of one value per block in the frame. 83 | * The caller must make sure that this is pointing to a valid and big enough block of memory. 84 | * If they are nullptr, no data will be written. 85 | */ 86 | uint32_t *brightnessPerBlock{}; 87 | uint32_t averageBrightness; 88 | 89 | uint32_t *energyPerBlock{}; 90 | uint32_t averageEnergy{}; 91 | 92 | uint32_t *energyDiffPerBlock{}; 93 | double energyDiff{}; 94 | 95 | uint32_t *averageUPerBlock{}; 96 | uint32_t *averageVPerBlock{}; 97 | uint32_t averageU{}; 98 | uint32_t averageV{}; 99 | 100 | uint32_t *energyUPerBlock{}; 101 | uint32_t *energyVPerBlock{}; 102 | uint32_t energyU{}; 103 | uint32_t energyV{}; 104 | 105 | double *entropyPerBlock{}; 106 | double averageEntropy; 107 | double *entropyDiffPerBlock{}; 108 | double entropyDiff{}; 109 | double *entropyUPerBlock{}; 110 | double *entropyVPerBlock{}; 111 | double entropyU{}; 112 | double entropyV{}; 113 | 114 | uint32_t *energyEpsilonPerBlock{}; 115 | double energyEpsilon{}; 116 | double entropyEpsilon{}; 117 | 118 | double *edgeDensityPerBlock{}; 119 | double averageEdgeDensity{}; 120 | 121 | int poc{}; 122 | bool isNewShot{}; 123 | 124 | // An increasing counter that is incremented with each call to 'vca_analyzer_push'. 125 | // So with this one can double check that the results are recieved in the right order. 126 | unsigned jobID{}; 127 | }; 128 | 129 | struct vca_frame_info 130 | { 131 | unsigned width{}; 132 | unsigned height{}; 133 | unsigned bitDepth{8}; 134 | vca_colorSpace colorspace{vca_colorSpace::YUV420}; 135 | }; 136 | 137 | /* Used to pass pictures into the analyzer, and to get picture data back out of 138 | * the analyzer. The input and output semantics are different */ 139 | struct vca_frame 140 | { 141 | /* Must be specified on input pictures, the number of planes is determined 142 | * by the colorSpace value */ 143 | uint8_t *planes[3]{nullptr, nullptr, nullptr}; 144 | 145 | /* Stride is the number of bytes between row starts */ 146 | int stride[3]{0, 0, 0}; 147 | int height[3]{0, 0, 0}; 148 | 149 | vca_frame_stats stats; 150 | vca_frame_info info; 151 | }; 152 | 153 | /* vca input parameters 154 | * 155 | */ 156 | struct vca_param 157 | { 158 | bool enableSIMD{true}; 159 | bool enableEnergyChroma{true}; 160 | bool enableEntropyChroma{true}; 161 | bool enableLowpass{true}; 162 | 163 | bool enableDCTenergy{true}; 164 | bool enableEntropy{true}; 165 | bool enableEdgeDensity{true}; 166 | 167 | vca_frame_info frameInfo{}; 168 | 169 | // Size (width/height) of the analysis block. Must be 8, 16 or 32. 170 | unsigned blockSize{32}; 171 | 172 | unsigned nrFrameThreads{0}; 173 | unsigned nrSliceThreads{0}; 174 | 175 | CpuSimd cpuSimd{CpuSimd::Autodetect}; 176 | 177 | void (*logFunction)(void *, LogLevel, const char *){}; 178 | void *logFunctionPrivateData{}; 179 | }; 180 | 181 | /* Create a new analyzer or nullptr if the config is invalid. 182 | */ 183 | DLL_PUBLIC vca_analyzer *vca_analyzer_open(vca_param cfg); 184 | 185 | typedef enum 186 | { 187 | VCA_OK = 0, 188 | VCA_ERROR 189 | } vca_result; 190 | 191 | /* Push a frame to the analyzer and start the analysis. 192 | * Note that only the pointers will be copied but no ownership of the memory is 193 | * transferred to the library. The caller must make sure that the pointers are 194 | * valid until the frame was analyzed. Once a results for a frame was pulled the 195 | * library will not use pointers anymore. 196 | * This may block until there is a slot available to work on. The number of 197 | * frames that will be processed in parallel can be set using nrFrameThreads. 198 | */ 199 | DLL_PUBLIC vca_result vca_analyzer_push(vca_analyzer *enc, vca_frame *pic_in); 200 | 201 | /* Check if a result is available to pull. 202 | */ 203 | DLL_PUBLIC bool vca_result_available(vca_analyzer *enc); 204 | 205 | /* Pull a result from the analyzer. This may block until a result is available. 206 | * Use vca_result_available if you want to only check if a result is ready. 207 | */ 208 | DLL_PUBLIC vca_result vca_analyzer_pull_frame_result(vca_analyzer *enc, vca_frame_results *result); 209 | 210 | DLL_PUBLIC void vca_analyzer_close(vca_analyzer *enc); 211 | 212 | struct vca_shot_detection_param 213 | { 214 | double minEpsilonThresh{1.5}; 215 | double maxEpsilonThresh{50}; 216 | double maxSadThresh{100}; 217 | 218 | double fps{}; 219 | 220 | void (*logFunction)(void *, LogLevel, const char *){}; 221 | void *logFunctionPrivateData{}; 222 | }; 223 | 224 | DLL_PUBLIC vca_result vca_shot_detection(const vca_shot_detection_param ¶m, 225 | vca_frame_results *frames, 226 | size_t num_frames); 227 | 228 | DLL_PUBLIC extern const char *vca_version_str; 229 | 230 | } // extern "C" 231 | -------------------------------------------------------------------------------- /source/lib/vca_config.h.in: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2024 Christian Doppler Laboratory ATHENA 3 | * 4 | * Authors: Vignesh V Menon 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. 18 | *****************************************************************************/ 19 | 20 | #ifndef VCA_CONFIG_H 21 | #define VCA_CONFIG_H 22 | 23 | #define VCA_VERSION "@VCA_VERSION@" 24 | #define VCA_LATEST_TAG "@VCA_LATEST_TAG@" 25 | #define VCA_LATEST_TAG_COMMIT "@VCA_LATEST_TAG_COMMIT@" 26 | #define VCA_TAG_DISTANCE "@VCA_TAG_DISTANCE@" 27 | #define VCA_REVISION_ID "@VCA_REVISION_ID@" 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /videos/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cd-athena/VCA/1283f1fdc912e4c800bda668e890273ce46b26e6/videos/.gitkeep --------------------------------------------------------------------------------