Argus Camera Sample Argus Camera Sample

- Main Page
- Related Pages
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Composer.cpp
Go to the documentation of this file.
1 /*
2 * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of NVIDIA CORPORATION nor the names of its
13 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #define GL_GLEXT_PROTOTYPES
30
31 #include <GLES3/gl31.h>
32 #include <GLES2/gl2ext.h>
33
34 #include <math.h>
35
36 #include "Error.h"
37 #include "UniquePointer.h"
38 #include "InitOnce.h"
39
40 #include "Composer.h"
41 #include "Window.h"
42 #include "StreamConsumer.h"
43 #include "PerfTracker.h"
44
45 namespace ArgusSamples
46 {
47
48 Composer::Composer()
49 : m_initialized(false)
50 , m_program(0)
51 , m_vbo(0)
52 , m_windowWidth(0)
53 , m_windowHeight(0)
54 , m_windowAspectRatio(1.0f)
55 {
56 }
57
58 Composer::~Composer()
59 {
60 if (!shutdown())
61 REPORT_ERROR("Failed to shutdown composer");
62 }
63
64 Composer &Composer::getInstance()
65 {
66 static InitOnce initOnce;
67 static Composer instance;
68
69 if (initOnce.begin())
70 {
71 if (instance.initialize())
72 {
73 initOnce.complete();
74 }
75 else
76 {
77 initOnce.failed();
78 REPORT_ERROR("Initalization failed");
79 }
80 }
81
82 return instance;
83 }
84
85 bool Composer::initialize()
86 {
87 if (m_initialized)
88 return true;
89
90 Window &window = Window::getInstance();
91
92 PROPAGATE_ERROR(m_display.initialize(window.getEGLNativeDisplay()));
93
94 PROPAGATE_ERROR(m_mutex.initialize());
95
96 // initialize the window size
97 PROPAGATE_ERROR(onResize(window.getWidth(), window.getHeight()));
98
99 // and register as observer for size changes
100 PROPAGATE_ERROR(window.registerObserver(this));
101
102 PROPAGATE_ERROR(Thread::initialize());
103 PROPAGATE_ERROR(Thread::waitRunning());
104
105 m_initialized = true;
106
107 return true;
108 }
109
110 bool Composer::shutdown()
111 {
112 if (!m_initialized)
113 return true;
114
115 PROPAGATE_ERROR_CONTINUE(Window::getInstance().unregisterObserver(this));
116
117 // request shutdown of the thread
118 PROPAGATE_ERROR_CONTINUE(Thread::requestShutdown());
119
120 PROPAGATE_ERROR_CONTINUE(Thread::shutdown());
121
122 PROPAGATE_ERROR_CONTINUE(m_display.cleanup());
123
124 m_initialized = false;
125
126 return true;
127 }
128
129 bool Composer::bindStream(EGLStreamKHR eglStream)
130 {
131 if (eglStream == EGL_NO_STREAM_KHR)
132 ORIGINATE_ERROR("Invalid stream");
133
134 PROPAGATE_ERROR(initialize());
135
136 UniquePointer<StreamConsumer> streamConsumer(new StreamConsumer(eglStream));
137 if (!streamConsumer)
138 ORIGINATE_ERROR("Out of memory");
139
140 // add the new stream consumer to the stream list
141 {
142 ScopedMutex sm(m_mutex);
143 PROPAGATE_ERROR(sm.expectLocked());
144
145 m_streams.push_back(Stream(streamConsumer.get()));
146 }
147
148 // wait until the stream is connected
149 while (streamConsumer->getStreamState() != EGL_STREAM_STATE_CONNECTING_KHR)
150 usleep(1000);
151 streamConsumer.release();
152
153 return true;
154 }
155
156 bool Composer::unbindStream(EGLStreamKHR eglStream)
157 {
158 ScopedMutex sm(m_mutex);
159 PROPAGATE_ERROR(sm.expectLocked());
160
161 for (StreamList::iterator it = m_streams.begin(); it != m_streams.end(); ++it)
162 {
163 if (it->m_consumer->isEGLStream(eglStream))
164 {
165 // set the shutdown flag, the composer thread will do the actual shutdown
166 it->m_shutdown = true;
167 return true;
168 }
169 }
170
171 ORIGINATE_ERROR("Stream was not bound");
172
173 return true;
174 }
175
176 bool Composer::setStreamActive(EGLStreamKHR eglStream, bool active)
177 {
178 ScopedMutex sm(m_mutex);
179 PROPAGATE_ERROR(sm.expectLocked());
180
181 for (StreamList::iterator it = m_streams.begin(); it != m_streams.end(); ++it)
182 {
183 if (it->m_consumer->isEGLStream(eglStream))
184 {
185 it->m_active = active;
186 return true;
187 }
188 }
189
190 ORIGINATE_ERROR("Stream was not bound");
191
192 return true;
193 }
194
195 bool Composer::setStreamAspectRatio(EGLStreamKHR eglStream, float aspectRatio)
196 {
197 ScopedMutex sm(m_mutex);
198 PROPAGATE_ERROR(sm.expectLocked());
199
200 for (StreamList::iterator it = m_streams.begin(); it != m_streams.end(); ++it)
201 {
202 if (it->m_consumer->isEGLStream(eglStream))
203 {
204 PROPAGATE_ERROR(it->m_consumer->setStreamAspectRatio(aspectRatio));
205 return true;
206 }
207 }
208
209 ORIGINATE_ERROR("Stream was not bound");
210
211 return true;
212 }
213
214 bool Composer::onResize(uint32_t width, uint32_t height)
215 {
216 m_windowWidth = width;
217 m_windowHeight = height;
218 m_windowAspectRatio = (float)width / (float)height;
219 return true;
220 }
221
222 bool Composer::threadInitialize()
223 {
224 // Initialize the GL context and make it current.
225 PROPAGATE_ERROR(m_context.initialize(&Window::getInstance()));
226 PROPAGATE_ERROR(m_context.makeCurrent());
227
228 // Create the shader program.
229 static const char vtxSrc[] =
230 "#version 300 es\n"
231 "#extension GL_ARB_explicit_uniform_location : require\n"
232 "in layout(location = 0) vec2 vertex;\n"
233 "out vec2 vTexCoord;\n"
234 "layout(location = 0) uniform vec2 offset;\n"
235 "layout(location = 1) uniform vec2 scale;\n"
236 "void main() {\n"
237 " gl_Position = vec4((offset + vertex * scale) * 2.0 - 1.0, 0.0, 1.0);\n"
238 " vTexCoord = vec2(vertex.x, 1.0 - vertex.y);\n"
239 "}\n";
240 static const char frgSrc[] =
241 "#version 300 es\n"
242 "#extension GL_OES_EGL_image_external : require\n"
243 "precision highp float;\n"
244 "uniform samplerExternalOES texSampler;\n"
245 "in vec2 vTexCoord;\n"
246 "out vec4 fragColor;\n"
247 "void main() {\n"
248 " fragColor = texture2D(texSampler, vTexCoord);\n"
249 "}\n";
250 PROPAGATE_ERROR(m_context.createProgram(vtxSrc, frgSrc, &m_program));
251
252 glUseProgram(m_program);
253
254 // Setup vertex state.
255 static const GLfloat vertices[] = {
256 0.0f, 0.0f,
257 0.0f, 1.0f,
258 1.0f, 0.0f,
259 1.0f, 1.0f,
260 };
261 glGenBuffers(1, &m_vbo);
262 glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
263 if (!m_vbo)
264 ORIGINATE_ERROR("Failed to create VBO");
265 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
266 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
267 glBindBuffer(GL_ARRAY_BUFFER, 0);
268 glEnableVertexAttribArray(0);
269
270 // sync to the display refresh rate
271 if (eglSwapInterval(m_display.get(), 1) != EGL_TRUE)
272 ORIGINATE_ERROR("Failed to set the swap interval");
273
274 return true;
275 }
276
277 bool Composer::renderStreams(uint32_t activeStreams)
278 {
279 glViewport(0,0, m_windowWidth, m_windowHeight);
280
281 glClear(GL_COLOR_BUFFER_BIT);
282
283 const uint32_t cells = static_cast<uint32_t>(ceil(sqrt(activeStreams)));
284 const float scaleX = 1.0f / cells;
285 const float scaleY = 1.0f / cells;
286 uint32_t offsetX = 0, offsetY = cells - 1;
287
288 {
289 ScopedMutex sm(m_mutex);
290 PROPAGATE_ERROR(sm.expectLocked());
291
292 for (StreamList::iterator it = m_streams.begin(); it != m_streams.end(); ++it)
293 {
294 if (!it->m_active)
295 continue;
296
297 const EGLint streamState = it->m_consumer->getStreamState();
298 if ((streamState == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR) ||
299 (streamState == EGL_STREAM_STATE_OLD_FRAME_AVAILABLE_KHR))
300 {
301 glBindTexture(GL_TEXTURE_EXTERNAL_OES, it->m_consumer->getStreamTextureID());
302
303 // scale according to aspect ratios
304 float sizeX = it->m_consumer->getStreamAspectRatio() / m_windowAspectRatio;
305 float sizeY = 1.0f;
306
307 if (sizeX > sizeY)
308 {
309 sizeY /= sizeX;
310 sizeX = 1.0f;
311 }
312 else
313 {
314 sizeX /= sizeY;
315 sizeY = 1.0f;
316 }
317
318 glUniform2f(0,
319 (offsetX - (sizeX - 1.0f) * 0.5f) * scaleX,
320 (offsetY - (sizeY - 1.0f) * 0.5f) * scaleY);
321 glUniform2f(1, scaleX * sizeX, scaleY * sizeY);
322
323 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
324 }
325
326 ++offsetX;
327 if (offsetX == cells)
328 {
329 --offsetY;
330 offsetX = 0;
331 }
332 }
333 }
334
335 PROPAGATE_ERROR(m_context.swapBuffers());
336
337 PROPAGATE_ERROR(PerfTracker::getInstance().onEvent(GLOBAL_EVENT_DISPLAY));
338
339 return true;
340 }
341
342 bool Composer::threadExecute()
343 {
344 bool render = false;
345 uint32_t activeStreams = 0;
346
347 {
348 ScopedMutex sm(m_mutex);
349 PROPAGATE_ERROR(sm.expectLocked());
350
351 if (m_streams.size() == 0)
352 return true;
353
354 // first iterate through the streams and check if there are streams which should be shutdown
355 // also count the active streams
356 for (StreamList::iterator it = m_streams.begin(); it != m_streams.end(); ++it)
357 {
358 if (it->m_shutdown)
359 {
360 // shutdown the stream consumer if it had marked so
361 PROPAGATE_ERROR_CONTINUE(it->m_consumer->shutdown());
362 delete it->m_consumer;
363 it = m_streams.erase(it);
364 continue;
365 }
366
367 // do the acquire in any case even if the stream is not active (needed to get the
368 // transition to connecting state)
369 bool acquiredNewFrame = false;
370 PROPAGATE_ERROR(it->m_consumer->acquire(&acquiredNewFrame));
371
372 // check if the stream is active
373 if (it->m_active)
374 {
375 ++activeStreams;
376 // if a new frame is available we need to render
377 if (acquiredNewFrame)
378 render = true;
379 }
380 }
381 }
382
383 if (render)
384 {
385 PROPAGATE_ERROR(renderStreams(activeStreams));
386 }
387 else
388 {
389 // wait some time and then check again if new frames are available
390 usleep(1000);
391 }
392
393 return true;
394 }
395
396 bool Composer::threadShutdown()
397 {
398 for (StreamList::iterator it = m_streams.begin(); it != m_streams.end(); ++it)
399 {
400 PROPAGATE_ERROR_CONTINUE(it->m_consumer->shutdown());
401 delete it->m_consumer;
402 }
403 m_streams.clear();
404
405 if (m_program)
406 {
407 glDeleteProgram(m_program);
408 m_program = 0;
409 }
410 if (m_vbo)
411 {
412 glDeleteBuffers(1, &m_vbo);
413 m_vbo = 0;
414 }
415
416 PROPAGATE_ERROR(m_context.cleanup());
417
418 return true;
419 }
420
421 }; // namespace ArgusSamples