Argus Camera Sample
Argus Camera Sample
 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, 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 
39 #include "Composer.h"
40 #include "Renderer.h"
41 #include "Window.h"
42 #include "StreamConsumer.h"
43 
44 namespace ArgusSamples
45 {
46 
48  : m_initialized(false)
49  , m_program(0)
50  , m_windowWidth(0)
51  , m_windowHeight(0)
52  , m_windowAspectRatio(1.0f)
53  , m_clientSequence(0)
54  , m_composerSequence(0)
55 {
56 }
57 
59 {
60  shutdown();
61 }
62 
64 {
65  if (m_initialized)
66  return true;
67 
68  PROPAGATE_ERROR(m_mutex.initialize());
69 
70  PROPAGATE_ERROR(m_sequenceMutex.initialize());
71  PROPAGATE_ERROR(m_sequenceCond.initialize());
72 
73  // initialize the window size
74  Window &window = Window::getInstance();
75  PROPAGATE_ERROR(onResize(window.getWidth(), window.getHeight()));
76 
77  // and register as observer for size changes
78  PROPAGATE_ERROR(window.registerObserver(this));
79 
80  PROPAGATE_ERROR(Thread::initialize());
81  PROPAGATE_ERROR(Thread::waitRunning());
82 
83  m_initialized = true;
84 
85  return true;
86 }
87 
89 {
90  if (!m_initialized)
91  return true;
92 
93  PROPAGATE_ERROR_CONTINUE(Window::getInstance().unregisterObserver(this));
94 
95  // request shutdown of the thread
96  PROPAGATE_ERROR_CONTINUE(Thread::requestShutdown());
97 
98  // trigger a re-compose to wake up the thread
99  PROPAGATE_ERROR_CONTINUE(reCompose());
100 
101  PROPAGATE_ERROR_CONTINUE(Thread::shutdown());
102 
103  for (StreamList::iterator it = m_streams.begin(); it != m_streams.end(); ++it)
104  {
105  PROPAGATE_ERROR_CONTINUE(it->m_consumer->shutdown());
106  delete it->m_consumer;
107  }
108  m_streams.clear();
109 
110  m_initialized = false;
111 
112  return true;
113 }
114 
115 bool Composer::bindStream(EGLStreamKHR eglStream)
116 {
117  if (eglStream == EGL_NO_STREAM_KHR)
118  ORIGINATE_ERROR("Invalid stream");
119 
120  PROPAGATE_ERROR(initialize());
121 
122  UniquePointer<StreamConsumer> streamConsumer(new StreamConsumer(eglStream));
123  if (!streamConsumer)
124  ORIGINATE_ERROR("Out of memory");
125 
126  PROPAGATE_ERROR(streamConsumer->initialize());
127 
128  ScopedMutex sm(m_mutex);
129  PROPAGATE_ERROR(sm.expectLocked());
130 
131  m_streams.push_back(Stream(streamConsumer.release()));
132 
133  // trigger a re-compose because the stream configuration changed
134  PROPAGATE_ERROR(reCompose());
135 
136  return true;
137 }
138 
139 bool Composer::unbindStream(EGLStreamKHR eglStream)
140 {
141  ScopedMutex sm(m_mutex);
142  PROPAGATE_ERROR(sm.expectLocked());
143 
144  for (StreamList::iterator it = m_streams.begin(); it != m_streams.end(); ++it)
145  {
146  if (it->m_consumer->isEGLStream(eglStream))
147  {
148  PROPAGATE_ERROR(it->m_consumer->shutdown());
149  delete it->m_consumer;
150  m_streams.erase(it);
151  // trigger a re-compose because the stream configuration changed
152  PROPAGATE_ERROR(reCompose());
153  return true;
154  }
155  }
156 
157  ORIGINATE_ERROR("Stream was not bound");
158 
159  return true;
160 }
161 
162 bool Composer::setStreamActive(EGLStreamKHR eglStream, bool active)
163 {
164  ScopedMutex sm(m_mutex);
165  PROPAGATE_ERROR(sm.expectLocked());
166 
167  for (StreamList::iterator it = m_streams.begin(); it != m_streams.end(); ++it)
168  {
169  if (it->m_consumer->isEGLStream(eglStream))
170  {
171  it->m_active = active;
172  // trigger a re-compose because the stream configuration changed
173  PROPAGATE_ERROR(reCompose());
174  return true;
175  }
176  }
177 
178  ORIGINATE_ERROR("Stream was not bound");
179 
180  return true;
181 }
182 
183 bool Composer::setStreamAspectRatio(EGLStreamKHR eglStream, float aspectRatio)
184 {
185  ScopedMutex sm(m_mutex);
186  PROPAGATE_ERROR(sm.expectLocked());
187 
188  for (StreamList::iterator it = m_streams.begin(); it != m_streams.end(); ++it)
189  {
190  if (it->m_consumer->isEGLStream(eglStream))
191  {
192  PROPAGATE_ERROR(it->m_consumer->setStreamAspectRatio(aspectRatio));
193  // trigger a re-compose because the stream configuration changed
194  PROPAGATE_ERROR(reCompose());
195  return true;
196  }
197  }
198 
199  ORIGINATE_ERROR("Stream was not bound");
200 
201  return true;
202 }
203 
205 {
207  PROPAGATE_ERROR(sm.expectLocked());
208 
210  PROPAGATE_ERROR(m_sequenceCond.signal());
211 
212  return true;
213 }
214 
215 bool Composer::onResize(uint32_t width, uint32_t height)
216 {
217  m_windowWidth = width;
218  m_windowHeight = height;
219  m_windowAspectRatio = (float)width / (float)height;
220  return true;
221 }
222 
224 {
225  Renderer &renderer = Renderer::getInstance();
226 
227  // Initialize the GL context and make it current.
228  PROPAGATE_ERROR(m_context.initialize(&Window::getInstance()));
229  PROPAGATE_ERROR(m_context.makeCurrent());
230 
231  // Create the shader program.
232  static const char vtxSrc[] =
233  "#version 300 es\n"
234  "#extension GL_ARB_explicit_uniform_location : require\n"
235  "in layout(location = 0) vec2 vertex;\n"
236  "out vec2 vTexCoord;\n"
237  "layout(location = 0) uniform vec2 offset;\n"
238  "layout(location = 1) uniform vec2 scale;\n"
239  "void main() {\n"
240  " gl_Position = vec4((offset + vertex * scale) * 2.0 - 1.0, 0.0, 1.0);\n"
241  " vTexCoord = vec2(vertex.x, 1.0 - vertex.y);\n"
242  "}\n";
243  static const char frgSrc[] =
244  "#version 300 es\n"
245  "#extension GL_OES_EGL_image_external : require\n"
246  "precision highp float;\n"
247  "uniform samplerExternalOES texSampler;\n"
248  "in vec2 vTexCoord;\n"
249  "out vec4 fragColor;\n"
250  "void main() {\n"
251  " fragColor = texture2D(texSampler, vTexCoord);\n"
252  "}\n";
253  PROPAGATE_ERROR(m_context.createProgram(vtxSrc, frgSrc, &m_program));
254 
255  // Setup vertex state.
256  static const GLfloat vertices[] = {
257  0.0f, 0.0f,
258  0.0f, 1.0f,
259  1.0f, 0.0f,
260  1.0f, 1.0f,
261  };
262  glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices);
263  glEnableVertexAttribArray(0);
264 
265  if (eglSwapInterval(renderer.getEGLDisplay(), 1) != EGL_TRUE)
266  ORIGINATE_ERROR("Failed to set the swap interval");
267 
268  return true;
269 }
270 
272 {
273  {
274  // wait for the sequence condition
276  PROPAGATE_ERROR(sm.expectLocked());
277 
278  PROPAGATE_ERROR(m_sequenceCond.wait(m_sequenceMutex));
279 
280  // check if the sequence differs
282  return true;
283 
284  // need to compose
286  }
287 
288  glViewport(0,0, m_windowWidth, m_windowHeight);
289 
290  glClear(GL_COLOR_BUFFER_BIT);
291 
292  // take a copy of the stream list to avoid holding the mutex too long
293  StreamList streamsCopy;
294  {
295  ScopedMutex sm(m_mutex);
296  PROPAGATE_ERROR(sm.expectLocked());
297 
298  streamsCopy = m_streams;
299  }
300 
301  if (streamsCopy.size() != 0)
302  {
303  // iterate through the streams and render them
304 
305  glUseProgram(m_program);
306 
307  uint32_t activeStreams = 0;
308  for (StreamList::iterator it = streamsCopy.begin(); it != streamsCopy.end(); ++it)
309  {
310  if (it->m_active)
311  ++activeStreams;
312  }
313 
314  const uint32_t cells = static_cast<uint32_t>(ceil(sqrt(activeStreams)));
315  const float scaleX = 1.0f / cells;
316  const float scaleY = 1.0f / cells;
317  uint32_t offsetX = 0, offsetY = cells - 1;
318 
319  for (StreamList::iterator it = streamsCopy.begin(); it != streamsCopy.end(); ++it)
320  {
321  if (it->m_active)
322  {
323 #ifdef WAR_EGL_STREAM_RACE
324  // hold the stream texture mutex while drawing with the texture
325  ScopedMutex sm(it->m_consumer->getStreamTextureMutex());
326  PROPAGATE_ERROR(sm.expectLocked());
327 #endif // WAR_EGL_STREAM_RACE
328 
329  glBindTexture(GL_TEXTURE_EXTERNAL_OES, it->m_consumer->getStreamTextureID());
330 
331  // scale according to aspect ratios
332  float sizeX = it->m_consumer->getStreamAspectRatio() / m_windowAspectRatio;
333  float sizeY = 1.0f;
334 
335  if (sizeX > sizeY)
336  {
337  sizeY /= sizeX;
338  sizeX = 1.0f;
339  }
340  else
341  {
342  sizeX /= sizeY;
343  sizeY = 1.0f;
344  }
345 
346  glUniform2f(0,
347  (offsetX - (sizeX - 1.0f) * 0.5f) * scaleX,
348  (offsetY - (sizeY - 1.0f) * 0.5f) * scaleY);
349  glUniform2f(1, scaleX * sizeX, scaleY * sizeY);
350 
351  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
352 
353  glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
354 
355  ++offsetX;
356  if (offsetX == cells)
357  {
358  --offsetY;
359  offsetX = 0;
360  }
361  }
362  }
363 
364  glUseProgram(0);
365  }
366 
367  PROPAGATE_ERROR(m_context.swapBuffers());
368 
369  return true;
370 }
371 
373 {
374  glDeleteProgram(m_program);
375  m_program = 0;
376 
377  PROPAGATE_ERROR(m_context.cleanup());
378 
379  return true;
380 }
381 
382 }; // namespace ArgusSamples