]> git.sesse.net Git - nageru/commitdiff
Start working on a frame analyzer.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 8 May 2017 17:55:30 +0000 (19:55 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 8 May 2017 21:58:23 +0000 (23:58 +0200)
The idea is that this will be able to show a histogram and a
color picker. So far, we have some logic to grab a frame,
and that's it.

Makefile
analyzer.cpp [new file with mode: 0644]
analyzer.h [new file with mode: 0644]
mainwindow.cpp
mainwindow.h
ui_analyzer.ui [new file with mode: 0644]
ui_mainwindow.ui

index 1a041fefab6d297e11f2fcbdd572e807424e01c4..34551329aff7b366635decdae5678f5a876c0a81 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@ endif
 LDLIBS=$(shell pkg-config --libs $(PKG_MODULES)) -pthread -lva -lva-drm -lva-x11 -lX11 -lavformat -lavcodec -lavutil -lswscale -lavresample -lzita-resampler -lasound -ldl
 
 # Qt objects
-OBJS_WITH_MOC = glwidget.o mainwindow.o vumeter.o lrameter.o compression_reduction_meter.o correlation_meter.o aboutdialog.o input_mapping_dialog.o midi_mapping_dialog.o nonlinear_fader.o
+OBJS_WITH_MOC = glwidget.o mainwindow.o vumeter.o lrameter.o compression_reduction_meter.o correlation_meter.o aboutdialog.o analyzer.o input_mapping_dialog.o midi_mapping_dialog.o nonlinear_fader.o
 OBJS += $(OBJS_WITH_MOC)
 OBJS += $(OBJS_WITH_MOC:.o=.moc.o) ellipsis_label.moc.o clickable_label.moc.o
 OBJS += context_menus.o vu_common.o piecewise_interpolator.o main.o
@@ -63,6 +63,7 @@ benchmark_audio_mixer: $(BM_OBJS)
 
 # Extra dependencies that need to be generated.
 aboutdialog.o: ui_aboutdialog.h
+analyzer.o: ui_analyzer.h
 alsa_pool.o: state.pb.h
 audio_mixer.o: state.pb.h
 input_mapping.o: state.pb.h
@@ -76,7 +77,7 @@ DEPS=$(OBJS:.o=.d) $(BM_OBJS:.o=.d)
 -include $(DEPS)
 
 clean:
-       $(RM) $(OBJS) $(BM_OBJS) $(DEPS) nageru benchmark_audio_mixer ui_aboutdialog.h ui_mainwindow.h ui_display.h ui_about.h ui_audio_miniview.h ui_audio_expanded_view.h ui_input_mapping.h ui_midi_mapping.h chain-*.frag *.dot *.pb.cc *.pb.h $(OBJS_WITH_MOC:.o=.moc.cpp) ellipsis_label.moc.cpp clickable_label.moc.cpp
+       $(RM) $(OBJS) $(BM_OBJS) $(DEPS) nageru benchmark_audio_mixer ui_aboutdialog.h ui_analyzer.h ui_mainwindow.h ui_display.h ui_about.h ui_audio_miniview.h ui_audio_expanded_view.h ui_input_mapping.h ui_midi_mapping.h chain-*.frag *.dot *.pb.cc *.pb.h $(OBJS_WITH_MOC:.o=.moc.cpp) ellipsis_label.moc.cpp clickable_label.moc.cpp
 
 PREFIX=/usr/local
 install:
diff --git a/analyzer.cpp b/analyzer.cpp
new file mode 100644 (file)
index 0000000..353229e
--- /dev/null
@@ -0,0 +1,145 @@
+#include "analyzer.h"
+
+#include <QDialogButtonBox>
+#include <QSurface>
+
+#include <movit/resource_pool.h>
+#include <movit/util.h>
+
+#include "context.h"
+#include "flags.h"
+#include "mixer.h"
+#include "ui_analyzer.h"
+
+using namespace std;
+
+Analyzer::Analyzer()
+       : ui(new Ui::Analyzer)
+{
+       ui->setupUi(this);
+
+       //connect(ui->button_box, &QDialogButtonBox::accepted, [this]{ this->close(); });
+
+       ui->input_box->addItem("Live", Mixer::OUTPUT_LIVE);
+       ui->input_box->addItem("Preview", Mixer::OUTPUT_PREVIEW);
+       unsigned num_channels = global_mixer->get_num_channels();
+       for (unsigned channel_idx = 0; channel_idx < num_channels; ++channel_idx) {
+               Mixer::Output channel = static_cast<Mixer::Output>(Mixer::OUTPUT_INPUT0 + channel_idx); 
+               string name = global_mixer->get_channel_name(channel);
+               ui->input_box->addItem(QString::fromStdString(name), channel);
+       }
+
+       connect(ui->grab_btn, &QPushButton::clicked, bind(&Analyzer::grab_clicked, this));
+       //ui->display->set_output(Mixer::OUTPUT_LIVE);
+       surface = create_surface(QSurfaceFormat::defaultFormat());
+       context = create_context(surface);
+
+       if (!make_current(context, surface)) {
+               printf("oops\n");
+               exit(1);
+       }
+
+        glGenBuffers(1, &pbo);
+        glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo);
+        glBufferData(GL_PIXEL_PACK_BUFFER_ARB, global_flags.width * global_flags.height * 4, NULL, GL_STREAM_READ);
+}
+
+Analyzer::~Analyzer()
+{
+       if (!make_current(context, surface)) {
+               printf("oops\n");
+               exit(1);
+       }
+       glDeleteBuffers(1, &pbo);
+       check_error();
+       if (resource_pool != nullptr) {
+               resource_pool->clean_context();
+       }
+       delete_context(context);
+       delete surface;  // TODO?
+}
+
+void Analyzer::grab_clicked()
+{
+       Mixer::Output channel = static_cast<Mixer::Output>(ui->input_box->currentData().value<int>());
+
+       if (!make_current(context, surface)) {
+               printf("oops\n");
+               exit(1);
+       }
+
+       Mixer::DisplayFrame frame;
+       if (!global_mixer->get_display_frame(channel, &frame)) {
+               printf("Not ready yet\n");
+               return;
+       }
+
+       // Set up an FBO to render into.
+       if (resource_pool == nullptr) {
+               resource_pool = frame.chain->get_resource_pool();
+       } else {
+               assert(resource_pool == frame.chain->get_resource_pool());
+       }
+       GLuint fbo_tex = resource_pool->create_2d_texture(GL_RGBA8, global_flags.width, global_flags.height);
+       check_error();
+       GLuint fbo = resource_pool->create_fbo(fbo_tex);
+       check_error();
+
+       glWaitSync(frame.ready_fence.get(), /*flags=*/0, GL_TIMEOUT_IGNORED);
+       check_error();
+       frame.setup_chain();
+       check_error();
+       glDisable(GL_FRAMEBUFFER_SRGB);
+       check_error();
+       frame.chain->render_to_fbo(fbo, global_flags.width, global_flags.height);
+       check_error();
+
+       // Read back to memory.
+       glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+       check_error();
+       glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
+       check_error();
+       glReadPixels(0, 0, global_flags.width, global_flags.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, BUFFER_OFFSET(0));
+       check_error();
+
+       unsigned char *buf = (unsigned char *)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
+       check_error();
+
+       int r_hist[256] = {0}, g_hist[256] = {0}, b_hist[256] = {0};
+       const unsigned char *ptr = buf;
+       for (int y = 0; y < global_flags.width; ++y) {
+               for (int x = 0; x < global_flags.height; ++x) {
+                       uint8_t b = *ptr++;
+                       uint8_t g = *ptr++;
+                       uint8_t r = *ptr++;
+                       uint8_t a = *ptr++;
+
+                       ++r_hist[r];
+                       ++g_hist[g];
+                       ++b_hist[b];
+               }
+       }
+
+       glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
+       check_error();
+       glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
+       check_error();
+       glBindFramebuffer(GL_FRAMEBUFFER, 0);
+       check_error();
+
+       printf("R hist:");
+       for (unsigned i = 0; i < 256; ++i) { printf(" %d", r_hist[i]); }
+       printf("\n");
+       printf("G hist:");
+       for (unsigned i = 0; i < 256; ++i) { printf(" %d", g_hist[i]); }
+       printf("\n");
+       printf("B hist:");
+       for (unsigned i = 0; i < 256; ++i) { printf(" %d", b_hist[i]); }
+       printf("\n");
+
+       resource_pool->release_2d_texture(fbo_tex);
+       check_error();
+       resource_pool->release_fbo(fbo);
+       check_error();
+}
+
diff --git a/analyzer.h b/analyzer.h
new file mode 100644 (file)
index 0000000..d239c0e
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _ANALYZER_H
+#define _ANALYZER_H 1
+
+#include <QDialog>
+#include <QString>
+
+#include <epoxy/gl.h>
+
+class QObject;
+class QOpenGLContext;
+class QSurface;
+
+namespace Ui {
+class Analyzer;
+}  // namespace Ui
+
+namespace movit {
+class ResourcePool;
+}  // namespace movit
+
+class Analyzer : public QDialog
+{
+       Q_OBJECT
+
+public:
+       Analyzer();
+       ~Analyzer();
+
+private:
+       void grab_clicked();
+
+       Ui::Analyzer *ui;
+       QSurface *surface;
+       QOpenGLContext *context;
+       GLuint pbo;
+       movit::ResourcePool *resource_pool = nullptr;
+};
+
+#endif  // !defined(_ANALYZER_H)
index 136890d93826d4b71c276eb896755eb648d9b27c..bff32ab8333c571ff6bfaa2bee6ecb19a3f298e4 100644 (file)
@@ -46,6 +46,7 @@
 
 #include "aboutdialog.h"
 #include "alsa_pool.h"
