-
-
Notifications
You must be signed in to change notification settings - Fork 418
Closed
Labels
questionThis issue is a general question about projectM and the surrounding ecosystem.This issue is a general question about projectM and the surrounding ecosystem.triageThis is a new issue which hasn't been reviewed yet by a staff member.This is a new issue which hasn't been reviewed yet by a staff member.
Description
Please confirm the following points:
- This question is NOT about the Android apps in the Play Store
- I have searched the project page to check if the question was already asked elsewhere
Topic
Development and Contributing
Your Request
Hello projectM team! I'm trying to understand what's wrong with my offscreen rendering pipeline. I launch it with the following command:
ffmpeg -i "/media/data/music/test.mp3" -f f32le -acodec pcm_f32le -ar 48000 - \
| ./projectm-render-pipe \
| ffmpeg -i "/media/data/music/test.mp3" -f ppm_pipe -r 60 -i - -map 0:a:0 -map 1:v:0 -y rendered.mp4As you can see there's a certain "glow" around the edges. Somehow when I render it offscreen it is not present. I'm kind of a rookie in graphics pipelines but I'm trying to implement offscreen rendering for ProjectM right now and before I submit a PR i need to get it right.
Here's my code that I use for offscreen rendering
#include "projectM-4/core.h"
#include <array>
#include <iostream>
#include <sstream>
#include <EGL/egl.h>
#include <GL/gl.h>
#include <projectM-4/projectM.h>
#include <vector>
static constexpr int kFixedFps = 60;
static constexpr int kAudioSamplerate = 48000;
static constexpr int kPbufferWidth = 1280;
static constexpr int kPbufferHeight = 720;
static constexpr int kBytesPerPixel = 3;
static const std::array configAttribs = {
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 8,
EGL_NONE
};
static const std::array contextAttribs = {
EGL_CONTEXT_MAJOR_VERSION, 3,
EGL_CONTEXT_MINOR_VERSION, 3,
EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
EGL_NONE,
};
static const std::array pbufferAttribs = {
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGB,
EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
EGL_WIDTH, kPbufferWidth,
EGL_HEIGHT, kPbufferHeight,
EGL_NONE,
};
static auto screenshotToPpm(size_t width, size_t height, unsigned char *pixels) -> std::string {
std::stringstream output;
output << "P6 " << width << ' ' << height << ' ' << 255 << '\n';
output.write((const char *) pixels, kBytesPerPixel * height * width);
return output.str();
}
static void assertEglError(const std::string& msg) {
EGLint error = eglGetError();
if (error != EGL_SUCCESS) {
std::stringstream exc;
exc << "EGL error 0x" << std::hex << error << " at " << msg;
throw std::runtime_error(exc.str());
}
}
auto main(int argc, char *argv[]) -> int {
// 1. Initialize EGL
EGLDisplay eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
EGLint major = 0;
EGLint minor = 0;
eglInitialize(eglDpy, &major, &minor);
assertEglError("initializing EGL");
// 2. Select an appropriate configuration
EGLint numConfigs = 0;
EGLConfig eglCfg = nullptr;
eglChooseConfig(eglDpy, configAttribs.data(), &eglCfg, 1, &numConfigs);
assertEglError("choosing EGL config");
// 3. Create a surface
EGLSurface eglSurf = eglCreatePbufferSurface(eglDpy, eglCfg, pbufferAttribs.data());
assertEglError("creating EGL surface");
// 4. Bind the API
eglBindAPI(EGL_OPENGL_API);
assertEglError("binding OpenGL API");
// 5. Create a context and make it current
EGLContext eglCtx = eglCreateContext(eglDpy, eglCfg, EGL_NO_CONTEXT, contextAttribs.data());
assertEglError("creating EGL context");
eglMakeCurrent(eglDpy, eglSurf, eglSurf, eglCtx);
assertEglError("making EGL context current");
// from now on use your OpenGL context
auto *projectm = projectm_create();
projectm_load_preset_file(projectm, "file:///usr/share/projectM/presets/cream-of-the-crop/Waveform/Wire Circular/$$$ Royal - Mashup (191).milk", false);
auto textures = std::vector<const char *> {"/usr/share/projectM/textures/cream-of-the-crop/"};
projectm_set_texture_search_paths(projectm, textures.data(), 1);
projectm_set_mesh_size(projectm, 64, 48);
projectm_set_window_size(projectm, kPbufferWidth, kPbufferHeight);
projectm_set_soft_cut_duration(projectm, 3);
projectm_set_preset_duration(projectm, 30);
projectm_set_easter_egg(projectm, 0.0);
projectm_set_hard_cut_enabled(projectm, false);
projectm_set_hard_cut_duration(projectm, 60);
projectm_set_hard_cut_sensitivity(projectm, 1.0);
projectm_set_beat_sensitivity(projectm, 1.0);
projectm_set_aspect_correction(projectm, true);
projectm_set_fps(projectm, kFixedFps);
projectm_set_fixed_fps(projectm, kFixedFps);
// buffer size for 60 fps should fit into 16ms
// for 16000 bitrate that's 266.66 samples for each channel
constexpr float samplesPerFrame = 2.0 * kAudioSamplerate / kFixedFps; // 533.33
constexpr auto bufferLen = static_cast<size_t>(samplesPerFrame) + 1; // 534
std::array<float, bufferLen> pcmBuffer{};
constexpr int size = kBytesPerPixel * kPbufferHeight * kPbufferWidth;
std::array<unsigned char, size> rgbBuffer{};
size_t samplesRead = 0;
for (size_t i = 0; ; i++) {
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// special arithmetic for cases where samplerate is not fully divisible by fps
// without this we eventually get un-synced video and audio
auto nextBatch = static_cast<size_t>(samplesPerFrame * i) - samplesRead; // could be 533, could be 534
std::cin.read(reinterpret_cast<char *>(pcmBuffer.data()), nextBatch * sizeof(decltype(pcmBuffer)::value_type));
if (std::cin.eof()) {
break;
}
samplesRead += nextBatch;
// std::cerr << "Iteration " << i << " filled buffer size " << samplesToRead << " with data" << '\n';
auto samplesToSubmit = std::min((unsigned int) bufferLen, projectm_pcm_get_max_samples());
projectm_pcm_add_float(projectm, pcmBuffer.data(), samplesToSubmit, PROJECTM_STEREO);
projectm_opengl_render_frame(projectm);
glReadPixels(0, 0, kPbufferWidth, kPbufferHeight, GL_RGB, GL_UNSIGNED_BYTE, rgbBuffer.data());
auto ppm = screenshotToPpm(kPbufferWidth, kPbufferHeight, rgbBuffer.data());
std::cout << ppm;
}
// 6. Terminate EGL when finished
eglDestroySurface(eglDpy, eglSurf);
eglDestroyContext(eglDpy, eglCtx);
eglTerminate(eglDpy);
return 0;
}I can also upload the full video or my small changes to timekeeping if you need it. I haven't touched any of GL-related code.
AriESQ and tom-leamon
Metadata
Metadata
Assignees
Labels
questionThis issue is a general question about projectM and the surrounding ecosystem.This issue is a general question about projectM and the surrounding ecosystem.triageThis is a new issue which hasn't been reviewed yet by a staff member.This is a new issue which hasn't been reviewed yet by a staff member.

