#include "pbo_pool.h"
#include "shared/memcpy_interleaved.h"
#include "shared/va_display.h"
+#include "shared/va_resource_pool.h"
#include <X11/Xlib.h>
#include <assert.h>
using namespace std;
static unique_ptr<VADisplayWithCleanup> va_dpy;
-static VAConfigID config_id;
-static VAImageFormat uyvy_format;
-bool vaapi_jpeg_decoding_usable = false;
-
-struct VAResources {
- unsigned width, height;
- VASurfaceID surface;
- VAContextID context;
- VAImage image;
-};
-static list<VAResources> va_resources_freelist;
-static mutex va_resources_mutex;
-
-#define CHECK_VASTATUS(va_status, func) \
- if (va_status != VA_STATUS_SUCCESS) { \
- fprintf(stderr, "%s:%d (%s) failed with %d\n", __func__, __LINE__, func, va_status); \
- abort(); \
- }
+static unique_ptr<VAResourcePool> va_pool;
-#define CHECK_VASTATUS_RET(va_status, func) \
- if (va_status != VA_STATUS_SUCCESS) { \
- fprintf(stderr, "%s:%d (%s) failed with %d\n", __func__, __LINE__, func, va_status); \
- return nullptr; \
- }
+bool vaapi_jpeg_decoding_usable = false;
// From libjpeg (although it's of course identical between implementations).
static const int jpeg_natural_order[DCTSIZE2] = {
53, 60, 61, 54, 47, 55, 62, 63,
};
-VAResources get_va_resources(unsigned width, unsigned height)
-{
- {
- lock_guard<mutex> lock(va_resources_mutex);
- for (auto it = va_resources_freelist.begin(); it != va_resources_freelist.end(); ++it) {
- if (it->width == width && it->height == height) {
- VAResources ret = *it;
- va_resources_freelist.erase(it);
- return ret;
- }
- }
- }
-
- VAResources ret;
-
- ret.width = width;
- ret.height = height;
-
- VAStatus va_status = vaCreateSurfaces(va_dpy->va_dpy, VA_RT_FORMAT_YUV422,
- width, height,
- &ret.surface, 1, nullptr, 0);
- CHECK_VASTATUS(va_status, "vaCreateSurfaces");
-
- va_status = vaCreateContext(va_dpy->va_dpy, config_id, width, height, 0, &ret.surface, 1, &ret.context);
- CHECK_VASTATUS(va_status, "vaCreateContext");
-
- va_status = vaCreateImage(va_dpy->va_dpy, &uyvy_format, width, height, &ret.image);
- CHECK_VASTATUS(va_status, "vaCreateImage");
-
- return ret;
-}
-
-void release_va_resources(VAResources resources)
+static unique_ptr<VADisplayWithCleanup> try_open_va_mjpeg(const string &va_display)
{
- lock_guard<mutex> lock(va_resources_mutex);
- if (va_resources_freelist.size() > 10) {
- auto it = va_resources_freelist.end();
- --it;
-
- VAStatus va_status = vaDestroyImage(va_dpy->va_dpy, it->image.image_id);
- CHECK_VASTATUS(va_status, "vaDestroyImage");
-
- va_status = vaDestroyContext(va_dpy->va_dpy, it->context);
- CHECK_VASTATUS(va_status, "vaDestroyContext");
-
- va_status = vaDestroySurfaces(va_dpy->va_dpy, &it->surface, 1);
- CHECK_VASTATUS(va_status, "vaDestroySurfaces");
+ VAConfigID config_id_422, config_id_420;
+ VAImageFormat uyvy_format, nv12_format;
- va_resources_freelist.erase(it);
- }
-
- va_resources_freelist.push_front(resources);
-}
-
-// RAII wrapper to release VAResources on return (even on error).
-class ReleaseVAResources {
-public:
- ReleaseVAResources(const VAResources &resources)
- : resources(resources) {}
- ~ReleaseVAResources()
- {
- if (!committed) {
- release_va_resources(resources);
- }
+ // Seemingly VA_FOURCC_422H is no good for vaGetImage(). :-/
+ unique_ptr<VADisplayWithCleanup> va_dpy =
+ try_open_va(va_display, { VAProfileJPEGBaseline }, VAEntrypointVLD,
+ { { "4:2:2", VA_RT_FORMAT_YUV422, VA_FOURCC_UYVY, &config_id_422, &uyvy_format },
+ { "4:2:0", VA_RT_FORMAT_YUV420, VA_FOURCC_NV12, &config_id_420, &nv12_format } },
+ /*chosen_profile=*/nullptr, /*error=*/nullptr);
+ if (va_dpy == nullptr) {
+ return va_dpy;
}
- void commit() { committed = true; }
-
-private:
- const VAResources &resources;
- bool committed = false;
-};
+ va_pool.reset(new VAResourcePool(va_dpy->va_dpy, uyvy_format, nv12_format, config_id_422, config_id_420, /*with_data_buffer=*/false));
-static unique_ptr<VADisplayWithCleanup> try_open_va_mjpeg(const string &va_display)
-{
- // Seemingly VA_FOURCC_422H is no good for vaGetImage(). :-/
- return try_open_va(va_display, { VAProfileJPEGBaseline }, VAEntrypointVLD,
- { { "4:2:2", VA_RT_FORMAT_YUV422, VA_FOURCC_UYVY, &config_id, &uyvy_format } },
- /*chosen_profile=*/nullptr, /*error=*/nullptr);
+ return va_dpy;
}
string get_usable_va_display()
pic_param.color_space = 0; // YUV.
pic_param.rotation = VA_ROTATION_NONE;
- VAResources resources = get_va_resources(dinfo.image_width, dinfo.image_height);
- ReleaseVAResources release(resources);
+ VAResourcePool::VAResources resources = va_pool->get_va_resources(dinfo.image_width, dinfo.image_height, VA_FOURCC_UYVY);
+ ReleaseVAResources release(va_pool.get(), resources);
VABufferID pic_param_buffer;
VAStatus va_status = vaCreateBuffer(va_dpy->va_dpy, resources.context, VAPictureParameterBufferType, sizeof(pic_param), 1, &pic_param, &pic_param_buffer);
add_stream(HTTPD::StreamID{ HTTPD::MULTICAM_STREAM, 0 });
// Initialize VA-API.
+ VAConfigID config_id_422, config_id_420;
string error;
va_dpy = try_open_va(va_display, { VAProfileJPEGBaseline }, VAEntrypointEncPicture,
{
fprintf(stderr, "Could not initialize VA-API for MJPEG encoding: %s. JPEGs will be encoded in software if needed.\n", error.c_str());
}
+ va_pool.reset(new VAResourcePool(va_dpy->va_dpy, uyvy_format, nv12_format, config_id_422, config_id_420, /*with_data_buffer=*/true));
+
encoder_thread = thread(&MJPEGEncoder::encoder_thread_func, this);
if (va_dpy != nullptr) {
va_receiver_thread = thread(&MJPEGEncoder::va_receiver_thread_func, this);
VABufferID buf;
};
-MJPEGEncoder::VAResources MJPEGEncoder::get_va_resources(unsigned width, unsigned height, uint32_t fourcc)
-{
- {
- lock_guard<mutex> lock(va_resources_mutex);
- for (auto it = va_resources_freelist.begin(); it != va_resources_freelist.end(); ++it) {
- if (it->width == width && it->height == height && it->fourcc == fourcc) {
- VAResources ret = *it;
- va_resources_freelist.erase(it);
- return ret;
- }
- }
- }
-
- VAResources ret;
-
- ret.width = width;
- ret.height = height;
- ret.fourcc = fourcc;
-
- VASurfaceAttrib attrib;
- attrib.flags = VA_SURFACE_ATTRIB_SETTABLE;
- attrib.type = VASurfaceAttribPixelFormat;
- attrib.value.type = VAGenericValueTypeInteger;
- attrib.value.value.i = fourcc;
-
- VAStatus va_status;
- VAConfigID config_id;
- if (fourcc == VA_FOURCC_UYVY) {
- va_status = vaCreateSurfaces(va_dpy->va_dpy, VA_RT_FORMAT_YUV422, width, height, &ret.surface, 1, &attrib, 1);
- config_id = config_id_422;
- } else {
- assert(fourcc == VA_FOURCC_NV12);
- va_status = vaCreateSurfaces(va_dpy->va_dpy, VA_RT_FORMAT_YUV420, width, height, &ret.surface, 1, &attrib, 1);
- config_id = config_id_420;
- }
-
- va_status = vaCreateContext(va_dpy->va_dpy, config_id, width, height, 0, &ret.surface, 1, &ret.context);
- CHECK_VASTATUS(va_status, "vaCreateContext");
-
- va_status = vaCreateBuffer(va_dpy->va_dpy, ret.context, VAEncCodedBufferType, width * height * 3 + 8192, 1, nullptr, &ret.data_buffer);
- CHECK_VASTATUS(va_status, "vaCreateBuffer");
-
- if (fourcc == VA_FOURCC_UYVY) {
- va_status = vaCreateImage(va_dpy->va_dpy, &uyvy_format, width, height, &ret.image);
- CHECK_VASTATUS(va_status, "vaCreateImage");
- } else {
- assert(fourcc == VA_FOURCC_NV12);
- va_status = vaCreateImage(va_dpy->va_dpy, &nv12_format, width, height, &ret.image);
- CHECK_VASTATUS(va_status, "vaCreateImage");
- }
-
- return ret;
-}
-
-void MJPEGEncoder::release_va_resources(MJPEGEncoder::VAResources resources)
-{
- lock_guard<mutex> lock(va_resources_mutex);
- if (va_resources_freelist.size() > 50) {
- auto it = va_resources_freelist.end();
- --it;
-
- VAStatus va_status = vaDestroyBuffer(va_dpy->va_dpy, it->data_buffer);
- CHECK_VASTATUS(va_status, "vaDestroyBuffer");
-
- va_status = vaDestroyContext(va_dpy->va_dpy, it->context);
- CHECK_VASTATUS(va_status, "vaDestroyContext");
-
- va_status = vaDestroySurfaces(va_dpy->va_dpy, &it->surface, 1);
- CHECK_VASTATUS(va_status, "vaDestroySurfaces");
-
- va_status = vaDestroyImage(va_dpy->va_dpy, it->image.image_id);
- CHECK_VASTATUS(va_status, "vaDestroyImage");
-
- va_resources_freelist.erase(it);
- }
-
- va_resources_freelist.push_front(resources);
-}
-
namespace {
void push16(uint16_t val, string *str)
unsigned width = qf.video_format.width;
unsigned height = qf.video_format.height;
- VAResources resources;
+ VAResourcePool::VAResources resources;
ReleaseVAResources release;
if (userdata->data_copy_current_src == PBOFrameAllocator::Userdata::FROM_VA_API) {
assert(is_uyvy(qf.frame));
} else {
assert(userdata->data_copy_current_src == PBOFrameAllocator::Userdata::FROM_MALLOC);
if (is_uyvy(qf.frame)) {
- resources = get_va_resources(width, height, VA_FOURCC_UYVY);
+ resources = va_pool->get_va_resources(width, height, VA_FOURCC_UYVY);
} else {
assert(is_i420(qf.frame));
- resources = get_va_resources(width, height, VA_FOURCC_NV12);
+ resources = va_pool->get_va_resources(width, height, VA_FOURCC_NV12);
}
- release = ReleaseVAResources(this, resources);
+ release = ReleaseVAResources(va_pool.get(), resources);
}
int y_h_samp_factor, y_v_samp_factor;
#include "defs.h"
#include "shared/ffmpeg_raii.h"
#include "shared/httpd.h"
+#include "shared/va_resource_pool.h"
#include "ref_counted_frame.h"
extern "C" {
bool using_vaapi() const { return va_dpy != nullptr; }
bool should_encode_mjpeg_for_card(unsigned card_index);
+ VAResourcePool *get_va_pool() const { return va_pool.get(); }
private:
static constexpr int quality = 90;
- struct VAResources {
- unsigned width, height;
- uint32_t fourcc;
- VASurfaceID surface;
- VAContextID context;
- VABufferID data_buffer;
- VAImage image;
- };
-
- // RAII wrapper to release VAResources on return (even on error).
- class ReleaseVAResources {
- public:
- ReleaseVAResources() : committed(true) {}
-
- ReleaseVAResources(MJPEGEncoder *mjpeg, const VAResources &resources)
- : mjpeg(mjpeg), resources(resources) {}
-
- ReleaseVAResources(ReleaseVAResources &) = delete;
-
- ReleaseVAResources(ReleaseVAResources &&other)
- : mjpeg(other.mjpeg), resources(other.resources), committed(other.committed) {
- other.commit();
- }
-
- ReleaseVAResources &operator= (ReleaseVAResources &) = delete;
-
- ReleaseVAResources &operator= (ReleaseVAResources &&other) {
- if (!committed) {
- mjpeg->release_va_resources(resources);
- }
- mjpeg = other.mjpeg;
- resources = std::move(other.resources);
- committed = other.committed;
- other.commit();
- return *this;
- }
-
- ~ReleaseVAResources()
- {
- if (!committed) {
- mjpeg->release_va_resources(resources);
- }
- }
-
- void commit() { committed = true; }
-
- private:
- MJPEGEncoder *mjpeg = nullptr;
- VAResources resources;
- bool committed = false;
- };
-
struct QueuedFrame {
int64_t pts;
unsigned card_index;
movit::RGBTriplet white_balance;
// Only for frames in the process of being encoded by VA-API.
- VAResources resources;
+ VAResourcePool::VAResources resources;
ReleaseVAResources resource_releaser;
};
bool running = false;
std::unique_ptr<VADisplayWithCleanup> va_dpy;
- VAConfigID config_id_422, config_id_420;
+ std::unique_ptr<VAResourcePool> va_pool;
struct VAKey {
unsigned width, height, y_h_samp_factor, y_v_samp_factor;
std::map<VAKey, VAData> va_data_for_parameters;
VAData get_va_data_for_parameters(unsigned width, unsigned height, unsigned y_h_samp_factor, unsigned y_v_samp_factor, const movit::RGBTriplet &white_balance);
- std::list<VAResources> va_resources_freelist;
- std::mutex va_resources_mutex;
- VAResources get_va_resources(unsigned width, unsigned height, uint32_t fourcc);
- void release_va_resources(VAResources resources);
-
uint8_t *tmp_y, *tmp_cbcr, *tmp_cb, *tmp_cr; // Private to the encoder thread. Used by the libjpeg backend only.
std::atomic<int64_t> metric_mjpeg_frames_zero_size_dropped{0};
mjpeg_encoder->should_encode_mjpeg_for_card(card_index)) {
if (mjpeg_encoder->using_vaapi()) {
VADisplay va_dpy = mjpeg_encoder->va_dpy->va_dpy;
- MJPEGEncoder::VAResources resources = mjpeg_encoder->get_va_resources(width, height, VA_FOURCC_UYVY); // Only used by DeckLinkCapture, so always 4:2:2.
- MJPEGEncoder::ReleaseVAResources release(mjpeg_encoder, resources);
+ VAResourcePool::VAResources resources = mjpeg_encoder->get_va_pool()->get_va_resources(width, height, VA_FOURCC_UYVY); // Only used by DeckLinkCapture, so always 4:2:2.
+ ReleaseVAResources release(mjpeg_encoder->get_va_pool(), resources);
if (resources.image.pitches[0] == stride) {
userdata->va_resources = move(resources);
{
// In case we never got to upload the frame to MJPEGEncoder.
Userdata *userdata = (Userdata *)frame.userdata;
- MJPEGEncoder::VAResources resources __attribute__((unused)) = move(userdata->va_resources);
- MJPEGEncoder::ReleaseVAResources release = move(userdata->va_resources_release);
+ VAResourcePool::VAResources resources __attribute__((unused)) = move(userdata->va_resources);
+ ReleaseVAResources release = move(userdata->va_resources_release);
if (frame.data_copy != nullptr && userdata->data_copy_current_src == Userdata::FROM_VA_API) {
VADisplay va_dpy = mjpeg_encoder->va_dpy->va_dpy;
// from there into VA-API buffers (by MJPEGEncoder) if needed.
enum { FROM_MALLOC, FROM_VA_API } data_copy_current_src;
uint8_t *data_copy_malloc;
- MJPEGEncoder::VAResources va_resources;
- MJPEGEncoder::ReleaseVAResources va_resources_release;
+ VAResourcePool::VAResources va_resources;
+ ReleaseVAResources va_resources_release;
int generation;
};
protobuf_lib = static_library('protobufs', proto_generated, dependencies: [protobufdep])
protobuf_hdrs = declare_dependency(sources: proto_generated)
-srcs = ['memcpy_interleaved.cpp', 'metacube2.cpp', 'ffmpeg_raii.cpp', 'mux.cpp', 'metrics.cpp', 'context.cpp', 'httpd.cpp', 'disk_space_estimator.cpp', 'read_file.cpp', 'text_proto.cpp', 'midi_device.cpp', 'ref_counted_texture.cpp', 'va_display.cpp']
+srcs = ['memcpy_interleaved.cpp', 'metacube2.cpp', 'ffmpeg_raii.cpp', 'mux.cpp', 'metrics.cpp', 'context.cpp', 'httpd.cpp', 'disk_space_estimator.cpp', 'read_file.cpp', 'text_proto.cpp', 'midi_device.cpp', 'ref_counted_texture.cpp', 'va_display.cpp', 'va_resource_pool.cpp']
srcs += proto_generated
# Qt objects.
--- /dev/null
+
+#include <assert.h>
+
+#include "shared/va_resource_pool.h"
+
+using namespace std;
+
+VAResourcePool::VAResources VAResourcePool::get_va_resources(unsigned width, unsigned height, uint32_t fourcc)
+{
+ {
+ lock_guard<mutex> lock(mu);
+ for (auto it = freelist.begin(); it != freelist.end(); ++it) {
+ if (it->width == width && it->height == height && it->fourcc == fourcc) {
+ VAResources ret = *it;
+ freelist.erase(it);
+ return ret;
+ }
+ }
+ }
+
+ VAResources ret;
+
+ ret.width = width;
+ ret.height = height;
+ ret.fourcc = fourcc;
+
+ VASurfaceAttrib attrib;
+ attrib.flags = VA_SURFACE_ATTRIB_SETTABLE;
+ attrib.type = VASurfaceAttribPixelFormat;
+ attrib.value.type = VAGenericValueTypeInteger;
+ attrib.value.value.i = fourcc;
+
+ VAStatus va_status;
+ VAConfigID config_id;
+ if (fourcc == VA_FOURCC_UYVY) {
+ va_status = vaCreateSurfaces(va_dpy, VA_RT_FORMAT_YUV422, width, height, &ret.surface, 1, &attrib, 1);
+ config_id = config_id_422;
+ } else {
+ assert(fourcc == VA_FOURCC_NV12);
+ va_status = vaCreateSurfaces(va_dpy, VA_RT_FORMAT_YUV420, width, height, &ret.surface, 1, &attrib, 1);
+ config_id = config_id_420;
+ }
+
+ va_status = vaCreateContext(va_dpy, config_id, width, height, 0, &ret.surface, 1, &ret.context);
+ CHECK_VASTATUS(va_status, "vaCreateContext");
+
+ if (with_data_buffer) {
+ va_status = vaCreateBuffer(va_dpy, ret.context, VAEncCodedBufferType, width * height * 3 + 8192, 1, nullptr, &ret.data_buffer);
+ CHECK_VASTATUS(va_status, "vaCreateBuffer");
+ }
+
+ if (fourcc == VA_FOURCC_UYVY) {
+ va_status = vaCreateImage(va_dpy, &uyvy_format, width, height, &ret.image);
+ CHECK_VASTATUS(va_status, "vaCreateImage");
+ } else {
+ assert(fourcc == VA_FOURCC_NV12);
+ va_status = vaCreateImage(va_dpy, &nv12_format, width, height, &ret.image);
+ CHECK_VASTATUS(va_status, "vaCreateImage");
+ }
+
+ return ret;
+}
+
+void VAResourcePool::release_va_resources(VAResourcePool::VAResources resources)
+{
+ lock_guard<mutex> lock(mu);
+ if (freelist.size() > 50) {
+ auto it = freelist.end();
+ --it;
+
+ VAStatus va_status;
+
+ if (with_data_buffer) {
+ va_status = vaDestroyBuffer(va_dpy, it->data_buffer);
+ CHECK_VASTATUS(va_status, "vaDestroyBuffer");
+ }
+
+ va_status = vaDestroyContext(va_dpy, it->context);
+ CHECK_VASTATUS(va_status, "vaDestroyContext");
+
+ va_status = vaDestroySurfaces(va_dpy, &it->surface, 1);
+ CHECK_VASTATUS(va_status, "vaDestroySurfaces");
+
+ va_status = vaDestroyImage(va_dpy, it->image.image_id);
+ CHECK_VASTATUS(va_status, "vaDestroyImage");
+
+ freelist.erase(it);
+ }
+
+ freelist.push_front(resources);
+}
+
--- /dev/null
+#ifndef _VA_RESOURCE_POOL
+#define _VA_RESOURCE_POOL 1
+
+#include <inttypes.h>
+#include <va/va.h>
+
+#include <list>
+#include <mutex>
+
+#define CHECK_VASTATUS(va_status, func) \
+ if (va_status != VA_STATUS_SUCCESS) { \
+ fprintf(stderr, "%s:%d (%s) failed: %s\n", __func__, __LINE__, func, vaErrorStr(va_status)); \
+ exit(1); \
+ }
+
+#define CHECK_VASTATUS_RET(va_status, func) \
+ if (va_status != VA_STATUS_SUCCESS) { \
+ fprintf(stderr, "%s:%d (%s) failed with %d\n", __func__, __LINE__, func, va_status); \
+ return nullptr; \
+ }
+
+class VAResourcePool {
+public:
+ struct VAResources {
+ unsigned width, height;
+ uint32_t fourcc;
+ VASurfaceID surface;
+ VAContextID context;
+ VABufferID data_buffer;
+ VAImage image;
+ };
+
+ VAResourcePool(VADisplay va_dpy, VAImageFormat uyvy_format, VAImageFormat nv12_format, VAConfigID config_id_422, VAConfigID config_id_420, bool with_data_buffer)
+ : va_dpy(va_dpy),
+ uyvy_format(uyvy_format),
+ nv12_format(nv12_format),
+ config_id_422(config_id_422),
+ config_id_420(config_id_420),
+ with_data_buffer(with_data_buffer) {}
+ VAResources get_va_resources(unsigned width, unsigned height, uint32_t fourcc);
+ void release_va_resources(VAResources resources);
+
+private:
+ const VADisplay va_dpy;
+ VAImageFormat uyvy_format, nv12_format;
+ const VAConfigID config_id_422, config_id_420;
+ const bool with_data_buffer;
+
+ std::mutex mu;
+ std::list<VAResources> freelist; // Under mu.
+};
+
+// RAII wrapper to release VAResources on return (even on error).
+class ReleaseVAResources {
+public:
+ ReleaseVAResources() : committed(true) {}
+
+ ReleaseVAResources(VAResourcePool *pool, const VAResourcePool::VAResources &resources)
+ : pool(pool), resources(resources) {}
+
+ ReleaseVAResources(ReleaseVAResources &) = delete;
+
+ ReleaseVAResources(ReleaseVAResources &&other)
+ : pool(other.pool), resources(other.resources), committed(other.committed) {
+ other.commit();
+ }
+
+ ReleaseVAResources &operator= (ReleaseVAResources &) = delete;
+
+ ReleaseVAResources &operator= (ReleaseVAResources &&other) {
+ if (!committed) {
+ pool->release_va_resources(resources);
+ }
+ pool = other.pool;
+ resources = std::move(other.resources);
+ committed = other.committed;
+ other.commit();
+ return *this;
+ }
+
+ ~ReleaseVAResources()
+ {
+ if (!committed) {
+ pool->release_va_resources(resources);
+ }
+ }
+
+ void commit() { committed = true; }
+
+private:
+ VAResourcePool *pool = nullptr;
+ VAResourcePool::VAResources resources;
+ bool committed = false;
+};
+
+#endif // !defined(_VA_RESOURCE_POOL)