Skip to content

Commit

Permalink
Implement a recorder interface, using a custom protocol
Browse files Browse the repository at this point in the history
A separate client using the lipstick_recorder protocol can request
frames, which lipstick will provide when redrawing.
  • Loading branch information
giucam committed Dec 16, 2014
1 parent abe5bc4 commit 5f2cb0f
Show file tree
Hide file tree
Showing 7 changed files with 362 additions and 0 deletions.
92 changes: 92 additions & 0 deletions protocol/lipstick-recorder.xml
@@ -0,0 +1,92 @@
<protocol name="lipstick_recorder">
<copyright>
Copyright (C) 2014 Jolla Ltd.

Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.

THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
</copyright>

<interface name="lipstick_recorder_manager" version="1">
<request name="create_recorder">
<description summary="create a recorder object">
Create a recorder object for the specified output.
</description>
<arg name="recorder" type="new_id" interface="lipstick_recorder"/>
<arg name="output" type="object" interface="wl_output"/>
</request>
</interface>

<interface name="lipstick_recorder" version="1">
<request name="destroy" type="destructor">
<description summary="destroy the recorder object">
Destroy the recorder object, discarding any frame request
that may be pending.
</description>
</request>
<request name="record_frame">
<description summary="request a frame to be recorded">
Ask the compositor to record its next frame, putting
the content into the specified buffer data. The frame
event will be sent when the frame is recorded.
Only one frame will be recorded, the client will have
to call this again after the frame event if it wants to
record more frames.

The buffer must be a shm buffer, trying to use another
type of buffer will result in frame being sent with the
bad_buffer result.
</description>
<arg name="buffer" type="object" interface="wl_buffer"/>
</request>

<enum name="result">
<entry name="ok" value="1"/>
<entry name="bad_buffer" value="2"/>
</enum>

<event name="frame">
<description summary="notify a frame was recorded, or an error">
The compositor will send this event after a frame was
recorded, or in case an error happened. The client can
call record_frame again to record the next frame.

The value of the 'result' argument will be one of the
values of the 'result' enum.
'time' is the time the compositor recorded that frame,
in milliseconds, with an unspecified base.
</description>
<arg name="result" type="int"/>
<arg name="buffer" type="object" interface="wl_buffer"/>
<arg name="time" type="uint"/>
</event>

<event name="cancelled">
<description summary="notify a request was cancelled">
The compositor will send this event if the client calls
request_frame more than one time for the same compositor
frame. The cancel event will be sent carrying the old
buffer, and the frame will be recorded using the newest
buffer.
</description>
<arg name="buffer" type="object" interface="wl_buffer"/>
</event>
</interface>
</protocol>
4 changes: 4 additions & 0 deletions src/compositor/compositor.pri
Expand Up @@ -13,6 +13,7 @@ PUBLICHEADERS += \
HEADERS += \
$$PWD/windowpixmapitem.h \
$$PWD/windowproperty.h \
$$PWD/lipstickrecorder.h \

SOURCES += \
$$PWD/lipstickcompositor.cpp \
Expand All @@ -23,7 +24,10 @@ SOURCES += \
$$PWD/windowpixmapitem.cpp \
$$PWD/windowproperty.cpp \
$$PWD/lipsticksurfaceinterface.cpp \
$$PWD/lipstickrecorder.cpp \

DEFINES += QT_COMPOSITOR_QUICK

QT += compositor

WAYLANDSERVERSOURCES += ../protocol/lipstick-recorder.xml \
9 changes: 9 additions & 0 deletions src/compositor/lipstickcompositor.cpp
Expand Up @@ -30,6 +30,7 @@
#include "lipstickcompositor.h"
#include "lipstickcompositoradaptor.h"
#include "lipsticksettings.h"
#include "lipstickrecorder.h"
#include <qpa/qwindowsysteminterface.h>
#include "alienmanager/alienmanager.h"

Expand Down Expand Up @@ -61,6 +62,7 @@ LipstickCompositor::LipstickCompositor()
QObject::connect(this, SIGNAL(afterRendering()), this, SLOT(windowSwapped()));
connect(m_displayState, SIGNAL(displayStateChanged(MeeGo::QmDisplayState::DisplayState)), this, SLOT(reactOnDisplayStateChanges(MeeGo::QmDisplayState::DisplayState)));
QObject::connect(HomeApplication::instance(), SIGNAL(aboutToDestroy()), this, SLOT(homeApplicationAboutToDestroy()));
connect(this, &QQuickWindow::afterRendering, this, &LipstickCompositor::readContent, Qt::DirectConnection);

m_orientationSensor = new QOrientationSensor(this);
QObject::connect(m_orientationSensor, SIGNAL(readingChanged()), this, SLOT(setScreenOrientationFromSensor()));
Expand Down Expand Up @@ -88,6 +90,8 @@ LipstickCompositor::LipstickCompositor()
qWarning("Unable to register object at path /: %s", systemBus.lastError().message().toUtf8().constData());
}

m_recorder = new LipstickRecorderManager;
addGlobalInterface(m_recorder);
addGlobalInterface(new AlienManagerGlobal);
}

Expand Down Expand Up @@ -640,3 +644,8 @@ void LipstickCompositor::setUpdatesEnabled(bool enabled)
}
}
}

void LipstickCompositor::readContent()
{
m_recorder->recordFrame(this);
}
3 changes: 3 additions & 0 deletions src/compositor/lipstickcompositor.h
Expand Up @@ -29,6 +29,7 @@ class WindowModel;
class LipstickCompositorWindow;
class LipstickCompositorProcWindow;
class QOrientationSensor;
class LipstickRecorderManager;

