Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
+338
@@ -0,0 +1,338 @@
|
||||
/* -*-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);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user