]> git.sesse.net Git - nageru/blobdiff - nageru/mjpeg_encoder.cpp
IWYU-fix nageru/*.cpp.
[nageru] / nageru / mjpeg_encoder.cpp
index 857346db7592d67a79305cef7a1c643b167912bf..71af1693778aa51684c8c98ba65c99582f779cde 100644 (file)
@@ -1,32 +1,59 @@
 #include "mjpeg_encoder.h"
 
+#include <Eigen/Core>
+#include <algorithm>
 #include <assert.h>
+#include <bmusb/bmusb.h>
 #include <jpeglib.h>
-#include <unistd.h>
+#include <math.h>
+#include <movit/colorspace_conversion_effect.h>
+#include <movit/effect.h>
+#include <movit/image_format.h>
+#include <movit/ycbcr.h>
+#include <mutex>
+#include <pthread.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+#include <thread>
+#include <type_traits>
+#include <utility>
+#include <va/va.h>
+#include <va/va_enc_jpeg.h>
+#include <vector>
 #if __SSE2__
 #include <immintrin.h>
 #endif
-#include <list>
 
 extern "C" {
+#include <libavcodec/codec_id.h>
+#include <libavcodec/defs.h>
+#include <libavcodec/packet.h>
 #include <libavformat/avformat.h>
+#include <libavformat/avio.h>
+#include <libavutil/avutil.h>
+#include <libavutil/channel_layout.h>
+#include <libavutil/dict.h>
+#include <libavutil/mathematics.h>
+#include <libavutil/mem.h>
+#include <libavutil/pixfmt.h>
+#include <libavutil/rational.h>
 }
 
-#include "defs.h"
-#include "shared/ffmpeg_raii.h"
 #include "flags.h"
+#include "pbo_frame_allocator.h"
+#include "ref_counted_frame.h"
+#include "shared/ffmpeg_raii.h"
 #include "shared/httpd.h"
 #include "shared/memcpy_interleaved.h"
 #include "shared/metrics.h"
-#include "pbo_frame_allocator.h"
+#include "shared/shared_defs.h"
 #include "shared/timebase.h"
 #include "shared/va_display.h"
-
-#include <movit/colorspace_conversion_effect.h>
-
-#include <va/va.h>
-#include <va/va_drm.h>
-#include <va/va_x11.h>
+#include "shared/va_resource_pool.h"
 
 using namespace Eigen;
 using namespace bmusb;
@@ -190,8 +217,9 @@ void add_audio_stream(AVFormatContext *avctx)
        stream->time_base = AVRational{ 1, OUTPUT_FREQUENCY };
        stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
        stream->codecpar->codec_id = AV_CODEC_ID_PCM_S32LE;
-       stream->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
-       stream->codecpar->channels = 2;
+       stream->codecpar->ch_layout.order = AV_CHANNEL_ORDER_NATIVE;
+       stream->codecpar->ch_layout.nb_channels = 2;
+       stream->codecpar->ch_layout.u.mask = AV_CH_LAYOUT_STEREO;
        stream->codecpar->sample_rate = OUTPUT_FREQUENCY;
 }
 
@@ -221,14 +249,22 @@ MJPEGEncoder::MJPEGEncoder(HTTPD *httpd, const string &va_display)
        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, &error, &config_id_422, &config_id_420);
+       va_dpy = try_open_va(va_display, { VAProfileJPEGBaseline }, VAEntrypointEncPicture,
+               {
+                       { "4:2:2", VA_RT_FORMAT_YUV422, VA_FOURCC_UYVY, &config_id_422, &uyvy_format },
+                       // We'd prefer VA_FOURCC_I420, but it's not supported by Intel's driver.
+                       { "4:2:0", VA_RT_FORMAT_YUV420, VA_FOURCC_NV12, &config_id_420, &nv12_format }
+               },
+               /*chosen_profile=*/nullptr, &error);
        if (va_dpy == nullptr) {
                fprintf(stderr, "Could not initialize VA-API for MJPEG encoding: %s. JPEGs will be encoded in software if needed.\n", error.c_str());
        }
 
        encoder_thread = thread(&MJPEGEncoder::encoder_thread_func, this);
        if (va_dpy != nullptr) {
+               va_pool.reset(new VAResourcePool(va_dpy->va_dpy, uyvy_format, nv12_format, config_id_422, config_id_420, /*with_data_buffer=*/true));
                va_receiver_thread = thread(&MJPEGEncoder::va_receiver_thread_func, this);
        }
 
