From 03f28370d825d4aa8effbef58e7d07fd4135cff6 Mon Sep 17 00:00:00 2001 From: ianwal Date: Tue, 16 Jul 2024 17:12:35 -0700 Subject: [PATCH] [vpdq] Fix macOS build Clang doesn't like the forward declaration of the templates. --- vpdq/README.md | 4 - vpdq/cpp/vpdq/CMakeLists.txt | 1 - vpdq/cpp/vpdq/cpp/hashing/filehasher.cpp | 11 +- vpdq/cpp/vpdq/cpp/hashing/hasher.cpp | 146 ----------------------- vpdq/cpp/vpdq/cpp/hashing/hasher.h | 122 +++++++++++++++++++ 5 files changed, 131 insertions(+), 153 deletions(-) delete mode 100644 vpdq/cpp/vpdq/cpp/hashing/hasher.cpp diff --git a/vpdq/README.md b/vpdq/README.md index 4c7437b2f..565cf5b86 100644 --- a/vpdq/README.md +++ b/vpdq/README.md @@ -157,10 +157,6 @@ Once you are in the container proceed to [**Building**](#building). - make - FFmpeg and libav* libraries -> **Note for macos:** -> -> Currently, the built-in Apple clang g++ does not work for building this implementation. Installing GCC and updating the [`CMake`](./cpp/CMakeLists.txt) CXX to use that version of g++ as a workaround is recommended. - ### Install FFmpeg [FFmpeg](https://ffmpeg.org/) and its [libav* libraries](https://trac.ffmpeg.org/wiki/Using%20libav*) must be installed before building. diff --git a/vpdq/cpp/vpdq/CMakeLists.txt b/vpdq/cpp/vpdq/CMakeLists.txt index 1fb8cb271..d4bc8c594 100644 --- a/vpdq/cpp/vpdq/CMakeLists.txt +++ b/vpdq/cpp/vpdq/CMakeLists.txt @@ -6,7 +6,6 @@ set(VPDQSOURCES cpp/hashing/filehasher.cpp cpp/hashing/ffmpegutils.cpp cpp/hashing/ffmpegwrapper.cpp - cpp/hashing/hasher.cpp cpp/hashing/matchTwoHash.cpp cpp/io/vpdqio.cpp ) diff --git a/vpdq/cpp/vpdq/cpp/hashing/filehasher.cpp b/vpdq/cpp/vpdq/cpp/hashing/filehasher.cpp index 1037067a4..16b19f640 100644 --- a/vpdq/cpp/vpdq/cpp/hashing/filehasher.cpp +++ b/vpdq/cpp/vpdq/cpp/hashing/filehasher.cpp @@ -3,6 +3,7 @@ // ================================================================ #include +#include #include #include #include @@ -137,8 +138,14 @@ class FFmpegHasher { // no error. we're good. } - // Get the current frame number from the video codec context - auto const frameNumber = m_video->codecContext->frame_number - 1; +// AVCodecContext::frame_number was deprecated in FFmpeg 6.0 +// in favor of AVCodecContext::frame_num +#if LIBAVCODEC_VERSION_MAJOR >= 60 + const auto codecFrameNumber = m_video->codecContext->frame_num; +#else + const auto codecFrameNumber = m_video->codecContext->frame_number; +#endif + const int64_t frameNumber{int64_t{codecFrameNumber} - 1}; if (frameNumber % m_frameMod == 0) { AVFramePtr targetFrame{}; diff --git a/vpdq/cpp/vpdq/cpp/hashing/hasher.cpp b/vpdq/cpp/vpdq/cpp/hashing/hasher.cpp deleted file mode 100644 index 38d913162..000000000 --- a/vpdq/cpp/vpdq/cpp/hashing/hasher.cpp +++ /dev/null @@ -1,146 +0,0 @@ -// ================================================================ -// Copyright (c) Meta Platforms, Inc. and affiliates. -// ================================================================ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace facebook { -namespace vpdq { -namespace hashing { - -namespace { - -/** @brief Hash a frame using PDQ. - * - * @param frame The frame to be hashed. - * @param frameNumber The metadata from the video the frame is from. - * - * @return The vdpdq hash of the frame. - **/ -template -vpdqFeature hashFrame(TFrame& frame, const VideoMetadata& video_metadata) { - auto phasher = FrameBufferHasherFactory::createFrameHasher( - video_metadata.height, video_metadata.width); - - int quality; - pdq::hashing::Hash256 pdqHash; - auto const is_hashing_successful = - phasher->hashFrame(frame.get_buffer_ptr(), pdqHash, quality); - if (!is_hashing_successful) { - throw std::runtime_error( - std::string{"Failed to hash frame buffer. Frame: "} + - std::to_string(frame.get_frame_number()) + - " Frame width or height smaller than the minimum hashable dimension"); - } - return vpdqFeature{ - pdqHash, - static_cast(frame.get_frame_number()), - quality, - static_cast(frame.get_frame_number()) / video_metadata.framerate}; -} - -} // namespace - -template -VpdqHasher::VpdqHasher( - size_t thread_count, VideoMetadata video_metadata) - : m_done_hashing(false), m_video_metadata(video_metadata) { - // Set thread count if specified - if (thread_count == 0) { - thread_count = std::thread::hardware_concurrency(); - } else { - thread_count = thread_count; - } - - m_multithreaded = (thread_count != 1); - - // Create consumer hasher threads if multithreading - if (m_multithreaded) { - consumer_threads.reserve(thread_count); - for (size_t thread_idx{0}; thread_idx < thread_count; ++thread_idx) { - consumer_threads.emplace_back( - std::thread(std::bind(&VpdqHasher::consumer, this))); - } - } -} - -template -void VpdqHasher::push_back(TFrame&& frame) { - if (m_multithreaded) { - { - std::lock_guard lock(m_queue_mutex); - m_queue.push(std::move(frame)); - m_queue_condition.notify_one(); - } - } else { - hasher(frame); - } -} - -template -std::vector VpdqHasher::finish() { - if (m_multithreaded) { - { - std::lock_guard lock(m_queue_mutex); - m_done_hashing = true; - } - - m_queue_condition.notify_all(); - for (auto& thread : consumer_threads) { - thread.join(); - } - } - - // Sort out of order frames by frame number - std::sort( - std::begin(m_result), - std::end(m_result), - [](const vpdqFeature& a, const vpdqFeature& b) { - return a.frameNumber < b.frameNumber; - }); - - return m_result; -} - -template -void VpdqHasher::hasher(TFrame& frame) { - auto hashedFrame = hashFrame(frame, m_video_metadata); - { - std::lock_guard lock(m_result_mutex); - m_result.push_back(std::move(hashedFrame)); - } -} - -template -void VpdqHasher::consumer() { - while (true) { - std::unique_lock lock(m_queue_mutex); - m_queue_condition.wait( - lock, [this] { return !m_queue.empty() || m_done_hashing; }); - if (m_queue.empty() && m_done_hashing) - break; - auto frame = std::move(m_queue.front()); - m_queue.pop(); - lock.unlock(); - hasher(frame); - } -} - -} // namespace hashing -} // namespace vpdq -} // namespace facebook diff --git a/vpdq/cpp/vpdq/cpp/hashing/hasher.h b/vpdq/cpp/vpdq/cpp/hashing/hasher.h index 4989650cb..b176f5637 100644 --- a/vpdq/cpp/vpdq/cpp/hashing/hasher.h +++ b/vpdq/cpp/vpdq/cpp/hashing/hasher.h @@ -5,12 +5,22 @@ #ifndef HASHER_H #define HASHER_H +#include +#include +#include #include +#include +#include +#include +#include +#include #include #include +#include #include #include +#include #include #include @@ -137,6 +147,118 @@ class VpdqHasher { void consumer(); }; +/** @brief Hash a frame using PDQ. + * + * @param frame The frame to be hashed. + * @param frameNumber The metadata from the video the frame is from. + * + * @return The vdpdq hash of the frame. + **/ +template +vpdqFeature hashFrame(TFrame& frame, const VideoMetadata& video_metadata) { + auto phasher = FrameBufferHasherFactory::createFrameHasher( + video_metadata.height, video_metadata.width); + + int quality; + pdq::hashing::Hash256 pdqHash; + auto const is_hashing_successful = + phasher->hashFrame(frame.get_buffer_ptr(), pdqHash, quality); + if (!is_hashing_successful) { + throw std::runtime_error( + std::string{"Failed to hash frame buffer. Frame: "} + + std::to_string(frame.get_frame_number()) + + " Frame width or height smaller than the minimum hashable dimension"); + } + return vpdqFeature{ + pdqHash, + static_cast(frame.get_frame_number()), + quality, + static_cast(frame.get_frame_number()) / video_metadata.framerate}; +} + +template +VpdqHasher::VpdqHasher( + size_t thread_count, VideoMetadata video_metadata) + : m_done_hashing(false), m_video_metadata(video_metadata) { + // Set thread count if specified + if (thread_count == 0) { + thread_count = std::thread::hardware_concurrency(); + } + + m_multithreaded = (thread_count != 1); + + // Create consumer hasher threads if multithreading + if (m_multithreaded) { + consumer_threads.reserve(thread_count); + for (size_t thread_idx{0}; thread_idx < thread_count; ++thread_idx) { + consumer_threads.emplace_back(std::thread(&VpdqHasher::consumer, this)); + } + } +} + +template +void VpdqHasher::push_back(TFrame&& frame) { + if (m_multithreaded) { + { + std::lock_guard lock(m_queue_mutex); + m_queue.push(std::move(frame)); + m_queue_condition.notify_one(); + } + } else { + hasher(frame); + } +} + +template +std::vector VpdqHasher::finish() { + if (m_multithreaded) { + { + std::lock_guard lock(m_queue_mutex); + m_done_hashing = true; + } + + m_queue_condition.notify_all(); + for (auto& thread : consumer_threads) { + thread.join(); + } + } + + // Sort out of order frames by frame number + std::sort( + std::begin(m_result), + std::end(m_result), + [](const vpdqFeature& a, const vpdqFeature& b) { + return a.frameNumber < b.frameNumber; + }); + + return m_result; +} + +template +void VpdqHasher::hasher(TFrame& frame) { + auto hashedFrame = hashFrame(frame, m_video_metadata); + { + std::lock_guard lock(m_result_mutex); + m_result.push_back(std::move(hashedFrame)); + } +} + +template +void VpdqHasher::consumer() { + while (true) { + std::unique_lock lock(m_queue_mutex); + m_queue_condition.wait( + lock, [this] { return !m_queue.empty() || m_done_hashing; }); + if (m_queue.empty() && m_done_hashing) { + break; + } + auto frame = std::move(m_queue.front()); + m_queue.pop(); + lock.unlock(); + hasher(frame); + } +} + // Explicit template instantiation for all frame types. template class VpdqHasher; template class VpdqHasher;