~phablet-team/aethercast/fix-for-microsoft-dongle

« back to all changes in this revision

Viewing changes to src/mcs/mir/streamrenderer.cpp

Add hardware encoding and video streaming support.

The hardware encoding is currently only for Android 5.x based devices. On all others encoding will simply not work. The streaming part of aethercast (MPEGTS packetizing, RTP sending) as based on some code from Android.

Approved by PS Jenkins bot, Thomas Voß, Jim Hodapp.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2015 Canonical, Ltd.
 
3
 *
 
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.
 
7
 *
 
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.
 
12
 *
 
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/>.
 
15
 *
 
16
 */
 
17
 
 
18
#define GL_GLEXT_PROTOTYPES
 
19
#define EGL_EGLEXT_PROTOTYPES
 
20
#include <EGL/egl.h>
 
21
#include <EGL/eglext.h>
 
22
#include <GLES2/gl2.h>
 
23
#include <GLES2/gl2ext.h>
 
24
 
 
25
#include <system/window.h>
 
26
 
 
27
#include <chrono>
 
28
#include <thread>
 
29
 
 
30
#include <boost/concept_check.hpp>
 
31
 
 
32
#include "mcs/logger.h"
 
33
 
 
34
#include "mcs/video/statistics.h"
 
35
 
 
36
#include "mcs/mir/screencast.h"
 
37
#include "mcs/mir/streamrenderer.h"
 
38
 
 
39
namespace {
 
40
static constexpr const char *kStreamRendererThreadName{"StreamRenderer"};
 
41
static constexpr unsigned int kNumBufferSlots{2};
 
42
}
 
43
 
 
44
namespace mcs {
 
45
namespace mir {
 
46
 
 
47
StreamRenderer::Ptr StreamRenderer::Create(const Screencast::Ptr &connector, const video::BaseEncoder::Ptr &encoder) {
 
48
    return std::shared_ptr<StreamRenderer>(new StreamRenderer(connector, encoder));
 
49
}
 
50
 
 
51
StreamRenderer::StreamRenderer(const Screencast::Ptr &connector, const video::BaseEncoder::Ptr &encoder) :
 
52
    connector_(connector),
 
53
    encoder_(encoder),
 
54
    running_(false),
 
55
    width_(0),
 
56
    height_(0),
 
57
    input_buffers_(mcs::video::BufferQueue::Create(kNumBufferSlots)) {
 
58
}
 
59
 
 
60
StreamRenderer::~StreamRenderer() {
 
61
    Stop();
 
62
}
 
63
 
 
64
void StreamRenderer::RenderThread() {
 
65
    mcs::Utils::SetThreadName(kStreamRendererThreadName);
 
66
 
 
67
    MCS_DEBUG("Everything successfully setup; Starting recording now %dx%d@%d",
 
68
              width_, height_, encoder_->Configuration().framerate);
 
69
 
 
70
    bool waiting = false;
 
71
    mcs::TimestampUs wait_slot_time;
 
72
 
 
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;
 
76
 
 
77
    while (running_) {
 
78
        if (!waiting)
 
79
            wait_slot_time = mcs::Utils::GetNowUs();
 
80
 
 
81
        mcs::TimestampUs iteration_start_time = mcs::Utils::GetNowUs();
 
82
 
 
83
        if (!input_buffers_->WaitForSlots()) {
 
84
            waiting = true;
 
85
            continue;
 
86
        }
 
87
 
 
88
        waiting = false;
 
89
 
 
90
        int64_t wait_time = (mcs::Utils::GetNowUs() - wait_slot_time) / 1000ll;
 
91
        video::Statistics::Instance()->RecordRendererWait(wait_time);
 
92
 
 
93
        mcs::TimestampUs before_swap = mcs::Utils::GetNowUs();
 
94
 
 
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();
 
98
 
 
99
        int64_t swap_time = (mcs::Utils::GetNowUs() - before_swap) / 1000ll;
 
100
        video::Statistics::Instance()->RecordRendererSwapped(swap_time);
 
101
 
 
102
        auto native_buffer = connector_->CurrentBuffer();
 
103
 
 
104
        auto buffer = mcs::video::Buffer::Create(native_buffer);
 
105
        buffer->SetDelegate(shared_from_this());
 
106
 
 
107
        frame_count++;
 
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);
 
111
            frame_count = 0;
 
112
            start_time_us = time_now_us;
 
113
        }
 
114
 
 
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());
 
119
 
 
120
        input_buffers_->Push(buffer);
 
121
 
 
122
        encoder_->QueueBuffer(buffer);
 
123
 
 
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);
 
128
 
 
129
        mcs::TimestampUs iteration_time = mcs::Utils::GetNowUs() - iteration_start_time;
 
130
        int64_t sleep_time = target_iteration_time - iteration_time;
 
131
        if (sleep_time > 0)
 
132
            std::this_thread::sleep_for(std::chrono::microseconds(sleep_time));
 
133
    }
 
134
}
 
135
 
 
136
void StreamRenderer::OnBufferFinished(const video::Buffer::Ptr &buffer) {
 
137
    boost::ignore_unused_variable_warning(buffer);
 
138
 
 
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();
 
144
}
 
145
 
 
146
void StreamRenderer::SetDimensions(unsigned int width, unsigned int height) {
 
147
    width_ = width;
 
148
    height_ = height;
 
149
}
 
150
 
 
151
void StreamRenderer::StartThreaded() {
 
152
    if (running_)
 
153
        return;
 
154
 
 
155
    auto output_mode = connector_->OutputMode();
 
156
 
 
157
    if (width_ == 0 || height_ == 0) {
 
158
        width_ = output_mode.width;
 
159
        height_ = output_mode.height;
 
160
    }
 
161
 
 
162
    running_ = true;
 
163
 
 
164
    render_thread_ = std::thread(&StreamRenderer::RenderThread, this);
 
165
}
 
166
 
 
167
void StreamRenderer::Stop() {
 
168
    if (!running_)
 
169
        return;
 
170
 
 
171
    running_ = false;
 
172
    render_thread_.join();
 
173
}
 
174
 
 
175
} // namespace mir
 
176
} // namespace mcs