@@ -271,87 +307,6 @@ void MJPEGEncoder::stop()
        }
 }
 
-unique_ptr<VADisplayWithCleanup> MJPEGEncoder::try_open_va(const string &va_display, string *error, VAConfigID *config_id_422, VAConfigID *config_id_420)
-{
-       unique_ptr<VADisplayWithCleanup> va_dpy = va_open_display(va_display);
-       if (va_dpy == nullptr) {
-               if (error) *error = "Opening VA display failed";
-               return nullptr;
-       }
-       int major_ver, minor_ver;
-       VAStatus va_status = vaInitialize(va_dpy->va_dpy, &major_ver, &minor_ver);
-       if (va_status != VA_STATUS_SUCCESS) {
-               char buf[256];
-               snprintf(buf, sizeof(buf), "vaInitialize() failed with status %d\n", va_status);
-               if (error != nullptr) *error = buf;
-               return nullptr;
-       }
-
-       {
-               VAConfigAttrib attr = { VAConfigAttribRTFormat, VA_RT_FORMAT_YUV422 };
-               va_status = vaCreateConfig(va_dpy->va_dpy, VAProfileJPEGBaseline, VAEntrypointEncPicture,
-                       &attr, 1, config_id_422);
-               if (va_status == VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT) {
-                       if (error != nullptr) *error = "No 4:2:2 hardware support";
-                       return nullptr;
-               } else if (va_status != VA_STATUS_SUCCESS) {
-                       char buf[256];
-                       snprintf(buf, sizeof(buf), "vaCreateConfig() for 4:2:2 failed with status %d\n", va_status);
-                       if (error != nullptr) *error = buf;
-                       return nullptr;
-               }
-       }
-       {
-               VAConfigAttrib attr = { VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420 };
-               va_status = vaCreateConfig(va_dpy->va_dpy, VAProfileJPEGBaseline, VAEntrypointEncPicture,
-                       &attr, 1, config_id_420);
-               if (va_status == VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT) {
-                       if (error != nullptr) *error = "No 4:2:0 hardware support";
-                       return nullptr;
-               } else if (va_status != VA_STATUS_SUCCESS) {
-                       char buf[256];
-                       snprintf(buf, sizeof(buf), "vaCreateConfig() for 4:2:0 failed with status %d\n", va_status);
-                       if (error != nullptr) *error = buf;
-                       return nullptr;
-               }
-       }
-
-       // TODO: Unify with the code in Futatabi.
-       int num_formats = vaMaxNumImageFormats(va_dpy->va_dpy);
-       assert(num_formats > 0);
-
-       unique_ptr<VAImageFormat[]> formats(new VAImageFormat[num_formats]);
-       va_status = vaQueryImageFormats(va_dpy->va_dpy, formats.get(), &num_formats);
-       if (va_status != VA_STATUS_SUCCESS) {
-               char buf[256];
-               snprintf(buf, sizeof(buf), "vaQueryImageFormats() failed with status %d\n", va_status);
-               if (error != nullptr) *error = buf;
-               return nullptr;
-       }
-
-       bool uyvy_found = false, nv12_found = false;
-       for (int i = 0; i < num_formats; ++i) {
-               if (formats[i].fourcc == VA_FOURCC_UYVY) {
-                       memcpy(&uyvy_format, &formats[i], sizeof(VAImageFormat));
-                       uyvy_found = true;
-               }
-               if (formats[i].fourcc == VA_FOURCC_NV12) {
-                       memcpy(&nv12_format, &formats[i], sizeof(VAImageFormat));
-                       nv12_found = true;
-               }
-       }
-       if (!uyvy_found) {
-               if (error != nullptr) *error = "UYVY format not found";
-               return nullptr;
-       }
-       if (!nv12_found) {
-               if (error != nullptr) *error = "NV12 format not found";
-               return nullptr;
-       }
-
-       return va_dpy;
-}
-
 namespace {
 
 bool is_uyvy(RefCountedFrame frame)
@@ -524,85 +479,6 @@ private:
        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)
@@ -862,7 +738,7 @@ void MJPEGEncoder::encode_jpeg_va(QueuedFrame &&qf)
        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));
@@ -871,13 +747,12 @@ void MJPEGEncoder::encode_jpeg_va(QueuedFrame &&qf)
        } 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));
-                       // We'd prefer VA_FOURCC_I420, but it's not supported by Intel's driver.
-                       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;