Skip to content

Commit

Permalink
[vpdq] Fix macOS build (#1605)
Browse files Browse the repository at this point in the history
Clang doesn't like the forward declaration of the templates.
  • Loading branch information
ianwal authored Jul 26, 2024
1 parent e58a463 commit 308a05c
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 153 deletions.
4 changes: 0 additions & 4 deletions vpdq/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 0 additions & 1 deletion vpdq/cpp/vpdq/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down
11 changes: 9 additions & 2 deletions vpdq/cpp/vpdq/cpp/hashing/filehasher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// ================================================================

#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <iostream>
#include <memory>
Expand Down Expand Up @@ -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{};
Expand Down
146 changes: 0 additions & 146 deletions vpdq/cpp/vpdq/cpp/hashing/hasher.cpp

This file was deleted.

122 changes: 122 additions & 0 deletions vpdq/cpp/vpdq/cpp/hashing/hasher.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,22 @@
#ifndef HASHER_H
#define HASHER_H

#include <algorithm>
#include <atomic>
#include <cmath>
#include <condition_variable>
#include <cstdio>
#include <fstream>
#include <functional>
#include <iostream>
#include <memory>
#include <mutex>
#include <queue>
#include <string>
#include <thread>
#include <vector>

#include <vpdq/cpp/hashing/bufferhasher.h>
#include <vpdq/cpp/hashing/ffmpegwrapper.h>
#include <vpdq/cpp/hashing/vpdqHashType.h>

Expand Down Expand Up @@ -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 <typename TFrame>
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<int>(frame.get_frame_number()),
quality,
static_cast<float>(frame.get_frame_number()) / video_metadata.framerate};
}

template <typename TFrame>
VpdqHasher<TFrame>::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 <typename TFrame>
void VpdqHasher<TFrame>::push_back(TFrame&& frame) {
if (m_multithreaded) {
{
std::lock_guard<std::mutex> lock(m_queue_mutex);
m_queue.push(std::move(frame));
m_queue_condition.notify_one();
}
} else {
hasher(frame);
}
}

template <typename TFrame>
std::vector<vpdqFeature> VpdqHasher<TFrame>::finish() {
if (m_multithreaded) {
{
std::lock_guard<std::mutex> 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 <typename TFrame>
void VpdqHasher<TFrame>::hasher(TFrame& frame) {
auto hashedFrame = hashFrame(frame, m_video_metadata);
{
std::lock_guard<std::mutex> lock(m_result_mutex);
m_result.push_back(std::move(hashedFrame));
}
}

template <typename TFrame>
void VpdqHasher<TFrame>::consumer() {
while (true) {
std::unique_lock<std::mutex> 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<GenericFrame>;
template class VpdqHasher<ffmpeg::FFmpegFrame>;
Expand Down

0 comments on commit 308a05c

Please sign in to comment.