+#include "analyzer.h"
 #include "clickable_label.h"
 #include "context_menus.h"
 #include "correlation_meter.h"
@@ -200,6 +201,7 @@ MainWindow::MainWindow()
        connect(ui->exit_action, &QAction::triggered, this, &MainWindow::exit_triggered);
        connect(ui->manual_action, &QAction::triggered, this, &MainWindow::manual_triggered);
        connect(ui->about_action, &QAction::triggered, this, &MainWindow::about_triggered);
+       connect(ui->open_analyzer_action, &QAction::triggered, this, &MainWindow::open_analyzer_triggered);
        connect(ui->simple_audio_mode, &QAction::triggered, this, &MainWindow::simple_audio_mode_triggered);
        connect(ui->multichannel_audio_mode, &QAction::triggered, this, &MainWindow::multichannel_audio_mode_triggered);
        connect(ui->input_mapping_action, &QAction::triggered, this, &MainWindow::input_mapping_triggered);
@@ -590,6 +592,12 @@ void MainWindow::about_triggered()
        AboutDialog().exec();
 }
 
+void MainWindow::open_analyzer_triggered()
+{
+       analyzer.reset(new Analyzer);
+       analyzer->show();
+}
+
 void MainWindow::simple_audio_mode_triggered()
 {
        if (global_audio_mixer->get_mapping_mode() == AudioMixer::MappingMode::SIMPLE) {
index b5e43fd2e74f039a6e47b372e1ae149df93c064e..302e30967d4bdb031e3c311eab2445c8a034ccdf 100644 (file)
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "analyzer.h"
 #include "audio_mixer.h"
 #include "midi_mapper.h"
 #include "mixer.h"
@@ -47,6 +48,7 @@ public slots:
        void exit_triggered();
        void manual_triggered();
        void about_triggered();
+       void open_analyzer_triggered();
        void simple_audio_mode_triggered();
        void multichannel_audio_mode_triggered();
        void input_mapping_triggered();
@@ -159,6 +161,7 @@ private:
        std::vector<Ui::AudioExpandedView *> audio_expanded_views;
        int current_wb_pick_display = -1;
        MIDIMapper midi_mapper;
+       std::unique_ptr<Analyzer> analyzer;
 };
 
 extern MainWindow *global_mainwindow;
diff --git a/ui_analyzer.ui b/ui_analyzer.ui
new file mode 100644 (file)
index 0000000..254014f
--- /dev/null
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Analyzer</class>
+ <widget class="QDialog" name="Analyzer">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>843</width>
+    <height>450</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <widget class="QComboBox" name="input_box">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>200</y>
+     <width>251</width>
+     <height>23</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="grab_btn">
+   <property name="geometry">
+    <rect>
+     <x>270</x>
+     <y>200</y>
+     <width>61</width>
+     <height>23</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Grab</string>
+   </property>
+  </widget>
+  <widget class="QWidget" name="widget_3" native="true">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>240</y>
+     <width>320</width>
+     <height>180</height>
+    </rect>
+   </property>
+   <property name="autoFillBackground">
+    <bool>false</bool>
+   </property>
+   <property name="styleSheet">
+    <string notr="true">background: rgb(173, 127, 168)</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label">
+   <property name="geometry">
+    <rect>
+     <x>370</x>
+     <y>310</y>
+     <width>421</width>
+     <height>21</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Selected coordinate (x,y): (123,456)</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_3">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>420</y>
+     <width>321</width>
+     <height>21</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>RGB histogram</string>
+   </property>
+   <property name="alignment">
+    <set>Qt::AlignCenter</set>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_4">
+   <property name="geometry">
+    <rect>
+     <x>350</x>
+     <y>280</y>
+     <width>481</width>
+     <height>21</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Grabbed frame (1280x720)</string>
+   </property>
+   <property name="alignment">
+    <set>Qt::AlignCenter</set>
+   </property>
+  </widget>
+  <widget class="QWidget" name="gridLayoutWidget">
+   <property name="geometry">
+    <rect>
+     <x>600</x>
+     <y>350</y>
+     <width>191</width>
+     <height>80</height>
+    </rect>
+   </property>
+   <layout class="QGridLayout" name="gridLayout">
+    <item row="1" column="0">
+     <widget class="QLabel" name="label_6">
+      <property name="text">
+       <string>Green:</string>
+      </property>
+     </widget>
+    </item>
+    <item row="1" column="1">
+     <widget class="QLabel" name="label_9">
+      <property name="text">
+       <string>127</string>
+      </property>
+     </widget>
+    </item>
+    <item row="2" column="1">
+     <widget class="QLabel" name="label_10">
+      <property name="text">
+       <string>127</string>
+      </property>
+     </widget>
+    </item>
+    <item row="3" column="0">
+     <widget class="QLabel" name="label_2">
+      <property name="text">
+       <string>Hex:</string>
+      </property>
+     </widget>
+    </item>
+    <item row="0" column="1">
+     <widget class="QLabel" name="label_8">
+      <property name="text">
+       <string>127</string>
+      </property>
+     </widget>
+    </item>
+    <item row="2" column="0">
+     <widget class="QLabel" name="label_7">
+      <property name="text">
+       <string>Blue:</string>
+      </property>
+     </widget>
+    </item>
+    <item row="3" column="1">
+     <widget class="QLabel" name="label_11">
+      <property name="text">
+       <string>#7f7f7f</string>
+      </property>
+     </widget>
+    </item>
+    <item row="0" column="0">
+     <widget class="QLabel" name="label_5">
+      <property name="text">
+       <string>Red:</string>
+      </property>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QLabel" name="label_12">
+   <property name="geometry">
+    <rect>
+     <x>600</x>
+     <y>330</y>
+     <width>191</width>
+     <height>16</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Color (8-bit sRGB):</string>
+   </property>
+   <property name="alignment">
+    <set>Qt::AlignCenter</set>
+   </property>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
index 0118eaeb659f43edbbda4003f04c1c2c7dd3191b..19858aa1de2f1e8e68ba899926e3928dd75d016c 100644 (file)
                  <x>0</x>
                  <y>0</y>
                  <width>505</width>
-                 <height>236</height>
+                 <height>238</height>
                 </rect>
                </property>
                <layout class="QHBoxLayout" name="horizontalLayout_4">
                 <rect>
                  <x>0</x>
                  <y>0</y>
-                 <width>100</width>
-                 <height>30</height>
+                 <width>46</width>
+                 <height>20</height>
                 </rect>
                </property>
                <layout class="QHBoxLayout" name="horizontalLayout_5">
      <x>0</x>
      <y>0</y>
      <width>1089</width>
-     <height>23</height>
+     <height>20</height>
     </rect>
    </property>
    <widget class="QMenu" name="video_menu">
      <addaction name="separator"/>
     </widget>
     <addaction name="cut_action"/>
+    <addaction name="open_analyzer_action"/>
     <addaction name="x264_bitrate_action"/>
     <addaction name="hdmi_sdi_output_device_menu"/>
     <addaction name="hdmi_sdi_output_resolution_menu"/>
     <string>On standard &amp;output</string>
    </property>
   </action>
+  <action name="open_analyzer_action">
+   <property name="text">
+    <string>Open frame &amp;analyzer…</string>
+   </property>
+  </action>
  </widget>
  <layoutdefault spacing="6" margin="11"/>
  <customwidgets>