2
* Copyright (C) 2015 Canonical, Ltd.
4
* This program is free software: you can redistribute it and/or modify it
5
* under the terms of the GNU General Public License version 3, as published
6
* by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranties of
10
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11
* PURPOSE. See the GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License along
14
* with this program. If not, see <http://www.gnu.org/licenses/>.
18
#define GL_GLEXT_PROTOTYPES
19
#define EGL_EGLEXT_PROTOTYPES
21
#include <EGL/eglext.h>
22
#include <GLES2/gl2.h>
23
#include <GLES2/gl2ext.h>
25
#include <system/window.h>
30
#include <boost/concept_check.hpp>
32
#include "mcs/logger.h"
34
#include "mcs/video/statistics.h"
36
#include "mcs/mir/screencast.h"
37
#include "mcs/mir/streamrenderer.h"
40
static constexpr const char *kStreamRendererThreadName{"StreamRenderer"};
41
static constexpr unsigned int kNumBufferSlots{2};
47
StreamRenderer::Ptr StreamRenderer::Create(const Screencast::Ptr &connector, const video::BaseEncoder::Ptr &encoder) {
48
return std::shared_ptr<StreamRenderer>(new StreamRenderer(connector, encoder));
51
StreamRenderer::StreamRenderer(const Screencast::Ptr &connector, const video::BaseEncoder::Ptr &encoder) :
52
connector_(connector),
57
input_buffers_(mcs::video::BufferQueue::Create(kNumBufferSlots)) {
60
StreamRenderer::~StreamRenderer() {
64
void StreamRenderer::RenderThread() {
65
mcs::Utils::SetThreadName(kStreamRendererThreadName);
67
MCS_DEBUG("Everything successfully setup; Starting recording now %dx%d@%d",
68
width_, height_, encoder_->Configuration().framerate);
71
mcs::TimestampUs wait_slot_time;
73
static int64_t start_time_us = mcs::Utils::GetNowUs();
74
static unsigned int frame_count = 0;
75
static const mcs::TimestampUs target_iteration_time = (1. / encoder_->Configuration().framerate) * 1000000ll;
79
wait_slot_time = mcs::Utils::GetNowUs();
81
mcs::TimestampUs iteration_start_time = mcs::Utils::GetNowUs();
83
if (!input_buffers_->WaitForSlots()) {
90
int64_t wait_time = (mcs::Utils::GetNowUs() - wait_slot_time) / 1000ll;
91
video::Statistics::Instance()->RecordRendererWait(wait_time);
93
mcs::TimestampUs before_swap = mcs::Utils::GetNowUs();
95
// This will trigger the rendering/compositing process inside mir
96
// and will block until that is done and we received a new buffer
97
connector_->SwapBuffersSync();
99
int64_t swap_time = (mcs::Utils::GetNowUs() - before_swap) / 1000ll;
100
video::Statistics::Instance()->RecordRendererSwapped(swap_time);
102
auto native_buffer = connector_->CurrentBuffer();
104
auto buffer = mcs::video::Buffer::Create(native_buffer);
105
buffer->SetDelegate(shared_from_this());
108
const int64_t time_now_us = mcs::Utils::GetNowUs();
109
if (start_time_us + 1000000ll <= time_now_us) {
110
video::Statistics::Instance()->RecordRendererFramesPerSecond(frame_count);
112
start_time_us = time_now_us;
115
// FIXME: at optimum we would get the timestamp directly supplied
116
// from mir but as long as that isn't available we don't have any
117
// other chance and need to do it here.
118
buffer->SetTimestamp(mcs::Utils::GetNowUs());
120
input_buffers_->Push(buffer);
122
encoder_->QueueBuffer(buffer);
124
static mcs::TimestampUs last_queued_time = mcs::Utils::GetNowUs();
125
int64_t renderer_iteration_time = (mcs::Utils::GetNowUs() - last_queued_time) / 1000ll;
126
last_queued_time = mcs::Utils::GetNowUs();
127
video::Statistics::Instance()->RecordRendererIteration(renderer_iteration_time);
129
mcs::TimestampUs iteration_time = mcs::Utils::GetNowUs() - iteration_start_time;
130
int64_t sleep_time = target_iteration_time - iteration_time;
132
std::this_thread::sleep_for(std::chrono::microseconds(sleep_time));
136
void StreamRenderer::OnBufferFinished(const video::Buffer::Ptr &buffer) {
137
boost::ignore_unused_variable_warning(buffer);
139
// We're currently relying on the buffers to come back in order so
140
// we can safely remove the head from the queue here which then
141
// gives us a free slot at the beginning which will be filled by
142
// the renderer again.
143
input_buffers_->Pop();
146
void StreamRenderer::SetDimensions(unsigned int width, unsigned int height) {
151
void StreamRenderer::StartThreaded() {
155
auto output_mode = connector_->OutputMode();
157
if (width_ == 0 || height_ == 0) {
158
width_ = output_mode.width;
159
height_ = output_mode.height;
164
render_thread_ = std::thread(&StreamRenderer::RenderThread, this);
167
void StreamRenderer::Stop() {
172
render_thread_.join();