]> git.sesse.net Git - nageru/blob - analyzer.cpp
Add a display of the grabbed frame to the analyzer window.
[nageru] / analyzer.cpp
1 #include "analyzer.h"
2
3 #include <QDialogButtonBox>
4 #include <QSurface>
5
6 #include <movit/resource_pool.h>
7 #include <movit/util.h>
8
9 #include "context.h"
10 #include "flags.h"
11 #include "mixer.h"
12 #include "ui_analyzer.h"
13
14 using namespace std;
15
16 Analyzer::Analyzer()
17         : ui(new Ui::Analyzer)
18 {
19         ui->setupUi(this);
20
21         //connect(ui->button_box, &QDialogButtonBox::accepted, [this]{ this->close(); });
22
23         ui->input_box->addItem("Live", Mixer::OUTPUT_LIVE);
24         ui->input_box->addItem("Preview", Mixer::OUTPUT_PREVIEW);
25         unsigned num_channels = global_mixer->get_num_channels();
26         for (unsigned channel_idx = 0; channel_idx < num_channels; ++channel_idx) {
27                 Mixer::Output channel = static_cast<Mixer::Output>(Mixer::OUTPUT_INPUT0 + channel_idx); 
28                 string name = global_mixer->get_channel_name(channel);
29                 ui->input_box->addItem(QString::fromStdString(name), channel);
30         }
31
32         connect(ui->grab_btn, &QPushButton::clicked, bind(&Analyzer::grab_clicked, this));
33         connect(ui->input_box, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), bind(&Analyzer::signal_changed, this));
34         signal_changed();
35
36         surface = create_surface(QSurfaceFormat::defaultFormat());
37         context = create_context(surface);
38
39         if (!make_current(context, surface)) {
40                 printf("oops\n");
41                 exit(1);
42         }
43
44         glGenBuffers(1, &pbo);
45         glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo);
46         glBufferData(GL_PIXEL_PACK_BUFFER_ARB, global_flags.width * global_flags.height * 4, NULL, GL_STREAM_READ);
47 }
48
49 Analyzer::~Analyzer()
50 {
51         if (!make_current(context, surface)) {
52                 printf("oops\n");
53                 exit(1);
54         }
55         glDeleteBuffers(1, &pbo);
56         check_error();
57         if (resource_pool != nullptr) {
58                 resource_pool->clean_context();
59         }
60         delete_context(context);
61         delete surface;  // TODO?
62 }
63
64 void Analyzer::grab_clicked()
65 {
66         Mixer::Output channel = static_cast<Mixer::Output>(ui->input_box->currentData().value<int>());
67
68         if (!make_current(context, surface)) {
69                 printf("oops\n");
70                 exit(1);
71         }
72
73         Mixer::DisplayFrame frame;
74         if (!global_mixer->get_display_frame(channel, &frame)) {
75                 printf("Not ready yet\n");
76                 return;
77         }
78
79         // Set up an FBO to render into.
80         if (resource_pool == nullptr) {
81                 resource_pool = frame.chain->get_resource_pool();
82         } else {
83                 assert(resource_pool == frame.chain->get_resource_pool());
84         }
85         GLuint fbo_tex = resource_pool->create_2d_texture(GL_RGBA8, global_flags.width, global_flags.height);
86         check_error();
87         GLuint fbo = resource_pool->create_fbo(fbo_tex);
88         check_error();
89
90         glWaitSync(frame.ready_fence.get(), /*flags=*/0, GL_TIMEOUT_IGNORED);
91         check_error();
92         frame.setup_chain();
93         check_error();
94         glDisable(GL_FRAMEBUFFER_SRGB);
95         check_error();
96         frame.chain->render_to_fbo(fbo, global_flags.width, global_flags.height);
97         check_error();
98
99         // Read back to memory.
100         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
101         check_error();
102         glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
103         check_error();
104         glReadPixels(0, 0, global_flags.width, global_flags.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, BUFFER_OFFSET(0));
105         check_error();
106
107         unsigned char *buf = (unsigned char *)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
108         check_error();
109
110         QImage img(global_flags.width, global_flags.height, QImage::Format_ARGB32_Premultiplied);
111         size_t pitch = global_flags.width * 4;
112         for (int y = 0; y < global_flags.height; ++y) {
113                 memcpy(img.scanLine(global_flags.height - y - 1), buf + y * pitch, pitch);
114         }
115
116         QPixmap pixmap;
117         pixmap.convertFromImage(QImage(img));
118         ui->grabbed_frame_label->setPixmap(pixmap);
119
120         int r_hist[256] = {0}, g_hist[256] = {0}, b_hist[256] = {0};
121         const unsigned char *ptr = buf;
122         for (int i = 0; i < global_flags.height * global_flags.width; ++i) {
123                 uint8_t b = *ptr++;
124                 uint8_t g = *ptr++;
125                 uint8_t r = *ptr++;
126                 ++ptr;
127
128                 ++r_hist[r];
129                 ++g_hist[g];
130                 ++b_hist[b];
131         }
132
133         glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
134         check_error();
135         glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
136         check_error();
137         glBindFramebuffer(GL_FRAMEBUFFER, 0);
138         check_error();
139
140         printf("R hist:");
141         for (unsigned i = 0; i < 256; ++i) { printf(" %d", r_hist[i]); }
142         printf("\n");
143         printf("G hist:");
144         for (unsigned i = 0; i < 256; ++i) { printf(" %d", g_hist[i]); }
145         printf("\n");
146         printf("B hist:");
147         for (unsigned i = 0; i < 256; ++i) { printf(" %d", b_hist[i]); }
148         printf("\n");
149
150         resource_pool->release_2d_texture(fbo_tex);
151         check_error();
152         resource_pool->release_fbo(fbo);
153         check_error();
154 }
155
156 void Analyzer::signal_changed()
157 {
158         Mixer::Output channel = static_cast<Mixer::Output>(ui->input_box->currentData().value<int>());
159         ui->display->set_output(channel);
160 }