]> git.sesse.net Git - nageru/blobdiff - futatabi/vaapi_jpeg_decoder.cpp
Give in the right VAAPI context when decoding MJPEG.
[nageru] / futatabi / vaapi_jpeg_decoder.cpp
index 1d0f771cddb2d2f27d414b7393320df44799b51b..0441514a8b815efe62509e45490b595cc0a4562b 100644 (file)
@@ -3,6 +3,7 @@
 #include "jpeg_destroyer.h"
 #include "jpeg_frame.h"
 #include "jpeglib_error_wrapper.h"
+#include "pbo_pool.h"
 #include "shared/memcpy_interleaved.h"
 
 #include <X11/Xlib.h>
@@ -22,6 +23,8 @@
 #include <va/va_drm.h>
 #include <va/va_x11.h>
 
+#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
+
 using namespace std;
 
 static unique_ptr<VADisplayWithCleanup> va_dpy;
@@ -41,7 +44,7 @@ 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); \
-               exit(1); \
+               abort(); \
        }
 
 #define CHECK_VASTATUS_RET(va_status, func) \
@@ -337,6 +340,8 @@ shared_ptr<Frame> decode_jpeg_vaapi(const string &jpeg)
        }
        JPEGDestroyer destroy_dinfo(&dinfo);
 
+       jpeg_save_markers(&dinfo, JPEG_APP0 + 1, 0xFFFF);
+
        jpeg_mem_src(&dinfo, reinterpret_cast<const unsigned char *>(jpeg.data()), jpeg.size());
        if (!error_mgr.run([&dinfo] { jpeg_read_header(&dinfo, true); })) {
                return nullptr;
@@ -378,8 +383,11 @@ shared_ptr<Frame> decode_jpeg_vaapi(const string &jpeg)
        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);
+
        VABufferID pic_param_buffer;
-       VAStatus va_status = vaCreateBuffer(va_dpy->va_dpy, config_id, VAPictureParameterBufferType, sizeof(pic_param), 1, &pic_param, &pic_param_buffer);
+       VAStatus va_status = vaCreateBuffer(va_dpy->va_dpy, resources.context, VAPictureParameterBufferType, sizeof(pic_param), 1, &pic_param, &pic_param_buffer);
        CHECK_VASTATUS_RET(va_status, "vaCreateBuffer");
        VABufferDestroyer destroy_pic_param(va_dpy->va_dpy, pic_param_buffer);
 
@@ -404,7 +412,7 @@ shared_ptr<Frame> decode_jpeg_vaapi(const string &jpeg)
        }
 
        VABufferID iq_buffer;
-       va_status = vaCreateBuffer(va_dpy->va_dpy, config_id, VAIQMatrixBufferType, sizeof(iq), 1, &iq, &iq_buffer);
+       va_status = vaCreateBuffer(va_dpy->va_dpy, resources.context, VAIQMatrixBufferType, sizeof(iq), 1, &iq, &iq_buffer);
        CHECK_VASTATUS_RET(va_status, "vaCreateBuffer");
        VABufferDestroyer destroy_iq(va_dpy->va_dpy, iq_buffer);
 
@@ -438,7 +446,7 @@ shared_ptr<Frame> decode_jpeg_vaapi(const string &jpeg)
        }
 
        VABufferID huff_buffer;
-       va_status = vaCreateBuffer(va_dpy->va_dpy, config_id, VAHuffmanTableBufferType, sizeof(huff), 1, &huff, &huff_buffer);
+       va_status = vaCreateBuffer(va_dpy->va_dpy, resources.context, VAHuffmanTableBufferType, sizeof(huff), 1, &huff, &huff_buffer);
        CHECK_VASTATUS_RET(va_status, "vaCreateBuffer");
        VABufferDestroyer destroy_huff(va_dpy->va_dpy, huff_buffer);
 
@@ -468,19 +476,16 @@ shared_ptr<Frame> decode_jpeg_vaapi(const string &jpeg)
        parms.num_mcus = horiz_mcus * vert_mcus;
 
        VABufferID slice_param_buffer;
-       va_status = vaCreateBuffer(va_dpy->va_dpy, config_id, VASliceParameterBufferType, sizeof(parms), 1, &parms, &slice_param_buffer);
+       va_status = vaCreateBuffer(va_dpy->va_dpy, resources.context, VASliceParameterBufferType, sizeof(parms), 1, &parms, &slice_param_buffer);
        CHECK_VASTATUS_RET(va_status, "vaCreateBuffer");
        VABufferDestroyer destroy_slice_param(va_dpy->va_dpy, slice_param_buffer);
 
        // The actual data. VA-API will destuff and all for us.
        VABufferID data_buffer;
