Files
qt5-legatus-legacy/Lts/Plugins/BuildingViewer/Common/BgViewer/viewerCtrl/windowcapturecallback.cpp
T
2026-08-13 18:33:10 +02:00

339 lines
11 KiB
C++
Executable File

/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commericial and non commericial applications,
* as long as this copyright notice is maintained.
*
* This application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
/* -*-c++-*-
*
* Copyright (C) 2009 Cedric Pinson
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Authors:
* Cedric Pinson <cedric.pinson@plopbyte.net>
*
*/
#include <osg/RenderInfo>
#include <osg/GL>
#include <osg/GLExtensions>
#include <sstream>
#include "windowcapturecallback.h"
#include <gst/gst.h>
#include <gst/app/gstappsrc.h>
#include <dtEntity/property.h>
#include <dtEntity/core.h>
#include <dtEntity/windowinterface.h>
namespace VCtrl
{
////////////////////////////////////////////////////////////////////////////////
void WindowCaptureCallback::operator()(osg::RenderInfo &renderInfo) const
{
glReadBuffer(_readBuffer);
osg::GraphicsContext *gc = renderInfo.getState()->getGraphicsContext();
osg::ref_ptr<ContextData> cd = getContextData(gc);
cd->read();
dtEntity::GetWindowInterface()->RequestRedraw(_contextId);
}
////////////////////////////////////////////////////////////////////////////////
WindowCaptureCallback::WindowCaptureCallback(GLenum readBuffer, unsigned int contextid, const std::string &path, int w, int h, int framerate, int quality):
_readBuffer(readBuffer),
_contextId(contextid),
_filename(path),
_w(w),
_h(h),
_framerate(framerate),
_quality(quality)
{
dtEntity::GetWindowInterface()->RequestRedraw(_contextId);
}
////////////////////////////////////////////////////////////////////////////////
WindowCaptureCallback::ContextData *WindowCaptureCallback::createContextData(osg::GraphicsContext *gc) const
{
return new ContextData(gc, _readBuffer, _filename, _w, _h, _framerate, _quality);
}
////////////////////////////////////////////////////////////////////////////////
WindowCaptureCallback::ContextData *WindowCaptureCallback::getContextData(osg::GraphicsContext *gc) const
{
osg::ref_ptr<ContextData> &data = _contextDataMap[gc];
if (!data) data = createContextData(gc);
return data.get();
}
////////////////////////////////////////////////////////////////////////////////
static gboolean bus_message(GstBus *, GstMessage *message, WindowCaptureCallback *)
{
switch (GST_MESSAGE_TYPE(message))
{
case GST_MESSAGE_WARNING:
{
GError *err = nullptr;
gchar *dbg_info = nullptr;
gst_message_parse_warning(message, &err, &dbg_info);
g_printerr("WARN from element %s: %s\n",
GST_OBJECT_NAME(message->src), err->message);
g_printerr("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
g_error_free(err);
g_free(dbg_info);
break;
}
case GST_MESSAGE_ERROR:
{
GError *err = nullptr;
gchar *dbg_info = nullptr;
gst_message_parse_error(message, &err, &dbg_info);
g_printerr("ERROR from element %s: %s\n",
GST_OBJECT_NAME(message->src), err->message);
g_printerr("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
g_error_free(err);
g_free(dbg_info);
break;
}
#ifdef LTS_DEBUG
case GST_MESSAGE_PROGRESS:
{
GstProgressType type;
gchar *code, *text;
bool in_progress = false;
gst_message_parse_progress (message, &type, &code, &text);
switch (type) {
case GST_PROGRESS_TYPE_START:
case GST_PROGRESS_TYPE_CONTINUE:
in_progress = true;
break;
case GST_PROGRESS_TYPE_COMPLETE:
case GST_PROGRESS_TYPE_CANCELED:
case GST_PROGRESS_TYPE_ERROR:
in_progress = false;
break;
default:
break;
}
g_printerr("Progress: (%s) %s, is in progress: %d\n", code, text, in_progress);
g_free (code);
g_free (text);
break;
}
case GST_MESSAGE_QOS:
{
gboolean live;
guint64 running_time, stream_time, time_stamp, duration;
gst_message_parse_qos(message, &live, &running_time, &stream_time, &time_stamp, &duration);
g_printerr("QOS message: %d, %ld, %ld, %ld, %ld\n", live, running_time, stream_time, time_stamp, duration);
break;
}
case GST_MESSAGE_EOS:
g_printerr("End-Of-Stream received!n");
break;
case GST_MESSAGE_STATE_CHANGED:
{
GstState oldState, newState, pendingState;
gst_message_parse_state_changed(message, &oldState, &newState, &pendingState);
g_printerr("State change from %d to %d (pending: %d)\n", oldState, newState, pendingState);
break;
}
default:
g_printerr("got message %s\n",
gst_message_type_get_name(GST_MESSAGE_TYPE(message)));
break;
#else
default:
g_print("got message %s\n",
gst_message_type_get_name(GST_MESSAGE_TYPE(message)));
break;
#endif
}
return GST_BUS_PASS;
}
////////////////////////////////////////////////////////////////////////////////
WindowCaptureCallback::ContextData::ContextData(osg::GraphicsContext *gc, GLenum readBuffer,
const std::string &path, int w, int h, int framerate, int quality):
_gc(gc),
_readBuffer(readBuffer),
_fileName(path),
_width(0),
_height(0)
{
(void)quality;
_startTick = _previousFrameTick = osg::Timer::instance()->tick();
_pboBuffer.push_back(0);
getSize(gc, _width, _height);
osg::notify(osg::INFO) << this << " Window size " << _width << ", " << _height << std::endl;
GstBus *bus;
/* Initialize GStreamer */
gst_init(nullptr, nullptr);
std::ostringstream gstpipeline;
gstpipeline << "appsrc name=\"inputsrc\" ";
gstpipeline << "! videorate ";
gstpipeline << "! videoscale ";
gstpipeline << "! videoconvert ";
gstpipeline << "! video/x-raw, width=" << w << ", height=" << h << ", framerate=" << framerate << "/1 ";
gstpipeline << "! videoflip method=5 ";
//#define SHOW_DIRECTLY
#ifdef SHOW_DIRECTLY
gstpipeline << "! xvimagesink ";
#else
gstpipeline << " ! vp9enc end-usage=cbr";
gstpipeline << " ! webmmux ";
gstpipeline << " ! filesink location=" << path;
#endif
// g_warning("pipeline: '%s'\n", gstpipeline.str().c_str());
/* Build the pipeline */
_pipeline = gst_parse_launch(gstpipeline.str().c_str(), nullptr);
_app_source = gst_bin_get_by_name((GstBin *) _pipeline, "inputsrc");
bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline));
g_assert(bus);
gst_bus_add_watch(bus, (GstBusFunc) bus_message, this);
gst_object_unref(bus);
GstCaps *video_caps = gst_caps_new_simple("video/x-raw",
"format", G_TYPE_STRING, "RGBA",
"width", G_TYPE_INT, _width,
"height", G_TYPE_INT, _height,
"framerate", GST_TYPE_FRACTION, framerate, 1,
nullptr);
gst_app_src_set_caps(GST_APP_SRC(_app_source), video_caps);
g_object_set(GST_APP_SRC(_app_source), "format", GST_FORMAT_TIME, nullptr);
gst_caps_unref(video_caps);
// make gstreamer more verbose:
g_signal_connect( _pipeline, "deep-notify", G_CALLBACK(gst_object_default_deep_notify ), nullptr );
/* Start playing */
gst_element_set_state(_pipeline, GST_STATE_PLAYING);
}
////////////////////////////////////////////////////////////////////////////////
void WindowCaptureCallback::ContextData::getSize(osg::GraphicsContext *gc, int &width, int &height)
{
if (gc->getTraits())
{
width = gc->getTraits()->width;
height = gc->getTraits()->height;
}
}
////////////////////////////////////////////////////////////////////////////////
void WindowCaptureCallback::ContextData::read()
{
osg::GLExtensions *ext = osg::GLExtensions::Get(_gc->getState()->getContextID(), true);
int width = 0, height = 0;
getSize(_gc, width, height);
if (width != _width || _height != height)
{
osg::notify(osg::INFO) << this << " Window resized " << width << ", " << height << std::endl;
_width = width;
_height = height;
}
unsigned int size = _width * _height * 4;
GLuint &pbo = _pboBuffer[0];
if (pbo == 0)
{
ext->glGenBuffers(1, &pbo);
ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo);
ext->glBufferData(GL_PIXEL_PACK_BUFFER_ARB, size, nullptr, GL_STREAM_READ);
osg::notify(osg::INFO) << this << " Generating pbo " << pbo << std::endl;
}
else
ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo);
osg::Timer_t tick_now = osg::Timer::instance()->tick();
glReadPixels(0, 0, _width, _height, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
GLubyte *src = (GLubyte *)ext->glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB,
GL_READ_ONLY_ARB);
if (src)
{
guint64 timestamp = (tick_now - _startTick) * 1000;
guint64 duration = (tick_now - _previousFrameTick) * 1000;
GstBuffer *buffer = gst_buffer_new_allocate(nullptr, size, nullptr);
GstMapInfo info;
gst_buffer_map(buffer, &info, GST_MAP_WRITE);
memcpy(info.data, src, size);
gst_buffer_unmap(buffer, &info);
GST_BUFFER_PTS(buffer) = timestamp;
GST_BUFFER_DTS(buffer) = timestamp;
GST_BUFFER_DURATION(buffer) = duration;
_previousFrameTick = tick_now;
GstFlowReturn ret;
g_signal_emit_by_name(_app_source, "push-buffer", buffer, &ret);
gst_buffer_unref(buffer);
if (ret != GST_FLOW_OK)
{
// some error, stop sending data
GST_DEBUG("some error");
}
ext->glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB);
}
ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0);
}
////////////////////////////////////////////////////////////////////////////////
WindowCaptureCallback::ContextData::~ContextData()
{
gboolean ret = gst_element_send_event(_pipeline, gst_event_new_eos());
if (ret)
{
GstMessageType types = GST_MESSAGE_EOS;
GstMessage *msg = gst_bus_timed_pop_filtered(GST_ELEMENT_BUS(_pipeline), GST_CLOCK_TIME_NONE, types);
gst_message_unref(msg);
}
gst_element_set_state(_pipeline, GST_STATE_NULL);
gst_object_unref(_pipeline);
}
}