vcap/cmpto_j2k + video_frame_pool rework

use pimpl for video_frame_pool

use implicit constructor for vcomp/cmpto_j2k to allow switching of pools
This commit is contained in:
Martin Pulec
2024-08-28 16:14:51 +02:00
parent c2448bc613
commit bda593c85b
3 changed files with 119 additions and 52 deletions

View File

@@ -3,7 +3,7 @@
* @author Martin Pulec <pulec@cesnet.cz>
*/
/*
* Copyright (c) 2020-2021 CESNET, z. s. p. o.
* Copyright (c) 2020-2024 CESNET
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -37,14 +37,92 @@
#include "config_msvc.h"
#include "video_frame_pool.h"
#include <cassert>
#include <cstdlib> // for free, malloc
#include <condition_variable>
#include <exception> // for exception
#include <functional>
#include <iostream>
#include <memory>
#include <mutex>
#include <queue>
#include <stdexcept>
#include <utility>
#include "video_frame_pool.h"
#include "video_codec.h"
#include "video_frame.h"
struct video_frame_pool::impl {
public:
explicit impl(
unsigned int max_used_frames = 0,
video_frame_pool_allocator const &alloc = default_data_allocator());
~impl();
void reconfigure(struct video_desc new_desc,
size_t new_size = SIZE_MAX);
std::shared_ptr<video_frame> get_frame();
struct video_frame *get_disposable_frame();
struct video_frame *get_pod_frame();
video_frame_pool_allocator const &get_allocator();
private:
void remove_free_frames();
void deallocate_frame(struct video_frame *frame);
std::unique_ptr<video_frame_pool_allocator> m_allocator = std::unique_ptr<video_frame_pool_allocator>(new default_data_allocator);
std::queue<struct video_frame *> m_free_frames;
std::mutex m_lock;
std::condition_variable m_frame_returned;
int m_generation = 0;
struct video_desc m_desc{};
size_t m_max_data_len = 0;
unsigned int m_unreturned_frames = 0;
unsigned int m_max_used_frames;
};
// _
// . _| _ _ (_ _ _ _ _ _ _ _ |
// \/ | (_| (- (_) __ | | (_| ||| (- __ |_) (_) (_) |
// |
video_frame_pool &video_frame_pool::operator=(video_frame_pool &&) = default;
video_frame_pool::video_frame_pool(unsigned int max_used_frames,
video_frame_pool_allocator const &alloc)
: m_impl(std::make_unique<video_frame_pool::impl>(max_used_frames, alloc))
{
}
video_frame_pool::~video_frame_pool() = default;
void
video_frame_pool::reconfigure(struct video_desc new_desc, size_t new_size)
{
m_impl->reconfigure(new_desc, new_size);
}
std::shared_ptr<video_frame>
video_frame_pool::get_frame()
{
return m_impl->get_frame();
}
struct video_frame *
video_frame_pool::get_disposable_frame()
{
return m_impl->get_disposable_frame();
}
struct video_frame *
video_frame_pool::get_pod_frame()
{
return m_impl->get_pod_frame();
}
video_frame_pool_allocator const &
video_frame_pool::get_allocator()
{
return m_impl->get_allocator();
}
// _
// _| _ (_ _ | |_ _| _ |_ _ _ | | _ _ _ |_ _ _
// (_| (- | (_| |_| | |_ __ (_| (_| |_ (_| __ (_| | | (_) (_ (_| |_ (_) |
//
void *default_data_allocator::allocate(size_t size) {
return malloc(size);
}
@@ -55,17 +133,24 @@ struct video_frame_pool_allocator *default_data_allocator::clone() const {
return new default_data_allocator(*this);
}
video_frame_pool::video_frame_pool(unsigned int max_used_frames, video_frame_pool_allocator const &alloc) : m_allocator(alloc.clone()), m_generation(0), m_desc(), m_max_data_len(0), m_unreturned_frames(0), m_max_used_frames(max_used_frames) {
// _
// . _| _ _ (_ _ _ _ _ _ _ _ | . . . _ _ |
// \/ | (_| (- (_) __ | | (_| ||| (- __ |_) (_) (_) | . . | ||| |_) |
// | |
video_frame_pool::impl::impl(unsigned int max_used_frames,
video_frame_pool_allocator const &alloc)
: m_allocator(alloc.clone()), m_max_used_frames(max_used_frames)
{
}
video_frame_pool::~video_frame_pool() {
video_frame_pool::impl::~impl() {
std::unique_lock<std::mutex> lk(m_lock);
remove_free_frames();
// wait also for all frames we gave out to return us
m_frame_returned.wait(lk, [this] {return m_unreturned_frames == 0;});
}
void video_frame_pool::reconfigure(struct video_desc new_desc, size_t new_size) {
void video_frame_pool::impl::reconfigure(struct video_desc new_desc, size_t new_size) {
std::unique_lock<std::mutex> lk(m_lock);
m_desc = new_desc;
m_max_data_len = new_size != SIZE_MAX ? new_size : new_desc.height * vc_get_linesize(new_desc.width, new_desc.color_spec);
@@ -73,7 +158,7 @@ void video_frame_pool::reconfigure(struct video_desc new_desc, size_t new_size)
m_generation++;
}
std::shared_ptr<video_frame> video_frame_pool::get_frame() {
std::shared_ptr<video_frame> video_frame_pool::impl::get_frame() {
struct video_frame *ret = NULL;
std::unique_lock<std::mutex> lk(m_lock);
assert(m_generation != 0);
@@ -118,7 +203,7 @@ std::shared_ptr<video_frame> video_frame_pool::get_frame() {
}, std::placeholders::_1, m_generation));
}
struct video_frame *video_frame_pool::get_disposable_frame() {
struct video_frame *video_frame_pool::impl::get_disposable_frame() {
auto && frame = get_frame();
struct video_frame *out = frame.get();
out->callbacks.dispose_udata =
@@ -128,7 +213,7 @@ struct video_frame *video_frame_pool::get_disposable_frame() {
return out;
}
struct video_frame *video_frame_pool::get_pod_frame() {
struct video_frame *video_frame_pool::impl::get_pod_frame() {
auto && frame = get_frame();
struct video_frame *out = vf_alloc_desc(video_desc_from_frame(frame.get()));
for (unsigned int i = 0; i < frame->tile_count; ++i) {
@@ -141,11 +226,11 @@ struct video_frame *video_frame_pool::get_pod_frame() {
return out;
}
video_frame_pool_allocator const & video_frame_pool::get_allocator() {
video_frame_pool_allocator const & video_frame_pool::impl::get_allocator() {
return *m_allocator;
}
void video_frame_pool::remove_free_frames() {
void video_frame_pool::impl::remove_free_frames() {
while (!m_free_frames.empty()) {
struct video_frame *frame = m_free_frames.front();
m_free_frames.pop();
@@ -153,7 +238,7 @@ void video_frame_pool::remove_free_frames() {
}
}
void video_frame_pool::deallocate_frame(struct video_frame *frame) {
void video_frame_pool::impl::deallocate_frame(struct video_frame *frame) {
if (frame == NULL)
return;
for (unsigned int i = 0; i < frame->tile_count; ++i) {
@@ -162,6 +247,10 @@ void video_frame_pool::deallocate_frame(struct video_frame *frame) {
vf_free(frame);
}
// __ __
// / /\ |__) |
// \__ /--\ | |
//
void *video_frame_pool_init(struct video_desc desc, int len) {
auto *out = new video_frame_pool(len, default_data_allocator());
out->reconfigure(desc);
@@ -177,4 +266,3 @@ void video_frame_pool_destroy(void *state) {
auto *s = static_cast<video_frame_pool* >(state);
delete s;
}

View File

@@ -3,7 +3,7 @@
* @author Martin Pulec <pulec@cesnet.cz>
*/
/*
* Copyright (c) 2014-2021 CESNET, z. s. p. o.
* Copyright (c) 2014-2024 CESNET
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -38,18 +38,13 @@
#ifndef VIDEO_FRAME_POOL_H_
#define VIDEO_FRAME_POOL_H_
#include "debug.h"
#include "host.h"
#include "types.h" // for video_frame
#include "utils/macros.h"
#include "video.h"
#ifdef __cplusplus
#include <condition_variable>
#include <cstdint>
#include <mutex>
#include <cstddef> // for size_t
#include <memory>
#include <queue>
struct video_frame_pool_allocator {
virtual void *allocate(size_t size) = 0;
@@ -73,7 +68,8 @@ struct video_frame_pool {
* is unreturned, get_frames() will block.
*/
video_frame_pool(unsigned int max_used_frames = 0, video_frame_pool_allocator const &alloc = default_data_allocator());
virtual ~video_frame_pool();
video_frame_pool &operator=(video_frame_pool &&);
~video_frame_pool();
/**
* @param new_size if omitted, deduce from video desc (only for pixel formats)
@@ -97,18 +93,8 @@ struct video_frame_pool {
video_frame_pool_allocator const & get_allocator();
private:
void remove_free_frames();
void deallocate_frame(struct video_frame *frame);
std::unique_ptr<video_frame_pool_allocator> m_allocator;
std::queue<struct video_frame *> m_free_frames;
std::mutex m_lock;
std::condition_variable m_frame_returned;
int m_generation;
struct video_desc m_desc;
size_t m_max_data_len;
unsigned int m_unreturned_frames;
unsigned int m_max_used_frames;
struct impl;
std::unique_ptr<impl> m_impl;
};
#endif // __cplusplus

View File

@@ -123,19 +123,13 @@ using allocator = default_data_allocator;
#endif
struct state_video_compress_j2k {
state_video_compress_j2k(long long int bitrate, unsigned int pool_size, int mct)
: rate(bitrate), mct(mct), pool(pool_size, allocator()),
max_in_frames(pool_size)
{
}
struct module module_data{};
struct cmpto_j2k_enc_ctx *context{};
struct cmpto_j2k_enc_cfg *enc_settings{};
long long int rate; ///< bitrate in bits per second
int mct; // force use of mct - -1 means default
long long int rate = 0; ///< bitrate in bits per second
int mct = -1; // force use of mct - -1 means default
video_frame_pool pool; ///< pool for frames allocated by us but not yet consumed by encoder
unsigned int max_in_frames; ///< max number of frames between push and pop
unsigned int max_in_frames = DEFAULT_POOL_SIZE; ///< max number of frames between push and pop
unsigned int in_frames{}; ///< number of currently encoding frames
mutex lock;
condition_variable frame_popped;
@@ -366,48 +360,47 @@ static void usage() {
static struct module * j2k_compress_init(struct module *parent, const char *c_cfg)
{
double quality = DEFAULT_QUALITY;
int mct = -1;
long long int bitrate = 0;
long long int mem_limit = DEFAULT_MEM_LIMIT;
unsigned int tile_limit = DEFAULT_TILE_LIMIT;
unsigned int pool_size = DEFAULT_POOL_SIZE;
const auto *version = cmpto_j2k_enc_get_version();
LOG(LOG_LEVEL_INFO) << MOD_NAME << "Using codec version: " << (version == nullptr ? "(unknown)" : version->name) << "\n";
auto *s = new state_video_compress_j2k();
char *tmp = (char *) alloca(strlen(c_cfg) + 1);
strcpy(tmp, c_cfg);
char *save_ptr, *item;
while ((item = strtok_r(tmp, ":", &save_ptr))) {
tmp = NULL;
if (strncasecmp("rate=", item, strlen("rate=")) == 0) {
ASSIGN_CHECK_VAL(bitrate, strchr(item, '=') + 1, 1);
ASSIGN_CHECK_VAL(s->rate, strchr(item, '=') + 1, 1);
} else if (strncasecmp("quality=", item, strlen("quality=")) == 0) {
quality = stod(strchr(item, '=') + 1);
} else if (strcasecmp("mct", item) == 0 || strcasecmp("nomct", item) == 0) {
mct = strcasecmp("mct", item) == 0 ? 1 : 0;
s->mct = strcasecmp("mct", item) == 0 ? 1 : 0;
} else if (strncasecmp("mem_limit=", item, strlen("mem_limit=")) == 0) {
ASSIGN_CHECK_VAL(mem_limit, strchr(item, '=') + 1, 1);
} else if (strncasecmp("tile_limit=", item, strlen("tile_limit=")) == 0) {
ASSIGN_CHECK_VAL(tile_limit, strchr(item, '=') + 1, 0);
} else if (strncasecmp("pool_size=", item, strlen("pool_size=")) == 0) {
ASSIGN_CHECK_VAL(pool_size, strchr(item, '=') + 1, 1);
ASSIGN_CHECK_VAL(s->max_in_frames, strchr(item, '=') + 1, 1);
} else if (strcasecmp("help", item) == 0) {
usage();
return static_cast<module*>(INIT_NOERR);
} else {
log_msg(LOG_LEVEL_ERROR, "[J2K] Wrong option: %s\n", item);
return NULL;
goto error;
}
}
s->pool = video_frame_pool(s->max_in_frames, allocator());
if (quality < 0.0 || quality > 1.0) {
LOG(LOG_LEVEL_ERROR) << "[J2K] Quality should be in interval [0-1]!\n";
return nullptr;
goto error;
}
auto *s = new state_video_compress_j2k(bitrate, pool_size, mct);
struct cmpto_j2k_enc_ctx_cfg *ctx_cfg;
CHECK_OK(cmpto_j2k_enc_ctx_cfg_create(&ctx_cfg), "Context configuration create",
goto error);