Merge lp:~alan-griffiths/mir/screenshot-to-buffer into lp:mir
- screenshot-to-buffer
- Merge into development-branch
Status: | Merged |
---|---|
Approved by: | Alberto Aguirre |
Approved revision: | no longer in the source branch. |
Merged at revision: | 4189 |
Proposed branch: | lp:~alan-griffiths/mir/screenshot-to-buffer |
Merge into: | lp:mir |
Diff against target: |
877 lines (+487/-28) 16 files modified
examples/CMakeLists.txt (+4/-0) examples/screencast.cpp (+149/-0) include/client/mir_toolkit/mir_screencast.h (+39/-0) include/test/mir/test/doubles/stub_session_authorizer.h (+6/-0) src/client/mir_screencast.cpp (+59/-7) src/client/mir_screencast.h (+25/-1) src/client/mir_screencast_api.cpp (+37/-0) src/client/symbols.map (+2/-0) src/server/compositor/compositing_screencast.cpp (+42/-14) src/server/compositor/compositing_screencast.h (+1/-2) src/server/compositor/screencast_display_buffer.cpp (+30/-1) src/server/compositor/screencast_display_buffer.h (+6/-0) src/server/frontend/protobuf_message_processor.cpp (+1/-1) src/server/frontend/session_mediator.cpp (+1/-1) tests/acceptance-tests/test_client_screencast.cpp (+85/-0) tests/unit-tests/compositor/test_compositing_screencast.cpp (+0/-1) |
To merge this branch: | bzr merge lp:~alan-griffiths/mir/screenshot-to-buffer |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mir CI Bot | continuous-integration | Approve | |
Alberto Aguirre (community) | Approve | ||
Daniel van Vugt | Pending | ||
Mir development team | Pending | ||
Review via email: mp+324943@code.launchpad.net |
This proposal supersedes a proposal from 2017-02-21.
Commit message
client: add mir_screencast_
Description of the change
client: add mir_screencast_
The tricky part in screencasting to a buffer is dealing with the differences that might arise because of platforms, pixel formats, etc. The current screencast stream will generate 'hardware buffers' and then send them to the client. On android, these buffers can be accessed directly. On mesa, the hardware buffers might be tiled, and the buffers are ran through a GL context to use glReadPixels.
The new system lets the user submit a buffer to the server to fill.
The function will return one of 3 MirErrors in the screencast domain, failed (something went wrong), unsupported (not supported), and performance_warning (operation completed, but may not have been optimal)
Also, the buffers come out in the correct mirror_mode (the current system flipped them)
The demo works with android 'software' buffers, and fails with mesa ShmBuffers currently. Allocating a mesa gbm buffer would work, but the user would have to do the same sort of thing that mirscreencast does with glReadPixels to make sense of the contents.
The eventual plan is to give more plumbing so that more format conversions and buffer combos will work. So, overtime, more things go from being unsupported to either working, or giving a 'performance_
Mir CI Bot (mir-ci-bot) wrote : Posted in a previous version of this proposal | # |
Alexandros Frantzis (afrantzis) wrote : Posted in a previous version of this proposal | # |
+++ include/
+ void mir_screencast_
+ MirError const* mir_screencast_
Shoudn't these be part of the screencast API (mir_screencast.h) rather than the buffer API?
+ if (request-
+ error = std::make_
+ else
+ error = nullptr;
+
+ request-
+ reinterpret_
We apparently support multiple concurrent screencast captures on the same object, but we share the error instance among them, leading to memory errors like:
auto const error1 = mir_screencast_
auto const error2 = mir_screencast_
// error1 now points to freed memory!
+ class CaptureSync
We can use std::promise<
Mir CI Bot (mir-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:3968
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Alexandros Frantzis (afrantzis) wrote : Posted in a previous version of this proposal | # |
> add warning about the limited lifetime of MirError
I don't think a warning is enough. The shared error subtly limits the interface to only one screencast in flight at any time. Another example with async requests:
void callback(buffer, error, context)
{
// It's not safe to access error here!
}
mir_screencast_
mir_screencast_
We either need to fix the shared error problem so that concurrent requests can work safely, or, alternatively, explicitly disallow concurrent requests (e.g. abort() if detected).
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal | # |
I think the long term future of screencasting lays in fixing bug 1660269. Any buffer shared with a screencasting client should be linear and not require any GL code on the client side.
So that's just a reminder. I won't stand in the way of progress...
Alberto Aguirre (albaguirre) wrote : Posted in a previous version of this proposal | # |
MirError const* mir_screencast_
I think returning a MirError here can lead to the same lifetime issues we had with wait handles.
We need an explicit "mir_error_release" at the very least.
Alexandros Frantzis (afrantzis) : Posted in a previous version of this proposal | # |
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4184
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4184
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4187
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4188
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4188
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4189
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Alan Griffiths (alan-griffiths) wrote : | # |
Four cross process tests failing:
14:34:04 11: [ FAILED ] ServerDisconnec
14:34:04 11: [ FAILED ] ServerDisconnec
14:34:04 11: [ FAILED ] ServerStartup.
14:34:04 11: [ FAILED ] ServerStartup.
Typically,
"14:29:22 11: ==3081== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)"
Which looks like a valgrind error "inherited" from an earlier test (presumably Screencast.*) but where is that detailed?
Alan Griffiths (alan-griffiths) wrote : | # |
Oh...
From NestedServerWit
14:28:02 11: ==2818== Thread 6 Mir/Input Reade:
14:28:02 11: ==2818== Invalid read of size 8
Looks unrelated to this MP.
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4189
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Alberto Aguirre (albaguirre) wrote : | # |
Comments inline,
Mainly removing the enum values that are not used as well as the error domain.
And some nitpicks in the example code.
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4191
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) : | # |
Preview Diff
1 | === modified file 'examples/CMakeLists.txt' |
2 | --- examples/CMakeLists.txt 2017-06-05 11:06:24 +0000 |
3 | +++ examples/CMakeLists.txt 2017-06-06 16:26:39 +0000 |
4 | @@ -312,3 +312,7 @@ |
5 | target_link_libraries(mir_demo_client_chain_jumping_buffers |
6 | mirclient |
7 | ) |
8 | + |
9 | +mir_add_wrapped_executable(mir_demo_client_screencast screencast.cpp) |
10 | + |
11 | +target_link_libraries(mir_demo_client_screencast mirclient) |
12 | |
13 | === added file 'examples/screencast.cpp' |
14 | --- examples/screencast.cpp 1970-01-01 00:00:00 +0000 |
15 | +++ examples/screencast.cpp 2017-06-06 16:26:39 +0000 |
16 | @@ -0,0 +1,149 @@ |
17 | +/* |
18 | + * Copyright © 2017 Canonical Ltd. |
19 | + * |
20 | + * This program is free software: you can redistribute it and/or modify |
21 | + * it under the terms of the GNU General Public License version 3 as |
22 | + * published by the Free Software Foundation. |
23 | + * |
24 | + * This program is distributed in the hope that it will be useful, |
25 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
26 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
27 | + * GNU General Public License for more details. |
28 | + * |
29 | + * You should have received a copy of the GNU General Public License |
30 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
31 | + * |
32 | + * Authored by: Kevin DuBois <kevin.dubois@canonical.com> |
33 | + */ |
34 | + |
35 | +#include "mir_toolkit/mir_client_library.h" |
36 | +#include "mir_toolkit/mir_screencast.h" |
37 | +#include "mir_toolkit/mir_buffer.h" |
38 | +#include <memory> |
39 | +#include <iostream> |
40 | +#include <unistd.h> |
41 | +#include <getopt.h> |
42 | +#include <fstream> |
43 | + |
44 | +int main(int argc, char *argv[]) |
45 | +try |
46 | +{ |
47 | + int arg = -1; |
48 | + static char const* socket_file = NULL; |
49 | + static char const* output_file = "screencap_output.raw"; |
50 | + unsigned int width = 400; |
51 | + unsigned int height = 300; |
52 | + |
53 | + auto disp_id = 0; |
54 | + while ((arg = getopt (argc, argv, "m:f:d:s:h:")) != -1) |
55 | + { |
56 | + switch (arg) |
57 | + { |
58 | + case 'm': |
59 | + socket_file = optarg; |
60 | + break; |
61 | + case 'f': |
62 | + output_file = optarg; |
63 | + break; |
64 | + case 'd': |
65 | + disp_id = atoi(optarg); |
66 | + break; |
67 | + case 's': |
68 | + if (sscanf(optarg, "%ux%u", &width, &height) != 2 || width == 0 || height == 0) |
69 | + { |
70 | + std::cerr << "could not parse size " << optarg << '\n'; |
71 | + return -1; |
72 | + } |
73 | + break; |
74 | + case 'h': |
75 | + default: |
76 | + std::cout << argv[0] << std::endl; |
77 | + std::cout << "Usage:\n"; |
78 | + std::cout << " -m <Mir server socket>\n"; |
79 | + std::cout << " -f file to output to\n"; |
80 | + std::cout << " -d output id to capture\n"; |
81 | + std::cout << " -s size of capture buffer (WxH)\n"; |
82 | + std::cout << " -h help dialog\n"; |
83 | + return -1; |
84 | + } |
85 | + } |
86 | + |
87 | + int rc = 0; |
88 | + auto connection = mir_connect_sync(socket_file, "screencap_to_buffer"); |
89 | + if (!mir_connection_is_valid(connection)) |
90 | + { |
91 | + std::cerr << "could not connect to server\n"; |
92 | + return -1; |
93 | + } |
94 | + |
95 | + auto display_config = mir_connection_create_display_configuration(connection); |
96 | + if (disp_id < 0 || disp_id > mir_display_config_get_num_outputs(display_config)) |
97 | + { |
98 | + std::cerr << "invalid display id set\n"; |
99 | + return -1; |
100 | + } |
101 | + |
102 | + auto output = mir_display_config_get_output(display_config, disp_id); |
103 | + auto mode = mir_output_get_current_mode(output); |
104 | + |
105 | + auto pf = mir_pixel_format_abgr_8888; |
106 | + MirRectangle rect { |
107 | + mir_output_get_position_x(output), |
108 | + mir_output_get_position_y(output), |
109 | + static_cast<unsigned int>(mir_output_mode_get_width(mode)), |
110 | + static_cast<unsigned int>(mir_output_mode_get_height(mode)) }; |
111 | + mir_display_config_release(display_config); |
112 | + |
113 | + auto spec = mir_create_screencast_spec(connection); |
114 | + mir_screencast_spec_set_capture_region(spec, &rect); |
115 | + mir_screencast_spec_set_mirror_mode(spec, mir_mirror_mode_horizontal); |
116 | + //TODO: the default screencast spec will capture a buffer when creating the screencast. |
117 | + // Set to zero to avoid this, and when the old screencast-bufferstream method is removed, |
118 | + // the initial capture will be removed. |
119 | + mir_screencast_spec_set_number_of_buffers(spec, 0); |
120 | + |
121 | + auto screencast = mir_screencast_create_sync(spec); |
122 | + mir_screencast_spec_release(spec); |
123 | + |
124 | + auto buffer = mir_connection_allocate_buffer_sync(connection, width, height, pf); |
125 | + |
126 | + if (auto status = mir_screencast_capture_to_buffer_sync(screencast, buffer)) |
127 | + { |
128 | + switch (status) |
129 | + { |
130 | + case mir_screencast_error_failure: |
131 | + std::cerr << "screencast failed" << std::endl; |
132 | + rc = -1; |
133 | + break; |
134 | + default: |
135 | + break; |
136 | + } |
137 | + } |
138 | + |
139 | + std::ofstream file(output_file); |
140 | + if (!rc && output) |
141 | + { |
142 | + MirBufferLayout layout; |
143 | + MirGraphicsRegion region; |
144 | + mir_buffer_map(buffer, ®ion, &layout); |
145 | + |
146 | + auto addr = region.vaddr; |
147 | + for (int i = 0; i < region.height; i++) |
148 | + { |
149 | + file.write(addr, region.width * MIR_BYTES_PER_PIXEL(pf)); |
150 | + addr += region.stride; |
151 | + } |
152 | + |
153 | + mir_buffer_unmap(buffer); |
154 | + } |
155 | + |
156 | + mir_buffer_release(buffer); |
157 | + mir_screencast_release_sync(screencast); |
158 | + mir_connection_release(connection); |
159 | + return EXIT_SUCCESS; |
160 | +} |
161 | +catch(std::exception& e) |
162 | +{ |
163 | + std::cerr << "error : " << e.what() << std::endl; |
164 | + return EXIT_FAILURE; |
165 | +} |
166 | |
167 | === modified file 'include/client/mir_toolkit/mir_screencast.h' |
168 | --- include/client/mir_toolkit/mir_screencast.h 2017-05-25 08:58:03 +0000 |
169 | +++ include/client/mir_toolkit/mir_screencast.h 2017-06-06 16:26:39 +0000 |
170 | @@ -28,6 +28,22 @@ |
171 | extern "C" { |
172 | #endif |
173 | |
174 | +typedef enum MirScreencastResult |
175 | +{ |
176 | + /** |
177 | + * Screencasting to the MirBuffer succeeded. |
178 | + */ |
179 | + mir_screencast_success, |
180 | + |
181 | + /** |
182 | + * Screencasting failed. |
183 | + */ |
184 | + mir_screencast_error_failure, |
185 | +} MirScreencastResult; |
186 | + |
187 | +typedef void (*MirScreencastBufferCallback)( |
188 | + MirScreencastResult status, MirBuffer* buffer, void* context); |
189 | + |
190 | /** |
191 | * Create a screencast specification. |
192 | * |
193 | @@ -161,6 +177,29 @@ |
194 | */ |
195 | MirBufferStream* mir_screencast_get_buffer_stream(MirScreencast* screencast); |
196 | |
197 | +/** Capture the contents of the screen to a particular buffer. |
198 | + * |
199 | + * \param [in] screencast The screencast |
200 | + * \param [in] buffer The buffer |
201 | + * \param [in] available_callback Callback triggered when buffer is available again |
202 | + * \param [in] available_context The context for the above callback |
203 | + **/ |
204 | +void mir_screencast_capture_to_buffer( |
205 | + MirScreencast* screencast, |
206 | + MirBuffer* buffer, |
207 | + MirScreencastBufferCallback available_callback, void* available_context); |
208 | + |
209 | +/** Capture the contents of the screen to a particular buffer and wait for the |
210 | + * capture to complete. |
211 | + * |
212 | + * \warning The returned MirError will be valid until the next call |
213 | + * to mir_screencast_capture_to_buffer or mir_screencast_capture_to_buffer_sync. |
214 | + * \param [in] screencast The screencast |
215 | + * \param [in] buffer The buffer |
216 | + * \return The error condition |
217 | + **/ |
218 | +MirScreencastResult mir_screencast_capture_to_buffer_sync(MirScreencast* screencast, MirBuffer* buffer); |
219 | + |
220 | #ifdef __cplusplus |
221 | } |
222 | /**@}*/ |
223 | |
224 | === renamed file 'tests/include/mir/test/doubles/mock_gl.h' => 'include/test/mir/test/doubles/mock_gl.h' |
225 | === modified file 'include/test/mir/test/doubles/stub_session_authorizer.h' |
226 | --- include/test/mir/test/doubles/stub_session_authorizer.h 2017-05-08 03:04:26 +0000 |
227 | +++ include/test/mir/test/doubles/stub_session_authorizer.h 2017-06-06 16:26:39 +0000 |
228 | @@ -20,6 +20,7 @@ |
229 | #define MIR_TEST_DOUBLES_STUB_SESSION_AUTHORIZER_H_ |
230 | |
231 | #include "mir/frontend/session_authorizer.h" |
232 | +#include <gmock/gmock.h> |
233 | |
234 | namespace mir |
235 | { |
236 | @@ -60,6 +61,11 @@ |
237 | } |
238 | }; |
239 | |
240 | +struct MockSessionAuthorizer : StubSessionAuthorizer |
241 | +{ |
242 | + MOCK_METHOD1(screencast_is_allowed, bool(frontend::SessionCredentials const&)); |
243 | +}; |
244 | + |
245 | } |
246 | } |
247 | } // namespace mir |
248 | |
249 | === modified file 'src/client/mir_screencast.cpp' |
250 | --- src/client/mir_screencast.cpp 2017-05-08 03:04:26 +0000 |
251 | +++ src/client/mir_screencast.cpp 2017-06-06 16:26:39 +0000 |
252 | @@ -21,10 +21,12 @@ |
253 | #include "mir_protobuf.pb.h" |
254 | #include "make_protobuf_object.h" |
255 | #include "mir/mir_buffer_stream.h" |
256 | +#include "mir/mir_buffer.h" |
257 | #include "mir/frontend/client_constants.h" |
258 | #include "mir_toolkit/mir_native_buffer.h" |
259 | |
260 | #include <boost/throw_exception.hpp> |
261 | +#include <algorithm> |
262 | |
263 | namespace mcl = mir::client; |
264 | namespace mp = mir::protobuf; |
265 | @@ -55,6 +57,16 @@ |
266 | message.mutable_region()->set_height(region.height); |
267 | } |
268 | |
269 | + if (spec.num_buffers.is_set() && spec.num_buffers.value() == 0) |
270 | + { |
271 | + if (!spec.pixel_format.is_set()) |
272 | + message.set_pixel_format(mir_pixel_format_abgr_8888); |
273 | + if (!spec.width.is_set()) |
274 | + message.set_width(1); |
275 | + if (!spec.width.is_set()) |
276 | + message.set_height(1); |
277 | + } |
278 | + |
279 | return message; |
280 | } |
281 | |
282 | @@ -72,9 +84,6 @@ |
283 | |
284 | #define THROW_IF_ZERO(option) THROW_IF_EQ(option, 0) |
285 | |
286 | - THROW_IF_ZERO(width); |
287 | - THROW_IF_ZERO(height); |
288 | - THROW_IF_EQ(pixel_format, mir_pixel_format_invalid); |
289 | THROW_IF_UNSET(capture_region); |
290 | |
291 | if (spec.capture_region.is_set()) |
292 | @@ -85,6 +94,14 @@ |
293 | if (region.height == 0) |
294 | BOOST_THROW_EXCEPTION(std::runtime_error("Invalid capture region height")); |
295 | } |
296 | + |
297 | + //zero nbuffers don't need to set pixel format, width, or height |
298 | + if (!spec.num_buffers.is_set() || spec.num_buffers.value() > 0) |
299 | + { |
300 | + THROW_IF_ZERO(width); |
301 | + THROW_IF_ZERO(height); |
302 | + THROW_IF_EQ(pixel_format, mir_pixel_format_invalid); |
303 | + } |
304 | } |
305 | } |
306 | |
307 | @@ -180,13 +197,13 @@ |
308 | void MirScreencast::screencast_created( |
309 | MirScreencastCallback callback, void* context) |
310 | { |
311 | - if (!protobuf_screencast->has_error() && connection) |
312 | + if (connection && !protobuf_screencast->has_error() && |
313 | + protobuf_screencast->has_buffer_stream() && protobuf_screencast->buffer_stream().has_buffer()) |
314 | { |
315 | std::lock_guard<decltype(mutex)> lock(mutex); |
316 | try |
317 | { |
318 | - buffer_stream = connection->make_consumer_stream( |
319 | - protobuf_screencast->buffer_stream()); |
320 | + buffer_stream = connection->make_consumer_stream(protobuf_screencast->buffer_stream()); |
321 | } |
322 | catch (...) |
323 | { |
324 | @@ -207,7 +224,7 @@ |
325 | |
326 | { |
327 | std::lock_guard<decltype(mutex)> lock(mutex); |
328 | - if (connection) |
329 | + if (buffer_stream) |
330 | connection->release_consumer_stream(buffer_stream.get()); |
331 | buffer_stream.reset(); |
332 | } |
333 | @@ -219,3 +236,38 @@ |
334 | std::lock_guard<decltype(mutex)> lock(mutex); |
335 | return buffer_stream.get(); |
336 | } |
337 | + |
338 | +void MirScreencast::screencast_done(ScreencastRequest* request) |
339 | +{ |
340 | + auto const status = request->response.has_error() ? mir_screencast_error_failure : mir_screencast_success; |
341 | + |
342 | + request->available_callback(status, reinterpret_cast<MirBuffer*>(request->buffer), request->available_context); |
343 | + |
344 | + std::unique_lock<decltype(mutex)> lk(mutex); |
345 | + auto it = std::find_if(requests.begin(), requests.end(), |
346 | + [&request] (auto const& it) { return it.get() == request; } ); |
347 | + if (it != requests.end()) |
348 | + requests.erase(it); |
349 | +} |
350 | + |
351 | +void MirScreencast::screencast_to_buffer( |
352 | + mcl::MirBuffer* buffer, |
353 | + MirScreencastBufferCallback cb, |
354 | + void* context) |
355 | +{ |
356 | + if (!server) //construction with nullptr should be invalid |
357 | + BOOST_THROW_EXCEPTION(std::runtime_error("invalid screencast")); |
358 | + |
359 | + std::unique_lock<decltype(mutex)> lk(mutex); |
360 | + |
361 | + requests.emplace_back(std::make_unique<ScreencastRequest>(buffer, cb, context)); |
362 | + |
363 | + mir::protobuf::ScreencastRequest request; |
364 | + request.set_buffer_id(buffer->rpc_id()); |
365 | + request.mutable_id()->set_value(protobuf_screencast->screencast_id().value()); |
366 | + |
367 | + server->screencast_to_buffer( |
368 | + &request, |
369 | + &(requests.back()->response), |
370 | + google::protobuf::NewCallback(this, &MirScreencast::screencast_done, requests.back().get())); |
371 | +} |
372 | |
373 | === modified file 'src/client/mir_screencast.h' |
374 | --- src/client/mir_screencast.h 2017-05-08 03:04:26 +0000 |
375 | +++ src/client/mir_screencast.h 2017-06-06 16:26:39 +0000 |
376 | @@ -20,10 +20,11 @@ |
377 | #define MIR_CLIENT_MIR_SCREENCAST_H_ |
378 | |
379 | #include "mir_wait_handle.h" |
380 | -#include "mir_toolkit/client_types.h" |
381 | +#include "mir_toolkit/mir_screencast.h" |
382 | #include "mir/optional_value.h" |
383 | #include "mir/geometry/size.h" |
384 | #include "mir/geometry/rectangle.h" |
385 | +#include "mir_protobuf.pb.h" |
386 | |
387 | #include <EGL/eglplatform.h> |
388 | |
389 | @@ -38,6 +39,7 @@ |
390 | } |
391 | namespace client |
392 | { |
393 | +class MirBuffer; |
394 | namespace rpc |
395 | { |
396 | class DisplayServer; |
397 | @@ -85,6 +87,11 @@ |
398 | |
399 | MirBufferStream* get_buffer_stream(); |
400 | |
401 | + void screencast_to_buffer( |
402 | + mir::client::MirBuffer* buffer, |
403 | + MirScreencastBufferCallback available_callback, |
404 | + void* available_context); |
405 | + |
406 | private: |
407 | void screencast_created( |
408 | MirScreencastCallback callback, void* context); |
409 | @@ -103,6 +110,23 @@ |
410 | MirWaitHandle release_wait_handle; |
411 | |
412 | std::string const empty_error_message; |
413 | + |
414 | + struct ScreencastRequest |
415 | + { |
416 | + ScreencastRequest(mir::client::MirBuffer* b, MirScreencastBufferCallback cb, void* context) : |
417 | + buffer(b), |
418 | + available_callback(cb), |
419 | + available_context(context) |
420 | + { |
421 | + } |
422 | + |
423 | + mir::client::MirBuffer* buffer; |
424 | + MirScreencastBufferCallback available_callback; |
425 | + void* available_context; |
426 | + mir::protobuf::Void response; |
427 | + }; |
428 | + std::vector<std::unique_ptr<ScreencastRequest>> requests; |
429 | + void screencast_done(ScreencastRequest* request); |
430 | }; |
431 | |
432 | #endif /* MIR_CLIENT_MIR_SCREENCAST_H_ */ |
433 | |
434 | === modified file 'src/client/mir_screencast_api.cpp' |
435 | --- src/client/mir_screencast_api.cpp 2017-05-08 03:04:26 +0000 |
436 | +++ src/client/mir_screencast_api.cpp 2017-06-06 16:26:39 +0000 |
437 | @@ -19,11 +19,13 @@ |
438 | #define MIR_LOG_COMPONENT "MirScreencastAPI" |
439 | |
440 | #include "mir_toolkit/mir_screencast.h" |
441 | +#include "mir_toolkit/mir_buffer.h" |
442 | #include "mir_screencast.h" |
443 | #include "mir_connection.h" |
444 | #include "mir/raii.h" |
445 | #include "mir/require.h" |
446 | #include "mir/uncaught.h" |
447 | +#include "no_tls_future-inl.h" |
448 | |
449 | #include <stdexcept> |
450 | #include <boost/throw_exception.hpp> |
451 | @@ -131,3 +133,38 @@ |
452 | MIR_LOG_UNCAUGHT_EXCEPTION(ex); |
453 | return nullptr; |
454 | } |
455 | + |
456 | +void mir_screencast_capture_to_buffer( |
457 | + MirScreencast* screencast, |
458 | + MirBuffer* b, |
459 | + MirScreencastBufferCallback available_callback, void* available_context) |
460 | +try |
461 | +{ |
462 | + mir::require(b); |
463 | + mir::require(screencast); |
464 | + |
465 | + auto buffer = reinterpret_cast<mir::client::MirBuffer*>(b); |
466 | + screencast->screencast_to_buffer(buffer, available_callback, available_context); |
467 | +} |
468 | +catch (std::exception const& ex) |
469 | +{ |
470 | + MIR_LOG_UNCAUGHT_EXCEPTION(ex); |
471 | + available_callback(mir_screencast_error_failure, nullptr, available_context); |
472 | +} |
473 | + |
474 | +MirScreencastResult mir_screencast_capture_to_buffer_sync(MirScreencast* screencast, MirBuffer* buffer) |
475 | +try |
476 | +{ |
477 | + mir::client::NoTLSPromise<MirScreencastResult> promise; |
478 | + mir_screencast_capture_to_buffer(screencast, buffer, |
479 | + [](MirScreencastResult status, MirBuffer* /*buffer*/, void* context) |
480 | + { |
481 | + reinterpret_cast<mir::client::NoTLSPromise<MirScreencastResult>*>(context)->set_value(status); |
482 | + }, &promise); |
483 | + return promise.get_future().get(); |
484 | +} |
485 | +catch (std::exception const& ex) |
486 | +{ |
487 | + MIR_LOG_UNCAUGHT_EXCEPTION(ex); |
488 | + return mir_screencast_error_failure; |
489 | +} |
490 | |
491 | === modified file 'src/client/symbols.map' |
492 | --- src/client/symbols.map 2017-05-25 08:58:03 +0000 |
493 | +++ src/client/symbols.map 2017-06-06 16:26:39 +0000 |
494 | @@ -584,6 +584,8 @@ |
495 | mir_buffer_stream_get_microseconds_till_vblank; |
496 | mir_connection_apply_session_input_config; |
497 | mir_connection_set_base_input_config; |
498 | + mir_screencast_capture_to_buffer; |
499 | + mir_screencast_capture_to_buffer_sync; |
500 | mir_create_freestyle_window_spec; |
501 | mir_create_gloss_window_spec; |
502 | mir_create_satellite_window_spec; |
503 | |
504 | === modified file 'src/server/compositor/compositing_screencast.cpp' |
505 | --- src/server/compositor/compositing_screencast.cpp 2017-05-08 03:04:26 +0000 |
506 | +++ src/server/compositor/compositing_screencast.cpp 2017-06-06 16:26:39 +0000 |
507 | @@ -26,6 +26,7 @@ |
508 | #include "mir/graphics/display_buffer.h" |
509 | #include "mir/graphics/graphic_buffer_allocator.h" |
510 | #include "mir/compositor/display_buffer_compositor_factory.h" |
511 | +#include "mir/graphics/transformation.h" |
512 | #include "mir/compositor/display_buffer_compositor.h" |
513 | #include "mir/geometry/rectangles.h" |
514 | #include "mir/raii.h" |
515 | @@ -71,21 +72,20 @@ |
516 | ScreencastSessionContext( |
517 | std::shared_ptr<Scene> const& scene, |
518 | mg::Display& display, |
519 | - mg::GraphicBufferAllocator& buffer_allocator, |
520 | DisplayBufferCompositorFactory& db_compositor_factory, |
521 | + std::vector<std::shared_ptr<mg::Buffer>> const& buffers, |
522 | geom::Rectangle const& capture_region, |
523 | geom::Size const& capture_size, |
524 | - MirPixelFormat pixel_format, |
525 | - int nbuffers, |
526 | MirMirrorMode mirror_mode) |
527 | : scene{scene}, |
528 | display_buffer{std::make_unique<ScreencastDisplayBuffer>(capture_region, capture_size, mirror_mode, free_queue, ready_queue, display)}, |
529 | display_buffer_compositor{db_compositor_factory.create_compositor_for(*display_buffer)}, |
530 | - virtual_output{make_virtual_output(display, capture_region)} |
531 | + virtual_output{make_virtual_output(display, capture_region)}, |
532 | + queue_size(capture_size), |
533 | + mirror_mode(mirror_mode) |
534 | { |
535 | - mg::BufferProperties const buffer_properties{capture_size, pixel_format, mg::BufferUsage::hardware}; |
536 | - for (int i = 0; i < nbuffers; i++) |
537 | - free_queue.schedule(buffer_allocator.alloc_buffer(buffer_properties)); |
538 | + for (auto buffer : buffers) |
539 | + free_queue.schedule(buffer); |
540 | |
541 | scene->register_compositor(this); |
542 | if (virtual_output) |
543 | @@ -99,6 +99,9 @@ |
544 | std::shared_ptr<mg::Buffer> capture() |
545 | { |
546 | std::lock_guard<decltype(mutex)> lk(mutex); |
547 | + if (queue_size != display_buffer->renderbuffer_size()) |
548 | + display_buffer->set_renderbuffer_size(queue_size); |
549 | + |
550 | //FIXME:: the client needs a better way to express it is no longer |
551 | //using the last captured buffer |
552 | if (last_captured_buffer) |
553 | @@ -113,6 +116,23 @@ |
554 | void capture(std::shared_ptr<mg::Buffer> const& buffer) |
555 | { |
556 | std::lock_guard<decltype(mutex)> lk(mutex); |
557 | + if (buffer->size() != display_buffer->renderbuffer_size()) |
558 | + display_buffer->set_renderbuffer_size(buffer->size()); |
559 | + |
560 | + //a bit confusingly, the old way of screencasting had the mirror_mode_none |
561 | + //produce upside down buffers. |
562 | + if (mirror_mode == mir_mirror_mode_none) |
563 | + display_buffer->set_transformation(mg::transformation(mir_mirror_mode_vertical)); |
564 | + if (mirror_mode == mir_mirror_mode_vertical) |
565 | + display_buffer->set_transformation(mg::transformation(mir_mirror_mode_none)); |
566 | + if (mirror_mode == mir_mirror_mode_horizontal) |
567 | + { |
568 | + glm::mat2 mat; |
569 | + mat[0][0] = -1; |
570 | + mat[1][1] = -1; |
571 | + display_buffer->set_transformation(mat); |
572 | + } |
573 | + |
574 | auto scheduled = free_queue.num_scheduled(); |
575 | free_queue.schedule(buffer); |
576 | for(auto i = 0u; i < scheduled; i++) |
577 | @@ -121,6 +141,8 @@ |
578 | display_buffer_compositor->composite(scene->scene_elements_for(this)); |
579 | if (buffer != ready_queue.next_buffer()) |
580 | throw std::runtime_error("unable to capture to buffer"); |
581 | + |
582 | + display_buffer->set_transformation(mg::transformation(mirror_mode)); |
583 | } |
584 | |
585 | private: |
586 | @@ -133,6 +155,8 @@ |
587 | std::unique_ptr<compositor::DisplayBufferCompositor> display_buffer_compositor; |
588 | std::unique_ptr<graphics::VirtualOutput> virtual_output; |
589 | std::shared_ptr<mg::Buffer> last_captured_buffer; |
590 | + geom::Size queue_size; |
591 | + MirMirrorMode mirror_mode; |
592 | }; |
593 | |
594 | |
595 | @@ -159,14 +183,20 @@ |
596 | size.height.as_int() == 0 || |
597 | region.size.width.as_int() == 0 || |
598 | region.size.height.as_int() == 0 || |
599 | - pixel_format == mir_pixel_format_invalid || |
600 | - nbuffers < 0) |
601 | + ((nbuffers > 1) && (pixel_format == mir_pixel_format_invalid))) |
602 | { |
603 | BOOST_THROW_EXCEPTION(std::runtime_error("Invalid parameters")); |
604 | } |
605 | std::lock_guard<decltype(session_mutex)> lock{session_mutex}; |
606 | auto const id = next_available_session_id(); |
607 | - session_contexts[id] = create_session_context(region, size, pixel_format, nbuffers, mirror_mode); |
608 | + |
609 | + std::vector<std::shared_ptr<mg::Buffer>> buffers(nbuffers); |
610 | + for (auto& buffer : buffers) |
611 | + { |
612 | + buffer = buffer_allocator->alloc_buffer( |
613 | + mg::BufferProperties{size, pixel_format, mg::BufferUsage::hardware}); |
614 | + } |
615 | + session_contexts[id] = create_session_context(region, size, buffers, mirror_mode); |
616 | |
617 | return id; |
618 | } |
619 | @@ -204,13 +234,11 @@ |
620 | mc::CompositingScreencast::create_session_context( |
621 | geometry::Rectangle const& rect, |
622 | geometry::Size const& size, |
623 | - MirPixelFormat pixel_format, |
624 | - int nbuffers, |
625 | + std::vector<std::shared_ptr<mg::Buffer>> const& buffers, |
626 | MirMirrorMode mirror_mode) |
627 | { |
628 | return std::make_shared<detail::ScreencastSessionContext>( |
629 | - scene, *display, *buffer_allocator, *db_compositor_factory, |
630 | - rect, size, pixel_format, nbuffers, mirror_mode); |
631 | + scene, *display, *db_compositor_factory, buffers, rect, size, mirror_mode); |
632 | } |
633 | |
634 | void mc::CompositingScreencast::capture( |
635 | |
636 | === modified file 'src/server/compositor/compositing_screencast.h' |
637 | --- src/server/compositor/compositing_screencast.h 2017-05-08 03:04:26 +0000 |
638 | +++ src/server/compositor/compositing_screencast.h 2017-06-06 16:26:39 +0000 |
639 | @@ -64,8 +64,7 @@ |
640 | std::shared_ptr<detail::ScreencastSessionContext> |
641 | create_session_context(geometry::Rectangle const& rect, |
642 | geometry::Size const& size, |
643 | - MirPixelFormat pixel_format, |
644 | - int nbuffers, |
645 | + std::vector<std::shared_ptr<graphics::Buffer>> const& buffers, |
646 | MirMirrorMode mirror_mode); |
647 | std::shared_ptr<detail::ScreencastSessionContext> session(frontend::ScreencastSessionId id); |
648 | |
649 | |
650 | === modified file 'src/server/compositor/screencast_display_buffer.cpp' |
651 | --- src/server/compositor/screencast_display_buffer.cpp 2017-05-08 03:04:26 +0000 |
652 | +++ src/server/compositor/screencast_display_buffer.cpp 2017-06-06 16:26:39 +0000 |
653 | @@ -72,7 +72,8 @@ |
654 | rect(rect), |
655 | transform(mg::transformation(mirror_mode)), |
656 | free_queue(free_queue), ready_queue(ready_queue), |
657 | - old_fbo(), old_viewport() |
658 | + old_fbo(), old_viewport(), |
659 | + current_size(size) |
660 | { |
661 | auto const gl_context_raii = mir::raii::paired_calls( |
662 | [this] { gl_context->make_current(); }, |
663 | @@ -182,3 +183,31 @@ |
664 | { |
665 | return this; |
666 | } |
667 | + |
668 | +geom::Size mc::ScreencastDisplayBuffer::renderbuffer_size() |
669 | +{ |
670 | + return current_size; |
671 | +} |
672 | + |
673 | +void mc::ScreencastDisplayBuffer::set_renderbuffer_size(geom::Size size) |
674 | +{ |
675 | + if (size == current_size) |
676 | + return; |
677 | + |
678 | + auto depth_buffer = allocate_gl_resource<glGenRenderbuffers, glDeleteRenderbuffers>(); |
679 | + glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer); |
680 | + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, |
681 | + size.width.as_uint32_t(), |
682 | + size.height.as_uint32_t()); |
683 | + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, |
684 | + GL_RENDERBUFFER, depth_buffer); |
685 | + |
686 | + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) |
687 | + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create FBO for buffer")); |
688 | + depth_rbo = std::move(depth_buffer); |
689 | +} |
690 | + |
691 | +void mc::ScreencastDisplayBuffer::set_transformation(glm::mat2 const& t) |
692 | +{ |
693 | + transform = t; |
694 | +} |
695 | |
696 | === modified file 'src/server/compositor/screencast_display_buffer.h' |
697 | --- src/server/compositor/screencast_display_buffer.h 2017-05-08 03:04:26 +0000 |
698 | +++ src/server/compositor/screencast_display_buffer.h 2017-06-06 16:26:39 +0000 |
699 | @@ -116,6 +116,10 @@ |
700 | |
701 | NativeDisplayBuffer* native_display_buffer() override; |
702 | |
703 | + geometry::Size renderbuffer_size(); |
704 | + void set_renderbuffer_size(geometry::Size); |
705 | + void set_transformation(glm::mat2 const& transform); |
706 | + |
707 | private: |
708 | std::unique_ptr<renderer::gl::Context> gl_context; |
709 | geometry::Rectangle const rect; |
710 | @@ -131,6 +135,8 @@ |
711 | detail::GLResource<glDeleteTextures> color_tex; |
712 | detail::GLResource<glDeleteRenderbuffers> depth_rbo; |
713 | detail::GLResource<glDeleteFramebuffers> fbo; |
714 | + |
715 | + geometry::Size current_size; |
716 | }; |
717 | |
718 | } |
719 | |
720 | === modified file 'src/server/frontend/protobuf_message_processor.cpp' |
721 | --- src/server/frontend/protobuf_message_processor.cpp 2017-05-08 03:04:26 +0000 |
722 | +++ src/server/frontend/protobuf_message_processor.cpp 2017-06-06 16:26:39 +0000 |
723 | @@ -425,7 +425,7 @@ |
724 | void mfd::ProtobufMessageProcessor::send_response( |
725 | ::google::protobuf::uint32 id, mir::protobuf::Screencast* response) |
726 | { |
727 | - if (response->has_buffer_stream()) |
728 | + if (response->has_buffer_stream() && response->buffer_stream().has_buffer()) |
729 | sender->send_response(id, response, |
730 | {extract_fds_from(response->mutable_buffer_stream()->mutable_buffer())}); |
731 | else |
732 | |
733 | === modified file 'src/server/frontend/session_mediator.cpp' |
734 | --- src/server/frontend/session_mediator.cpp 2017-05-25 08:58:03 +0000 |
735 | +++ src/server/frontend/session_mediator.cpp 2017-06-06 16:26:39 +0000 |
736 | @@ -826,6 +826,7 @@ |
737 | protobuf_screencast->mutable_buffer_stream()->set_pixel_format(pixel_format); |
738 | protobuf_screencast->mutable_buffer_stream()->mutable_id()->set_value( |
739 | screencast_session_id.as_value()); |
740 | + protobuf_screencast->mutable_buffer_stream()->set_pixel_format(pixel_format); |
741 | |
742 | done->Run(); |
743 | } |
744 | @@ -869,7 +870,6 @@ |
745 | auto session = weak_session.lock(); |
746 | ScreencastSessionId const screencast_session_id{request->id().value()}; |
747 | auto buffer = session->get_buffer(mg::BufferID{request->buffer_id()}); |
748 | - mf::ScreencastSessionId const screencast_id{request->id().value()}; |
749 | screencast->capture(screencast_session_id, buffer); |
750 | done->Run(); |
751 | } |
752 | |
753 | === modified file 'tests/acceptance-tests/test_client_screencast.cpp' |
754 | --- tests/acceptance-tests/test_client_screencast.cpp 2017-05-08 03:04:26 +0000 |
755 | +++ tests/acceptance-tests/test_client_screencast.cpp 2017-06-06 16:26:39 +0000 |
756 | @@ -24,6 +24,7 @@ |
757 | #include "mir_test_framework/connected_client_headless_server.h" |
758 | #include "mir/test/doubles/stub_session_authorizer.h" |
759 | #include "mir/test/fake_shared.h" |
760 | +#include "mir/test/doubles/mock_gl.h" |
761 | |
762 | #include <gtest/gtest.h> |
763 | #include <gmock/gmock.h> |
764 | @@ -32,6 +33,8 @@ |
765 | namespace mtd = mir::test::doubles; |
766 | namespace mf = mir::frontend; |
767 | namespace mt = mir::test; |
768 | +using namespace testing; |
769 | +using namespace std::literals::chrono_literals; |
770 | |
771 | namespace |
772 | { |
773 | @@ -48,6 +51,7 @@ |
774 | struct Screencast : mtf::HeadlessInProcessServer |
775 | { |
776 | MockSessionAuthorizer mock_authorizer; |
777 | + NiceMock<mtd::MockGL> mockgl; |
778 | |
779 | void SetUp() override |
780 | { |
781 | @@ -126,3 +130,84 @@ |
782 | mir_screencast_release_sync(screencast); |
783 | mir_connection_release(connection); |
784 | } |
785 | + |
786 | +TEST_F(Screencast, can_cast_to_buffer) |
787 | +{ |
788 | + EXPECT_CALL(mock_authorizer, screencast_is_allowed(_)) |
789 | + .WillOnce(Return(true)); |
790 | + auto const connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); |
791 | + |
792 | + auto const buffer = mir_connection_allocate_buffer_sync( |
793 | + connection, default_width, default_height, default_pixel_format); |
794 | + |
795 | + ASSERT_TRUE(buffer); |
796 | + |
797 | + MirScreencastSpec* spec = mir_create_screencast_spec(connection); |
798 | + //We have to set nbuffers == 0 now to avoid capturing at startup. Current default is 1. |
799 | + mir_screencast_spec_set_number_of_buffers(spec, 0); |
800 | + |
801 | + mir_screencast_spec_set_capture_region(spec, &default_capture_region); |
802 | + auto screencast = mir_screencast_create_sync(spec); |
803 | + mir_screencast_spec_release(spec); |
804 | + |
805 | + struct Capture |
806 | + { |
807 | + std::mutex mutex; |
808 | + std::condition_variable cv; |
809 | + bool capture = false; |
810 | + } capture; |
811 | + |
812 | + mir_screencast_capture_to_buffer(screencast, buffer, |
813 | + [] (MirScreencastResult /*status*/, MirBuffer* /*buffer*/, void* context) { |
814 | + auto c = reinterpret_cast<Capture*>(context); |
815 | + std::unique_lock<decltype(c->mutex)> lk(c->mutex); |
816 | + c->capture = true; |
817 | + c->cv.notify_all(); |
818 | + }, &capture ); |
819 | + |
820 | + std::unique_lock<decltype(capture.mutex)> lk2(capture.mutex); |
821 | + ASSERT_TRUE(capture.cv.wait_for(lk2, 5s, [&] { return capture.capture; })); |
822 | + EXPECT_THAT(mir_buffer_get_error_message(buffer), StrEq("")); |
823 | + |
824 | + mir_screencast_release_sync(screencast); |
825 | + mir_connection_release(connection); |
826 | +} |
827 | + |
828 | +TEST_F(Screencast, can_cast_to_buffer_sync) |
829 | +{ |
830 | + EXPECT_CALL(mock_authorizer, screencast_is_allowed(_)) |
831 | + .WillOnce(Return(true)); |
832 | + auto const connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); |
833 | + |
834 | + struct BufferSync |
835 | + { |
836 | + MirBuffer* buffer = nullptr; |
837 | + std::mutex mutex; |
838 | + std::condition_variable cv; |
839 | + } buffer_info; |
840 | + |
841 | + mir_connection_allocate_buffer( |
842 | + connection, |
843 | + default_width, default_height, default_pixel_format, |
844 | + [](MirBuffer* b, void* ctxt) { |
845 | + auto info = reinterpret_cast<BufferSync*>(ctxt); |
846 | + std::unique_lock<decltype(info->mutex)> lk(info->mutex); |
847 | + info->buffer = b; |
848 | + info->cv.notify_all(); |
849 | + }, &buffer_info); |
850 | + std::unique_lock<decltype(buffer_info.mutex)> lk(buffer_info.mutex); |
851 | + ASSERT_TRUE(buffer_info.cv.wait_for(lk, 5s, [&] { return buffer_info.buffer; })); |
852 | + |
853 | + MirScreencastSpec* spec = mir_create_screencast_spec(connection); |
854 | + //We have to set nbuffers == 0 now to avoid capturing at startup. Current default is 1. |
855 | + mir_screencast_spec_set_number_of_buffers(spec, 0); |
856 | + |
857 | + mir_screencast_spec_set_capture_region(spec, &default_capture_region); |
858 | + auto screencast = mir_screencast_create_sync(spec); |
859 | + |
860 | + mir_screencast_spec_release(spec); |
861 | + mir_screencast_capture_to_buffer_sync(screencast, buffer_info.buffer); |
862 | + |
863 | + mir_screencast_release_sync(screencast); |
864 | + mir_connection_release(connection); |
865 | +} |
866 | |
867 | === modified file 'tests/unit-tests/compositor/test_compositing_screencast.cpp' |
868 | --- tests/unit-tests/compositor/test_compositing_screencast.cpp 2017-05-08 03:04:26 +0000 |
869 | +++ tests/unit-tests/compositor/test_compositing_screencast.cpp 2017-06-06 16:26:39 +0000 |
870 | @@ -251,7 +251,6 @@ |
871 | EXPECT_THROW(screencast.create_session(invalid_region, default_size, default_pixel_format, default_num_buffers, default_mirror_mode), std::runtime_error); |
872 | EXPECT_THROW(screencast.create_session(default_region, invalid_size, default_pixel_format, default_num_buffers, default_mirror_mode), std::runtime_error); |
873 | EXPECT_THROW(screencast.create_session(default_region, default_size, mir_pixel_format_invalid, default_num_buffers, default_mirror_mode), std::runtime_error); |
874 | - EXPECT_THROW(screencast.create_session(default_region, default_size, mir_pixel_format_invalid, 0, default_mirror_mode), std::runtime_error); |
875 | } |
876 | |
877 | TEST_F(CompositingScreencastTest, throws_on_capture_with_invalid_session_id) |
FAILED: Continuous integration, rev:3964 /mir-jenkins. ubuntu. com/job/ mir-ci/ 3035/ /mir-jenkins. ubuntu. com/job/ build-mir/ 4055/console /mir-jenkins. ubuntu. com/job/ build-0- fetch/4142 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= vivid+overlay/ 4132 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= xenial+ overlay/ 4132 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= zesty/4132 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= zesty/4082/ console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= xenial+ overlay/ 4082/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= zesty/4082/ console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= android, release= vivid+overlay/ 4082 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= android, release= vivid+overlay/ 4082/artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= android, release= vivid+overlay/ 4082 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= android, release= vivid+overlay/ 4082/artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= mesa,release= xenial+ overlay/ 4082/console
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild: /mir-jenkins. ubuntu. com/job/ mir-ci/ 3035/rebuild
https:/