-       va_status = vaCreateBuffer(va_dpy->va_dpy, config_id, VASliceDataBufferType, dinfo.src->bytes_in_buffer, 1, const_cast<unsigned char *>(dinfo.src->next_input_byte), &data_buffer);
+       va_status = vaCreateBuffer(va_dpy->va_dpy, resources.context, VASliceDataBufferType, dinfo.src->bytes_in_buffer, 1, const_cast<unsigned char *>(dinfo.src->next_input_byte), &data_buffer);
        CHECK_VASTATUS_RET(va_status, "vaCreateBuffer");
        VABufferDestroyer destroy_data(va_dpy->va_dpy, data_buffer);
 
-       VAResources resources = get_va_resources(dinfo.image_width, dinfo.image_height);
-       ReleaseVAResources release(resources);
-
        va_status = vaBeginPicture(va_dpy->va_dpy, resources.context, resources.surface);
        CHECK_VASTATUS_RET(va_status, "vaBeginPicture");
        va_status = vaRenderPicture(va_dpy->va_dpy, resources.context, &pic_param_buffer, 1);
@@ -547,24 +552,46 @@ shared_ptr<Frame> decode_jpeg_vaapi(const string &jpeg)
 #else
        // Convert Y'CbCr to separate Y' and CbCr.
        frame->is_semiplanar = true;
-       frame->y.reset(new uint8_t[dinfo.image_width * dinfo.image_height]);
-       frame->cbcr.reset(new uint8_t[dinfo.image_width * dinfo.image_height]);
+
+       PBO pbo = global_pbo_pool->alloc_pbo();
+       size_t cbcr_offset = dinfo.image_width * dinfo.image_height;
+       uint8_t *y_pix = pbo.ptr;
+       uint8_t *cbcr_pix = pbo.ptr + cbcr_offset;
+
        const uint8_t *src = (const uint8_t *)mapped + resources.image.offsets[0];
        if (resources.image.pitches[0] == dinfo.image_width * 2) {
-               memcpy_interleaved(frame->cbcr.get(), frame->y.get(), src, dinfo.image_width * dinfo.image_height * 2);
+               memcpy_interleaved(cbcr_pix, y_pix, src, dinfo.image_width * dinfo.image_height * 2);
        } else {
                for (unsigned y = 0; y < dinfo.image_height; ++y) {
-                       memcpy_interleaved(frame->cbcr.get() + y * dinfo.image_width, frame->y.get() + y * dinfo.image_width,
+                       memcpy_interleaved(cbcr_pix + y * dinfo.image_width, y_pix + y * dinfo.image_width,
                                           src + y * resources.image.pitches[0], dinfo.image_width * 2);
                }
        }
+
+       glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo.pbo);
+       frame->y = create_texture_2d(dinfo.image_width, dinfo.image_height, GL_R8, GL_RED, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0));
+       frame->cbcr = create_texture_2d(dinfo.image_width / 2, dinfo.image_height, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, BUFFER_OFFSET(cbcr_offset));
+       glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+
+       glFlushMappedNamedBufferRange(pbo.pbo, 0, dinfo.image_width * dinfo.image_height * 2);
+       glMemoryBarrier(GL_PIXEL_BUFFER_BARRIER_BIT);
+       pbo.upload_done = RefCountedGLsync(GL_SYNC_GPU_COMMANDS_COMPLETE, /*flags=*/0);
+       frame->uploaded_ui_thread = pbo.upload_done;
+       frame->uploaded_interpolation = pbo.upload_done;
+       global_pbo_pool->release_pbo(move(pbo));
 #endif
        frame->width = dinfo.image_width;
        frame->height = dinfo.image_height;
        frame->chroma_subsampling_x = 2;
        frame->chroma_subsampling_y = 1;
-       frame->pitch_y = dinfo.image_width;
-       frame->pitch_chroma = dinfo.image_width / 2;
+
+       if (dinfo.marker_list != nullptr &&
+           dinfo.marker_list->marker == JPEG_APP0 + 1 &&
+           dinfo.marker_list->data_length >= 4 &&
+           memcmp(dinfo.marker_list->data, "Exif", 4) == 0) {
+               frame->exif_data.assign(reinterpret_cast<char *>(dinfo.marker_list->data),
+                       dinfo.marker_list->data_length);
+       }
 
        va_status = vaUnmapBuffer(va_dpy->va_dpy, resources.image.buf);
        CHECK_VASTATUS_RET(va_status, "vaUnmapBuffer");