From 44efd2eb014f80ad50533164882bcc37a5592c11 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sun, 3 Mar 2024 00:41:59 +0100 Subject: [PATCH 01/10] Set CEF autoplay policy to be more lenient. Requiring click-to-play for videos doesn't make sense in the CEF context; we don't even support interactivity, and most pages are going to be from trusted sources anyway. (Besides, any muted videos already autoplay just fine, and our CEF source is always muted.) --- nageru/nageru_cef_app.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/nageru/nageru_cef_app.cpp b/nageru/nageru_cef_app.cpp index b74c04b..70b9afe 100644 --- a/nageru/nageru_cef_app.cpp +++ b/nageru/nageru_cef_app.cpp @@ -18,6 +18,7 @@ void NageruCefApp::OnBeforeCommandLineProcessing( command_line->AppendSwitch("disable-gpu"); command_line->AppendSwitch("disable-gpu-compositing"); command_line->AppendSwitch("enable-begin-frame-scheduling"); + command_line->AppendSwitchWithValue("autoplay-policy", "no-user-gesture-required"); // https://bitbucket.org/chromiumembedded/cef/issues/2717/xmlhttprequest-empty-responsetext command_line->AppendSwitch("disable-web-security"); -- 2.39.2 From c2845f746537c747a9a0177d5aa6b8ec141572a4 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Tue, 7 May 2024 20:09:00 +0200 Subject: [PATCH 02/10] Fix a dangling reference (found by GCC 14). --- nageru/input_mapping_dialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nageru/input_mapping_dialog.cpp b/nageru/input_mapping_dialog.cpp index 2fe7e34..5f65d56 100644 --- a/nageru/input_mapping_dialog.cpp +++ b/nageru/input_mapping_dialog.cpp @@ -274,7 +274,7 @@ void InputMappingDialog::remove_clicked() void InputMappingDialog::updown_clicked(int direction) { assert(ui->table->selectedRanges().size() == 1); - const QTableWidgetSelectionRange &range = ui->table->selectedRanges()[0]; + QTableWidgetSelectionRange range = ui->table->selectedRanges()[0]; int a_row = range.bottomRow(); int b_row = range.bottomRow() + direction; -- 2.39.2 From d1065378c0f1899aa3a74e9350577ee2a0003a27 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Mon, 13 May 2024 17:56:46 +0200 Subject: [PATCH 03/10] Small refactoring in PBOAllocator::init_frame(). Decouple the frame creation (which is now a static function) from the act of pushing it on the freelist. This will make it easier to resize the frame dynamically later. --- nageru/pbo_frame_allocator.cpp | 16 ++++++++-------- nageru/pbo_frame_allocator.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/nageru/pbo_frame_allocator.cpp b/nageru/pbo_frame_allocator.cpp index 709a2bf..5910a02 100644 --- a/nageru/pbo_frame_allocator.cpp +++ b/nageru/pbo_frame_allocator.cpp @@ -47,7 +47,9 @@ PBOFrameAllocator::PBOFrameAllocator(bmusb::PixelFormat pixel_format, size_t fra { userdata.reset(new Userdata[num_queued_frames]); for (size_t i = 0; i < num_queued_frames; ++i) { - init_frame(i, frame_size, width, height, permissions, map_bits, generation); + Frame frame; + init_frame(frame, &userdata[i], this, pixel_format, frame_size, width, height, permissions, map_bits, buffer, generation); + freelist.push(frame); } glBindBuffer(buffer, 0); check_error(); @@ -55,7 +57,7 @@ PBOFrameAllocator::PBOFrameAllocator(bmusb::PixelFormat pixel_format, size_t fra check_error(); } -void PBOFrameAllocator::init_frame(size_t frame_idx, size_t frame_size, GLuint width, GLuint height, GLenum permissions, GLenum map_bits, int generation) +void PBOFrameAllocator::init_frame(Frame &frame, Userdata *ud, PBOFrameAllocator *owner, bmusb::PixelFormat pixel_format, size_t frame_size, GLuint width, GLuint height, GLenum permissions, GLenum map_bits, GLenum buffer, int generation) { GLuint pbo; glGenBuffers(1, &pbo); @@ -65,18 +67,16 @@ void PBOFrameAllocator::init_frame(size_t frame_idx, size_t frame_size, GLuint w glBufferStorage(buffer, frame_size, nullptr, permissions | GL_MAP_PERSISTENT_BIT); check_error(); - Frame frame; frame.data = (uint8_t *)glMapBufferRange(buffer, 0, frame_size, permissions | map_bits | GL_MAP_PERSISTENT_BIT); frame.data2 = frame.data + frame_size / 2; check_error(); frame.size = frame_size; - Userdata *ud = &userdata[frame_idx]; frame.userdata = ud; ud->generation = generation; ud->pbo = pbo; ud->pixel_format = pixel_format; ud->data_copy_malloc = new uint8_t[frame_size]; - frame.owner = this; + frame.owner = owner; // For 8-bit non-planar Y'CbCr, we ask the driver to split Y' and Cb/Cr // into separate textures. For 10-bit, the input format (v210) @@ -214,8 +214,6 @@ void PBOFrameAllocator::init_frame(size_t frame_idx, size_t frame_size, GLuint w assert(false); } } - - freelist.push(frame); } PBOFrameAllocator::~PBOFrameAllocator() @@ -465,7 +463,9 @@ void PBOFrameAllocator::reconfigure(bmusb::PixelFormat pixel_format, userdata.reset(new Userdata[num_queued_frames]); for (size_t i = 0; i < num_queued_frames; ++i) { - init_frame(i, frame_size, width, height, permissions, map_bits, generation); + Frame frame; + init_frame(frame, &userdata[i], this, pixel_format, frame_size, width, height, permissions, map_bits, buffer, generation); + freelist.push(frame); } // There may still be frames out with the old configuration diff --git a/nageru/pbo_frame_allocator.h b/nageru/pbo_frame_allocator.h index cff50aa..772d05e 100644 --- a/nageru/pbo_frame_allocator.h +++ b/nageru/pbo_frame_allocator.h @@ -101,7 +101,7 @@ public: }; private: - void init_frame(size_t frame_idx, size_t frame_size, GLuint width, GLuint height, GLenum permissions, GLenum map_bits, int generation); + static void init_frame(Frame &frame, Userdata *ud, PBOFrameAllocator *owner, bmusb::PixelFormat pixel_format, size_t frame_size, GLuint width, GLuint height, GLenum permissions, GLenum map_bits, GLenum buffer, int generation); void destroy_frame(Frame *frame); unsigned card_index; -- 2.39.2 From 52fff9ee540789363a6224dc75fa51de13650846 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Mon, 13 May 2024 20:11:14 +0200 Subject: [PATCH 04/10] Remove unused OpenGL surface(s). --- nageru/mixer.cpp | 3 --- nageru/mixer.h | 3 --- 2 files changed, 6 deletions(-) diff --git a/nageru/mixer.cpp b/nageru/mixer.cpp index b3e60fe..78f9ebf 100644 --- a/nageru/mixer.cpp +++ b/nageru/mixer.cpp @@ -646,9 +646,6 @@ void Mixer::configure_card(unsigned card_index, CaptureInterface *capture, CardT card->frame_allocator->reconfigure(pixel_format, FRAME_SIZE, global_flags.width, global_flags.height, card_index, mjpeg_encoder.get()); } card->capture->set_video_frame_allocator(card->frame_allocator.get()); - if (card->surface == nullptr) { - card->surface = create_surface_with_same_format(mixer_surface); - } while (!card->new_frames.empty()) card->new_frames.pop_front(); card->last_timecode = -1; card->capture->set_pixel_format(pixel_format); diff --git a/nageru/mixer.h b/nageru/mixer.h index 5549a24..c42d8f1 100644 --- a/nageru/mixer.h +++ b/nageru/mixer.h @@ -478,9 +478,6 @@ private: std::unique_ptr frame_allocator; - // Stuff for the OpenGL context (for texture uploading). - QSurface *surface = nullptr; - struct NewFrame { RefCountedFrame frame; int64_t length; // In TIMEBASE units. -- 2.39.2 From f9b263fff01d5692b52cfe371e5a4e9d3c80ad65 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Mon, 13 May 2024 22:57:59 +0200 Subject: [PATCH 05/10] Small #include fix. --- nageru/ffmpeg_capture.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nageru/ffmpeg_capture.cpp b/nageru/ffmpeg_capture.cpp index 6dbcbd6..34c2dd1 100644 --- a/nageru/ffmpeg_capture.cpp +++ b/nageru/ffmpeg_capture.cpp @@ -3,8 +3,7 @@ #include "shared/shared_defs.h" #include -#include -#include +#include #include #include #include @@ -13,6 +12,7 @@ #include #include #include +#include #include #include #include -- 2.39.2 From 41eed96d6c759b6f47c63d046110d69ec92b72ee Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Mon, 13 May 2024 23:16:22 +0200 Subject: [PATCH 06/10] Remove unused context.h duplicate. --- nageru/context.h | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 nageru/context.h diff --git a/nageru/context.h b/nageru/context.h deleted file mode 100644 index 13dbf24..0000000 --- a/nageru/context.h +++ /dev/null @@ -1,16 +0,0 @@ - -// Needs to be in its own file because Qt and libepoxy seemingly don't coexist well -// within the same file. - -class QSurface; -class QOpenGLContext; -class QSurfaceFormat; -class QGLWidget; - -extern bool using_egl; -extern QGLWidget *global_share_widget; -QSurface *create_surface(const QSurfaceFormat &format); -QSurface *create_surface_with_same_format(const QSurface *surface); -QOpenGLContext *create_context(const QSurface *surface); -bool make_current(QOpenGLContext *context, QSurface *surface); -void delete_context(QOpenGLContext *context); -- 2.39.2 From bb966861a939c3071f099d08e8a51afb63c36d3e Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Mon, 13 May 2024 23:18:05 +0200 Subject: [PATCH 07/10] Indent fixup. --- nageru/ffmpeg_capture.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nageru/ffmpeg_capture.cpp b/nageru/ffmpeg_capture.cpp index 34c2dd1..38ac591 100644 --- a/nageru/ffmpeg_capture.cpp +++ b/nageru/ffmpeg_capture.cpp @@ -426,9 +426,9 @@ void FFmpegCapture::producer_thread_func() } if (has_dequeue_callbacks) { - dequeue_cleanup_callback(); + dequeue_cleanup_callback(); has_dequeue_callbacks = false; - } + } } void FFmpegCapture::send_disconnected_frame() -- 2.39.2 From f8ad37841d9aaea3e6cfda093fcce4e1ec40294f Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Mon, 13 May 2024 23:45:46 +0200 Subject: [PATCH 08/10] Allow dynamic frame sizes for FFmpeg inputs. If we need a larger frame size than FRAME_SIZE for a frame, simply reallocate it; we're allowed to sleep in this path. This finally allows us to accept 2160p streams (assuming your GPU is fast enough, of course), at the cost of having to make an OpenGL context for each decoder thread (because allocating frames in GPU memory requires making a PBO and stuff). Unfortunately, we still cannot decode 2160p DeckLink frames because there's no OpenGL context there, but it's a good start. --- nageru/decklink_capture.cpp | 9 ++++++++- nageru/defs.h | 13 +++++++++++- nageru/ffmpeg_capture.cpp | 36 +++++++++++++++++++++++++++------- nageru/ffmpeg_capture.h | 6 ++++-- nageru/kaeru.cpp | 2 +- nageru/mixer.cpp | 4 ++-- nageru/pbo_frame_allocator.cpp | 22 +++++++++++++++++++-- nageru/theme.cpp | 15 ++++++++++---- nageru/theme.h | 9 ++++++++- 9 files changed, 95 insertions(+), 21 deletions(-) diff --git a/nageru/decklink_capture.cpp b/nageru/decklink_capture.cpp index e239ec9..90d86b1 100644 --- a/nageru/decklink_capture.cpp +++ b/nageru/decklink_capture.cpp @@ -256,7 +256,14 @@ HRESULT STDMETHODCALLTYPE DeckLinkCapture::VideoInputFrameArrived( assert(stride == width * 2); } - current_video_frame = video_frame_allocator->create_frame(width, height, stride); + if (width * stride > FRAME_SIZE) { + // TODO: If we had an OpenGL context here, calling create_frame() + // would be completely fine. + fprintf(stderr, "Card %u: Captured frame %d x %d (stride %d) would be larger than supported frame size (%d > %d), skipping.\n", + card_index, width, height, stride, width * stride, FRAME_SIZE); + } else { + current_video_frame = video_frame_allocator->create_frame(width, height, stride); + } if (current_video_frame.data != nullptr) { const uint8_t *src; video_frame->GetBytes((void **)&src); diff --git a/nageru/defs.h b/nageru/defs.h index 09743a5..369c9b8 100644 --- a/nageru/defs.h +++ b/nageru/defs.h @@ -6,7 +6,18 @@ // #define MAX_VIDEO_CARDS 16 // defined in shared_defs.h. #define MAX_ALSA_CARDS 16 #define MAX_BUSES 256 // Audio buses. -#define FRAME_SIZE (8 << 20) // 8 MB. (FIXME: Not enough for a 2160p frame!) + +// FRAME_SIZE is the default frame size, in bytes. FFmpeg inputs (video files and SRT streams) +// can allocate larger frames as needed; USB and DeckLink outputs always use FRAME_SIZE. +// We should eventually add support for at least DeckLink outputs, allowing us to capture +// 2160p frames. Also, it would allow us to lower the default frame size to the maximum +// bmusb supports (2 MB just about covers 1080i 4:2:2, then add some for 10-bit?) to waste +// less memory. +// +// As a general sanity check, we also have a MAX_FRAME_SIZE that even dynamic allocation +// will not go past. +#define FRAME_SIZE (8 << 20) // 8 MB (just enough for 1080p RGBA). +#define MAX_FRAME_SIZE (140 << 20) // 140 MB; enough for 8192*4320 RGBA and then some. // For deinterlacing. See also comments on InputState. #define FRAME_HISTORY_LENGTH 5 diff --git a/nageru/ffmpeg_capture.cpp b/nageru/ffmpeg_capture.cpp index 38ac591..b00cab6 100644 --- a/nageru/ffmpeg_capture.cpp +++ b/nageru/ffmpeg_capture.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -20,6 +21,7 @@ #include #include #include +#include extern "C" { #include @@ -56,6 +58,7 @@ extern "C" { #include #include "bmusb/bmusb.h" +#include "shared/context.h" #include "shared/ffmpeg_raii.h" #include "ffmpeg_util.h" #include "flags.h" @@ -283,8 +286,8 @@ RGBTriplet get_neutral_color(AVDictionary *metadata) } // namespace -FFmpegCapture::FFmpegCapture(const string &filename, unsigned width, unsigned height) - : filename(filename), width(width), height(height), video_timebase{1, 1} +FFmpegCapture::FFmpegCapture(const string &filename, unsigned width, unsigned height, QSurface *surface) + : filename(filename), width(width), height(height), video_timebase{1, 1}, surface(surface) { description = "Video: " + filename; @@ -294,12 +297,13 @@ FFmpegCapture::FFmpegCapture(const string &filename, unsigned width, unsigned he } #ifdef HAVE_SRT -FFmpegCapture::FFmpegCapture(int srt_sock, const string &stream_id) +FFmpegCapture::FFmpegCapture(int srt_sock, const string &stream_id, QSurface *surface) : srt_sock(srt_sock), width(0), // Don't resize; SRT streams typically have stable resolution, and should behave much like regular cards in general. height(0), pixel_format(bmusb::PixelFormat_8BitYCbCrPlanar), - video_timebase{1, 1} + video_timebase{1, 1}, + surface(surface) { if (stream_id.empty()) { description = "SRT stream"; @@ -323,6 +327,7 @@ FFmpegCapture::~FFmpegCapture() srt_close(srt_sock); } #endif + delete surface; } void FFmpegCapture::configure_card() @@ -383,6 +388,21 @@ void FFmpegCapture::producer_thread_func() snprintf(thread_name, sizeof(thread_name), "FFmpeg_C_%d", card_index); pthread_setname_np(pthread_self(), thread_name); + // We need a context in case create_frame() needs to reallocate something. + // (If none is given, we are probably in Kaeru, which uses MallocFrameAllocator + // anyway, which doesn't reallocate currently and definitely doesn't need + // an active OpenGL context to do so.) + QOpenGLContext *context = nullptr; + if (surface != nullptr) { + context = create_context(this->surface); + eglBindAPI(EGL_OPENGL_API); + if (!make_current(context, this->surface)) { + printf("display=%p surface=%p context=%p curr=%p err=%d\n", eglGetCurrentDisplay(), this->surface, context, eglGetCurrentContext(), + eglGetError()); + abort(); + } + } + while (!producer_thread_should_quit.should_quit()) { string filename_copy; { @@ -429,6 +449,8 @@ void FFmpegCapture::producer_thread_func() dequeue_cleanup_callback(); has_dequeue_callbacks = false; } + + delete_context(context); } void FFmpegCapture::send_disconnected_frame() @@ -1125,7 +1147,7 @@ UniqueFrame FFmpegCapture::make_video_frame(const AVFrame *frame, const string & { *error = false; - UniqueFrame video_frame(video_frame_allocator->alloc_frame()); + UniqueFrame video_frame(video_frame_allocator->create_frame(frame->width, frame->height, frame->width)); if (video_frame->data == nullptr) { return video_frame; } @@ -1190,8 +1212,8 @@ UniqueFrame FFmpegCapture::make_video_frame(const AVFrame *frame, const string & // FIXME: Currently, if the video is too high-res for one of the allocated // frames, we simply refuse to scale it here to avoid crashes. It would be better // if we could somehow signal getting larger frames, especially as 4K is a thing now. - if (video_frame->len > FRAME_SIZE) { - fprintf(stderr, "%s: Decoded frame would be larger than supported FRAME_SIZE (%zu > %u), not decoding.\n", pathname.c_str(), video_frame->len, FRAME_SIZE); + if (video_frame->len > video_frame->size) { + fprintf(stderr, "%s: Decoded frame would be larger than supported frame size (%zu > %zu), not decoding.\n", pathname.c_str(), video_frame->len, video_frame->size); *error = true; return video_frame; } diff --git a/nageru/ffmpeg_capture.h b/nageru/ffmpeg_capture.h index 122bf86..b974583 100644 --- a/nageru/ffmpeg_capture.h +++ b/nageru/ffmpeg_capture.h @@ -60,14 +60,15 @@ struct AVFormatContext; struct AVFrame; struct AVRational; struct AVPacket; +class QSurface; class FFmpegCapture : public bmusb::CaptureInterface { public: - FFmpegCapture(const std::string &filename, unsigned width, unsigned height); + FFmpegCapture(const std::string &filename, unsigned width, unsigned height, QSurface *surface); #ifdef HAVE_SRT // Takes ownership of the SRT client socket. - FFmpegCapture(int srt_sock, const std::string &stream_id); + FFmpegCapture(int srt_sock, const std::string &stream_id, QSurface *surface); #endif ~FFmpegCapture(); @@ -351,6 +352,7 @@ private: // -1 is strictly speaking outside the range of the enum, but hopefully, it will be alright. AVColorSpace last_colorspace = static_cast(-1); AVChromaLocation last_chroma_location = static_cast(-1); + QSurface *const surface; }; #endif // !defined(_FFMPEG_CAPTURE_H) diff --git a/nageru/kaeru.cpp b/nageru/kaeru.cpp index 2bdc686..caa71c6 100644 --- a/nageru/kaeru.cpp +++ b/nageru/kaeru.cpp @@ -267,7 +267,7 @@ int main(int argc, char *argv[]) } global_x264_encoder = x264_encoder.get(); - FFmpegCapture video(argv[optind], global_flags.width, global_flags.height); + FFmpegCapture video(argv[optind], global_flags.width, global_flags.height, /*surface=*/nullptr); video.set_pixel_format(FFmpegCapture::PixelFormat_NV12); if (global_flags.transcode_video) { video.set_frame_callback(bind(video_frame_callback, &video, x264_encoder.get(), audio_encoder.get(), _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11)); diff --git a/nageru/mixer.cpp b/nageru/mixer.cpp index 78f9ebf..e766d88 100644 --- a/nageru/mixer.cpp +++ b/nageru/mixer.cpp @@ -409,7 +409,7 @@ Mixer::Mixer(const QSurfaceFormat &format) } // Must be instantiated after VideoEncoder has initialized global_flags.use_zerocopy. - theme.reset(new Theme(global_flags.theme_filename, global_flags.theme_dirs, resource_pool.get())); + theme.reset(new Theme(global_flags.theme_filename, global_flags.theme_dirs, resource_pool.get(), create_surface(format))); // Must be instantiated after the theme, as the theme decides the number of FFmpeg inputs. std::vector video_inputs = theme->get_video_inputs(); @@ -1610,7 +1610,7 @@ void Mixer::handle_hotplugged_cards() fprintf(stderr, "New SRT stream connected (%s), choosing slot %d.\n", stream_id.c_str(), free_card_index); } CaptureCard *card = &cards[free_card_index]; - FFmpegCapture *capture = new FFmpegCapture(sock, stream_id); + FFmpegCapture *capture = new FFmpegCapture(sock, stream_id, create_surface_with_same_format(mixer_surface)); capture->set_card_index(free_card_index); configure_card(free_card_index, capture, CardType::FFMPEG_INPUT, /*output=*/nullptr, /*is_srt_card=*/true); card->srt_metrics.update_srt_stats(sock); // Initial zero stats. diff --git a/nageru/pbo_frame_allocator.cpp b/nageru/pbo_frame_allocator.cpp index 5910a02..a649232 100644 --- a/nageru/pbo_frame_allocator.cpp +++ b/nageru/pbo_frame_allocator.cpp @@ -13,6 +13,7 @@ #include #include "mjpeg_encoder.h" +#include "defs.h" #include "shared/va_resource_pool.h" #include "v210_converter.h" #include "shared/va_display.h" @@ -310,6 +311,11 @@ bmusb::FrameAllocator::Frame PBOFrameAllocator::create_frame(size_t width, size_ { Frame vf; + size_t desired_frame_bytes = width * stride; + if (stride > 8192 * 4 || height > 8192 || desired_frame_bytes > MAX_FRAME_SIZE) { + return vf; + } + { lock_guard lock(freelist_mutex); if (freelist.empty()) { @@ -322,10 +328,22 @@ bmusb::FrameAllocator::Frame PBOFrameAllocator::create_frame(size_t width, size_ freelist.pop(); } } - vf.len = 0; - vf.overflow = 0; Userdata *userdata = (Userdata *)vf.userdata; + assert(generation == userdata->generation); + if (vf.size < desired_frame_bytes || (vf.size > FRAME_SIZE && vf.size > desired_frame_bytes * 2)) { + // Frame is either too small or way too large, so reallocate it. + // Note that width and height now automatically becomes the right size + // (the one we just asked for, instead of the default for the allocator, + // which is generally the global resolution); it doesn't matter + // for correctness, since we'll recreate the texture on upload if needed, + // but it is nice to save that step. + destroy_frame(&vf); + init_frame(vf, userdata, this, pixel_format, std::max(desired_frame_bytes, FRAME_SIZE), width, height, permissions, map_bits, buffer, generation); + }; + + vf.len = 0; + vf.overflow = 0; if (mjpeg_encoder != nullptr && mjpeg_encoder->should_encode_mjpeg_for_card(card_index)) { diff --git a/nageru/theme.cpp b/nageru/theme.cpp index 06d147f..afef822 100644 --- a/nageru/theme.cpp +++ b/nageru/theme.cpp @@ -54,6 +54,7 @@ #include "mainwindow.h" #include "pbo_frame_allocator.h" #include "scene.h" +#include "shared/context.h" class Mixer; @@ -450,6 +451,9 @@ int ImageInput_new(lua_State* L) return wrap_lua_object_nonowned(L, "ImageInput", filename); } +} // namespace + +// Must be non-namespaced due to friend declaration. int VideoInput_new(lua_State* L) { assert(lua_gettop(L) == 2); @@ -460,17 +464,19 @@ int VideoInput_new(lua_State* L) print_warning(L, "Invalid enum %d used for video format, choosing Y'CbCr.\n", pixel_format); pixel_format = bmusb::PixelFormat_8BitYCbCrPlanar; } - int ret = wrap_lua_object_nonowned(L, "VideoInput", filename, global_flags.width, global_flags.height); + Theme *theme = get_theme_updata(L); + int ret = wrap_lua_object_nonowned(L, "VideoInput", filename, global_flags.width, global_flags.height, create_surface_with_same_format(theme->surface)); if (ret == 1) { FFmpegCapture **capture = (FFmpegCapture **)lua_touserdata(L, -1); (*capture)->set_pixel_format(bmusb::PixelFormat(pixel_format)); - Theme *theme = get_theme_updata(L); theme->register_video_input(*capture); } return ret; } +namespace { + int VideoInput_rewind(lua_State* L) { assert(lua_gettop(L) == 1); @@ -1513,8 +1519,8 @@ int Nageru_set_audio_bus_eq_level_db(lua_State *L) return 0; } -Theme::Theme(const string &filename, const vector &search_dirs, ResourcePool *resource_pool) - : resource_pool(resource_pool), signal_to_card_mapping(global_flags.default_stream_mapping) +Theme::Theme(const string &filename, const vector &search_dirs, ResourcePool *resource_pool, QSurface *surface) + : resource_pool(resource_pool), signal_to_card_mapping(global_flags.default_stream_mapping), surface(surface) { // Defaults. channel_names[0] = "Live"; @@ -1631,6 +1637,7 @@ Theme::~Theme() { theme_menu.reset(); lua_close(L); + // Leak the surface. } void Theme::register_globals() diff --git a/nageru/theme.h b/nageru/theme.h index 7fe0e85..2c6021e 100644 --- a/nageru/theme.h +++ b/nageru/theme.h @@ -24,6 +24,7 @@ class Scene; class CEFCapture; class FFmpegCapture; class LiveInputWrapper; +class QSurface; struct InputState; namespace movit { @@ -89,7 +90,7 @@ struct InputStateInfo { class Theme { public: - Theme(const std::string &filename, const std::vector &search_dirs, movit::ResourcePool *resource_pool); + Theme(const std::string &filename, const std::vector &search_dirs, movit::ResourcePool *resource_pool, QSurface *surface); ~Theme(); struct Chain { @@ -244,6 +245,11 @@ private: std::map channel_signals; // Set using Nageru.set_channel_signal(). Protected by . std::map channel_supports_wb; // Set using Nageru.set_supports_wb(). Protected by . + // Used to construct OpenGL contexts for VideoInputs. Needs to be available + // during the entire lifetime of Theme, since they may be created basically + // at any time. + const QSurface *surface; + friend class LiveInputWrapper; friend class Scene; friend int ThemeMenu_set(lua_State *L); @@ -251,6 +257,7 @@ private: friend int Nageru_set_num_channels(lua_State *L); friend int Nageru_set_channel_signal(lua_State *L); friend int Nageru_set_supports_wb(lua_State *L); + friend int VideoInput_new(lua_State* L); }; // LiveInputWrapper is a facade on top of an YCbCrInput, exposed to -- 2.39.2 From c96a3a9fbcf2327417dde10bb568d8e01b6cc1e3 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Mon, 13 May 2024 23:51:43 +0200 Subject: [PATCH 09/10] Various indent fixes. --- nageru/analyzer.cpp | 8 ++++---- nageru/av1_encoder.cpp | 2 +- nageru/benchmark_audio_mixer.cpp | 6 +++--- nageru/correlation_measurer.cpp | 2 +- nageru/ffmpeg_capture.cpp | 6 +++--- nageru/filter.cpp | 4 ++-- nageru/midi_mapper.cpp | 2 +- nageru/midi_mapping_dialog.cpp | 2 +- nageru/mixer.cpp | 2 +- nageru/pbo_frame_allocator.cpp | 6 +++--- nageru/resampling_queue.cpp | 4 ++-- nageru/theme.cpp | 8 ++++---- nageru/timecode_renderer.cpp | 14 +++++++------- nageru/v210_converter.cpp | 6 +++--- nageru/x264_dynamic.cpp | 2 +- 15 files changed, 37 insertions(+), 37 deletions(-) diff --git a/nageru/analyzer.cpp b/nageru/analyzer.cpp index 5ee873b..a9bfb6d 100644 --- a/nageru/analyzer.cpp +++ b/nageru/analyzer.cpp @@ -71,9 +71,9 @@ Analyzer::Analyzer() signal_changed(); ui->grabbed_frame_label->installEventFilter(this); - glGenBuffers(1, &pbo); - glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo); - glBufferData(GL_PIXEL_PACK_BUFFER_ARB, global_flags.width * global_flags.height * 4, nullptr, GL_STREAM_READ); + glGenBuffers(1, &pbo); + glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo); + glBufferData(GL_PIXEL_PACK_BUFFER_ARB, global_flags.width * global_flags.height * 4, nullptr, GL_STREAM_READ); } Analyzer::~Analyzer() @@ -238,7 +238,7 @@ bool Analyzer::eventFilter(QObject *watched, QEvent *event) ui->blue_label->setText(u8"—"); ui->hex_label->setText(u8"#—"); } - return false; + return false; } void Analyzer::grab_pixel(int x, int y) diff --git a/nageru/av1_encoder.cpp b/nageru/av1_encoder.cpp index 13f0e30..bb6c17b 100644 --- a/nageru/av1_encoder.cpp +++ b/nageru/av1_encoder.cpp @@ -212,7 +212,7 @@ void AV1Encoder::init_av1() global_headers = string(reinterpret_cast(header->p_buffer), header->n_filled_len); svt_av1_enc_stream_header_release(header); // Don't care about errors. - } + } } void AV1Encoder::encoder_thread_func() diff --git a/nageru/benchmark_audio_mixer.cpp b/nageru/benchmark_audio_mixer.cpp index 935295b..9eefc73 100644 --- a/nageru/benchmark_audio_mixer.cpp +++ b/nageru/benchmark_audio_mixer.cpp @@ -51,9 +51,9 @@ void reset_lcgrand() void callback(float level_lufs, float peak_db, std::vector bus_levels, - float global_level_lufs, float range_low_lufs, float range_high_lufs, - float final_makeup_gain_db, - float correlation) + float global_level_lufs, float range_low_lufs, float range_high_lufs, + float final_makeup_gain_db, + float correlation) { // Empty. } diff --git a/nageru/correlation_measurer.cpp b/nageru/correlation_measurer.cpp index 6be16b8..0fa4749 100644 --- a/nageru/correlation_measurer.cpp +++ b/nageru/correlation_measurer.cpp @@ -28,7 +28,7 @@ using namespace std; CorrelationMeasurer::CorrelationMeasurer(unsigned sample_rate, float lowpass_cutoff_hz, - float falloff_seconds) + float falloff_seconds) : w1(2.0 * M_PI * lowpass_cutoff_hz / sample_rate), w2(1.0 / (falloff_seconds * sample_rate)) { diff --git a/nageru/ffmpeg_capture.cpp b/nageru/ffmpeg_capture.cpp index b00cab6..14ad91f 100644 --- a/nageru/ffmpeg_capture.cpp +++ b/nageru/ffmpeg_capture.cpp @@ -1116,9 +1116,9 @@ void FFmpegCapture::convert_audio(const AVFrame *audio_avframe, FrameAllocator:: int out_samples = swr_convert(resampler, &data, num_samples_room, const_cast(audio_avframe->data), audio_avframe->nb_samples); if (out_samples < 0) { - fprintf(stderr, "Audio conversion failed.\n"); - abort(); - } + fprintf(stderr, "Audio conversion failed.\n"); + abort(); + } audio_frame->len += out_samples * bytes_per_sample; } diff --git a/nageru/filter.cpp b/nageru/filter.cpp index 835ec6c..e466951 100644 --- a/nageru/filter.cpp +++ b/nageru/filter.cpp @@ -23,8 +23,8 @@ using namespace std; #else // !defined(__SSE__) union uint_float { - float f; - unsigned int i; + float f; + unsigned int i; }; #define early_undenormalise(sample) { \ uint_float uf; \ diff --git a/nageru/midi_mapper.cpp b/nageru/midi_mapper.cpp index 2fd6dbb..e7b9068 100644 --- a/nageru/midi_mapper.cpp +++ b/nageru/midi_mapper.cpp @@ -49,7 +49,7 @@ void MIDIMapper::set_midi_mapping(const MIDIMappingProto &new_mapping) } num_controller_banks = min(max(mapping_proto->num_controller_banks(), 1), 5); - current_controller_bank = 0; + current_controller_bank = 0; receiver->clear_all_highlights(); update_highlights(); diff --git a/nageru/midi_mapping_dialog.cpp b/nageru/midi_mapping_dialog.cpp index 76776d2..2fa8fbe 100644 --- a/nageru/midi_mapping_dialog.cpp +++ b/nageru/midi_mapping_dialog.cpp @@ -158,7 +158,7 @@ int get_light_mapping(const MIDIMappingProto &mapping_proto, size_t bus_idx, int MIDIMappingDialog::MIDIMappingDialog(MIDIMapper *mapper) : ui(new Ui::MIDIMappingDialog), - mapper(mapper) + mapper(mapper) { ui->setupUi(this); diff --git a/nageru/mixer.cpp b/nageru/mixer.cpp index e766d88..b986ed5 100644 --- a/nageru/mixer.cpp +++ b/nageru/mixer.cpp @@ -809,7 +809,7 @@ int unwrap_timecode(uint16_t current_wrapped, int last) void Mixer::bm_frame(unsigned card_index, uint16_t timecode, FrameAllocator::Frame video_frame, size_t video_offset, VideoFormat video_format, - FrameAllocator::Frame audio_frame, size_t audio_offset, AudioFormat audio_format) + FrameAllocator::Frame audio_frame, size_t audio_offset, AudioFormat audio_format) { DeviceSpec device{InputSourceType::CAPTURE_CARD, card_index}; CaptureCard *card = &cards[card_index]; diff --git a/nageru/pbo_frame_allocator.cpp b/nageru/pbo_frame_allocator.cpp index a649232..6ebe13a 100644 --- a/nageru/pbo_frame_allocator.cpp +++ b/nageru/pbo_frame_allocator.cpp @@ -35,7 +35,7 @@ void set_clamp_to_edge() } // namespace PBOFrameAllocator::PBOFrameAllocator(bmusb::PixelFormat pixel_format, size_t frame_size, GLuint width, GLuint height, unsigned card_index, MJPEGEncoder *mjpeg_encoder, size_t num_queued_frames, GLenum buffer, GLenum permissions, GLenum map_bits) - : card_index(card_index), + : card_index(card_index), mjpeg_encoder(mjpeg_encoder), pixel_format(pixel_format), buffer(buffer), @@ -281,7 +281,7 @@ void PBOFrameAllocator::destroy_frame(Frame *frame) bmusb::FrameAllocator::Frame PBOFrameAllocator::alloc_frame() { - Frame vf; + Frame vf; lock_guard lock(freelist_mutex); // Meh. if (freelist.empty()) { @@ -309,7 +309,7 @@ bmusb::FrameAllocator::Frame PBOFrameAllocator::alloc_frame() bmusb::FrameAllocator::Frame PBOFrameAllocator::create_frame(size_t width, size_t height, size_t stride) { - Frame vf; + Frame vf; size_t desired_frame_bytes = width * stride; if (stride > 8192 * 4 || height > 8192 || desired_frame_bytes > MAX_FRAME_SIZE) { diff --git a/nageru/resampling_queue.cpp b/nageru/resampling_queue.cpp index a7756e4..94a8e16 100644 --- a/nageru/resampling_queue.cpp +++ b/nageru/resampling_queue.cpp @@ -44,8 +44,8 @@ ResamplingQueue::ResamplingQueue(const std::string &debug_description, unsigned // Prime the resampler so there's no more delay. vresampler.inp_count = vresampler.inpsize() / 2 - 1; - vresampler.out_count = 1048576; - vresampler.process (); + vresampler.out_count = 1048576; + vresampler.process(); } void ResamplingQueue::add_input_samples(steady_clock::time_point ts, const float *samples, ssize_t num_samples, ResamplingQueue::RateAdjustmentPolicy rate_adjustment_policy) diff --git a/nageru/theme.cpp b/nageru/theme.cpp index afef822..192b27e 100644 --- a/nageru/theme.cpp +++ b/nageru/theme.cpp @@ -124,9 +124,9 @@ InputStateInfo::InputStateInfo(const InputState &input_state) // An effect that does nothing. class IdentityEffect : public Effect { public: - IdentityEffect() {} - string effect_type_id() const override { return "IdentityEffect"; } - string output_fragment_shader() override { return read_file("identity.frag"); } + IdentityEffect() {} + string effect_type_id() const override { return "IdentityEffect"; } + string output_fragment_shader() override { return read_file("identity.frag"); } }; Effect *instantiate_effect(EffectChain *chain, EffectType effect_type) @@ -1527,7 +1527,7 @@ Theme::Theme(const string &filename, const vector &search_dirs, Resource channel_names[1] = "Preview"; L = luaL_newstate(); - luaL_openlibs(L); + luaL_openlibs(L); // Search through all directories until we find a file that will load // (as in, does not return LUA_ERRFILE); then run it. We store load errors diff --git a/nageru/timecode_renderer.cpp b/nageru/timecode_renderer.cpp index 745e6ca..23d0f00 100644 --- a/nageru/timecode_renderer.cpp +++ b/nageru/timecode_renderer.cpp @@ -65,11 +65,11 @@ TimecodeRenderer::TimecodeRenderer(movit::ResourcePool *resource_pool, unsigned TimecodeRenderer::~TimecodeRenderer() { resource_pool->release_2d_texture(tex); - check_error(); + check_error(); resource_pool->release_glsl_program(program_num); - check_error(); + check_error(); glDeleteBuffers(1, &vbo); - check_error(); + check_error(); } string TimecodeRenderer::get_timecode_text(double pts, unsigned frame_num) @@ -152,15 +152,15 @@ void TimecodeRenderer::render_buffer_to_fbo(GLuint fbo) check_error(); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, display_width, height, GL_RED, GL_UNSIGNED_BYTE, image->bits()); - check_error(); + check_error(); glUseProgram(program_num); check_error(); glUniform1i(texture_sampler_uniform, 0); - check_error(); + check_error(); - glBindBuffer(GL_ARRAY_BUFFER, vbo); - check_error(); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + check_error(); for (GLint attr_index : { position_attribute_index, texcoord_attribute_index }) { if (attr_index == -1) continue; diff --git a/nageru/v210_converter.cpp b/nageru/v210_converter.cpp index b9138af..c144952 100644 --- a/nageru/v210_converter.cpp +++ b/nageru/v210_converter.cpp @@ -42,7 +42,7 @@ void v210Converter::precompile_shader(unsigned width) char buf[16]; snprintf(buf, sizeof(buf), "%u", num_local_work_groups); - string shader_src = R"(#version 150 + string shader_src = R"(#version 150 #extension GL_ARB_compute_shader : enable #extension GL_ARB_shader_image_load_store : enable layout(local_size_x = )" + string(buf) + R"() in; @@ -144,9 +144,9 @@ void v210Converter::convert(GLuint tex_src, GLuint tex_dst, unsigned width, unsi check_error(); glUniform1i(shader.outbuf_pos, 1); check_error(); - glBindImageTexture(0, tex_src, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGB10_A2); + glBindImageTexture(0, tex_src, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGB10_A2); check_error(); - glBindImageTexture(1, tex_dst, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGB10_A2); + glBindImageTexture(1, tex_dst, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGB10_A2); check_error(); // Actually run the shader. diff --git a/nageru/x264_dynamic.cpp b/nageru/x264_dynamic.cpp index 8cdf42b..77ba357 100644 --- a/nageru/x264_dynamic.cpp +++ b/nageru/x264_dynamic.cpp @@ -58,7 +58,7 @@ X264Dynamic load_x264_for_bit_depth(unsigned depth) x264_suffix = string(ptr, (m->l_name + strlen(m->l_name)) - ptr); break; } - } + } dlclose(handle); if (x264_dir.empty()) { -- 2.39.2 From f23b02805e975b0450af662ca89d4f50105ad325 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Tue, 14 May 2024 00:01:14 +0200 Subject: [PATCH 10/10] Fix a Clang 19 warning. Clang rightfully pointed out that VLAs are a non-standard extension. We can do just fine with three small heap allocations per software-decoded JPEG (the main path is VA-API anyway). --- futatabi/jpeg_frame_view.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/futatabi/jpeg_frame_view.cpp b/futatabi/jpeg_frame_view.cpp index 6ab1948..6dafb3e 100644 --- a/futatabi/jpeg_frame_view.cpp +++ b/futatabi/jpeg_frame_view.cpp @@ -182,8 +182,10 @@ shared_ptr decode_jpeg(const string &jpeg) } if (!error_mgr.run([&dinfo, &y_pix, &cb_pix, &cr_pix, pitch_y, pitch_chroma, v_mcu_size, mcu_height_blocks] { - JSAMPROW yptr[v_mcu_size], cbptr[v_mcu_size], crptr[v_mcu_size]; - JSAMPARRAY data[3] = { yptr, cbptr, crptr }; + unique_ptr yptr(new JSAMPROW[v_mcu_size]); + unique_ptr cbptr(new JSAMPROW[v_mcu_size]); + unique_ptr crptr(new JSAMPROW[v_mcu_size]); + JSAMPARRAY data[3] = { yptr.get(), cbptr.get(), crptr.get() }; for (unsigned y = 0; y < mcu_height_blocks; ++y) { // NOTE: The last elements of cbptr/crptr will be unused for vertically subsampled chroma. for (unsigned yy = 0; yy < v_mcu_size; ++yy) { -- 2.39.2