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-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 
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 
59 {
60  if (!shutdown())
61  REPORT_ERROR("Failed to shutdown composer");
62 }
63 
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 
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 
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 
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 
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 
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