class LIPSTICK_EXPORT LipstickCompositor : public QQuickWindow, public QWaylandQuickCompositor,
public QQmlParserStatus
Expand Down Expand Up @@ -166,6 +167,7 @@ private slots:
void windowAdded(int);
void windowRemoved(int);
void windowDestroyed(LipstickCompositorWindow *item);
void readContent();

QQmlComponent *shaderEffectComponent();

Expand Down Expand Up @@ -193,6 +195,7 @@ private slots:
MeeGo::QmDisplayState::DisplayState m_previousDisplayState;
bool m_updatesEnabled;
int m_onUpdatesDisabledUnfocusedWindowId;
LipstickRecorderManager *m_recorder;
};

#endif // LIPSTICKCOMPOSITOR_H
168 changes: 168 additions & 0 deletions src/compositor/lipstickrecorder.cpp
@@ -0,0 +1,168 @@
/***************************************************************************
**
** Copyright (C) 2014 Jolla Ltd.
** Contact: Giulio Camuffo <giulio.camuffo@jollamobile.com>
**
** This file is part of lipstick.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include <sys/time.h>

#include <QMutexLocker>

#include "lipstickrecorder.h"
#include "lipstickcompositor.h"

static uint32_t getTime()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

static const QEvent::Type FrameEventType = (QEvent::Type)QEvent::registerEventType();

class FrameEvent : public QEvent
{
public:
FrameEvent(int r, uint32_t t)
: QEvent(FrameEventType)
, result(r)
, time(t)
{
}

int result;
uint32_t time;
};

LipstickRecorderManager::LipstickRecorderManager()
: QWaylandGlobalInterface()
{
}

const wl_interface* LipstickRecorderManager::interface() const
{
return &lipstick_recorder_manager_interface;
}

void LipstickRecorderManager::recordFrame(QWindow *window)
{
QMutexLocker lock(&m_mutex);
if (m_requests.isEmpty())
return;

uchar *pixels;
uint32_t time = getTime();
foreach (LipstickRecorder *recorder, m_requests.values(window)) {
wl_shm_buffer *buffer = recorder->buffer();
pixels = static_cast<uchar *>(wl_shm_buffer_get_data(buffer));
int width = wl_shm_buffer_get_width(buffer);
int height = wl_shm_buffer_get_height(buffer);

if (width != window->width() || height != window->height()) {
qApp->postEvent(recorder, new FrameEvent(QtWaylandServer::lipstick_recorder::result_bad_buffer, time));
continue;
}

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
qApp->postEvent(recorder, new FrameEvent(QtWaylandServer::lipstick_recorder::result_ok, time));

m_requests.remove(window, recorder);
}
}

void LipstickRecorderManager::requestFrame(QWindow *window, LipstickRecorder *recorder)
{
QMutexLocker lock(&m_mutex);
m_requests.insert(window, recorder);
}

void LipstickRecorderManager::remove(QWindow *window, LipstickRecorder *recorder)
{
QMutexLocker lock(&m_mutex);
m_requests.remove(window, recorder);
}

void LipstickRecorderManager::bind(wl_client *client, quint32 version, quint32 id)
{
Q_UNUSED(version)

add(client, id, version);
}

void LipstickRecorderManager::lipstick_recorder_manager_create_recorder(Resource *resource, uint32_t id, ::wl_resource *output)
{
// TODO: we should find out the window associated with this output, but there isn't
// a way to do that in qtcompositor yet. Just ignore it for now and use the one window we have.
Q_UNUSED(output)

new LipstickRecorder(this, resource->client(), id, LipstickCompositor::instance());
}


LipstickRecorder::LipstickRecorder(LipstickRecorderManager *manager, wl_client *client, quint32 id, QWindow *window)
: QtWaylandServer::lipstick_recorder(client, id, 1)
, m_manager(manager)
, m_bufferResource(Q_NULLPTR)
, m_client(client)
, m_window(window)
{
}

LipstickRecorder::~LipstickRecorder()
{
m_manager->remove(m_window, this);
}

void LipstickRecorder::sendFrame(int result, int time)
{
send_frame(result, m_bufferResource, time);
m_bufferResource = Q_NULLPTR;
}

void LipstickRecorder::lipstick_recorder_destroy_resource(Resource *resource)
{
Q_UNUSED(resource)
delete this;
}

void LipstickRecorder::lipstick_recorder_destroy(Resource *resource)
{
wl_resource_destroy(resource->handle);
}

void LipstickRecorder::lipstick_recorder_record_frame(Resource *resource, ::wl_resource *buffer)
{
Q_UNUSED(resource)
if (m_bufferResource) {
send_cancelled(buffer);
}
m_bufferResource = buffer;
m_buffer = wl_shm_buffer_get(buffer);
if (m_buffer) {
m_manager->requestFrame(m_window, this);
} else {
m_bufferResource = Q_NULLPTR;
send_frame(result_bad_buffer, buffer, getTime());
}
}

bool LipstickRecorder::event(QEvent *e)
{
if (e->type() == FrameEventType) {
FrameEvent *fe = static_cast<FrameEvent *>(e);
sendFrame(fe->result, fe->time);
wl_client_flush(client());
return true;
}
return QObject::event(e);
}

0 comments on commit 5f2cb0f

Please sign in to comment.