Argus Camera Sample
Argus Camera Sample
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
StreamConsumer.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 <unistd.h>
35 
36 #include "StreamConsumer.h"
37 #include "Renderer.h"
38 #include "Thread.h"
39 #include "Error.h"
40 #include "Util.h" // for TimeValue
41 
42 namespace ArgusSamples
43 {
44 
45 StreamConsumer::StreamConsumer(EGLStreamKHR eglStream)
46  : m_eglStream(eglStream)
47  , m_streamState(EGL_NONE)
48  , m_streamTexture(0)
49  , m_aspectRatio(1.0f)
51  , m_sync(EGL_NO_SYNC_KHR)
52 #endif // WAR_EGL_STREAM_RACE
53 {
54 }
55 
57 {
58  shutdown();
59 }
60 
62 {
63 #ifdef WAR_EGL_STREAM_RACE
64  PROPAGATE_ERROR(m_mutex.initialize());
65 #endif // WAR_EGL_STREAM_RACE
66  PROPAGATE_ERROR(Thread::initialize());
67  PROPAGATE_ERROR(Thread::waitRunning());
68  return true;
69 }
70 
72 {
73  PROPAGATE_ERROR(Thread::shutdown());
74  return true;
75 }
76 
77 bool StreamConsumer::isEGLStream(EGLStreamKHR eglStream) const
78 {
79  return (m_eglStream == eglStream);
80 }
81 
83 {
84  return m_streamTexture;
85 }
86 
87 bool StreamConsumer::setStreamAspectRatio(float aspectRatio)
88 {
89  m_aspectRatio = aspectRatio;
90  return true;
91 }
92 
94 {
95  return m_aspectRatio;
96 }
97 
98 #ifdef WAR_EGL_STREAM_RACE
100 {
101  return m_mutex;
102 }
103 #endif // WAR_EGL_STREAM_RACE
104 
106 {
107  Renderer &renderer = Renderer::getInstance();
108 
109  // Initialize the GL context and make it current.
110  PROPAGATE_ERROR(m_context.initialize(&renderer.getComposerContext()));
111  PROPAGATE_ERROR(m_context.makeCurrent());
112 
113  // Create an external texture and connect it to the stream as a the consumer.
114  glGenTextures(1, &m_streamTexture);
115  if (m_streamTexture == 0)
116  ORIGINATE_ERROR("Failed to create GL texture");
117 
118  glBindTexture(GL_TEXTURE_EXTERNAL_OES, m_streamTexture);
119  if (!eglStreamConsumerGLTextureExternalKHR(renderer.getEGLDisplay(), m_eglStream))
120  ORIGINATE_ERROR("Unable to connect GL as consumer");
121  glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
122 
123 #if defined(WAR_EGL_STREAM_RACE) && defined(EGL_NV_stream_sync)
124  // create a sync object
125  m_sync = eglCreateStreamSyncNV(renderer.getEGLDisplay(), m_eglStream, EGL_SYNC_NEW_FRAME_NV,
126  NULL);
127  if (m_sync == EGL_NO_SYNC_KHR)
128  ORIGINATE_ERROR("eglCreateStreamSyncNV failed (error 0x%04x)", eglGetError());
129 #endif // WAR_EGL_STREAM_RACE && EGL_NV_stream_sync
130 
131  // Set a negative timeout for the stream acquire so that it blocks indefinitely.
132  if (!eglStreamAttribKHR(renderer.getEGLDisplay(), m_eglStream,
133  EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR, -1))
134  {
135  ORIGINATE_ERROR("eglStreamAttribKHR failed (error 0x%04x)", eglGetError());
136  }
137 
138  return true;
139 }
140 
142 {
143  Renderer &renderer = Renderer::getInstance();
144  const EGLDisplay display = renderer.getEGLDisplay();
145 
146  // check the stream state
147  if ((m_streamState == EGL_NONE) ||
148  (m_streamState == EGL_STREAM_STATE_CONNECTING_KHR)
149 #ifdef WAR_EGL_STREAM_RACE
150  || !m_sync
151 #endif // WAR_EGL_STREAM_RACE
152  )
153  {
154  if (!eglQueryStreamKHR(display, m_eglStream, EGL_STREAM_STATE_KHR, &m_streamState))
155  ORIGINATE_ERROR("eglQueryStreamKHR failed");
156 
157  if (m_streamState == EGL_STREAM_STATE_CONNECTING_KHR)
158  {
159  // if the producer is not yet connected sleep and try again
160  usleep(1000);
161  }
162  }
163 
164  if ((m_streamState == EGL_BAD_STREAM_KHR) ||
165  (m_streamState == EGL_BAD_STATE_KHR))
166  {
167  ORIGINATE_ERROR("EGL stream is in bad state (0x%04x)", m_streamState);
168  }
169  else if ((m_streamState == EGL_STREAM_STATE_EMPTY_KHR) ||
170  (m_streamState == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR) ||
171  (m_streamState == EGL_STREAM_STATE_OLD_FRAME_AVAILABLE_KHR))
172  {
173 #ifdef WAR_EGL_STREAM_RACE
174  bool hasNewFrame = false;
175 
176  if (m_sync)
177  {
178  // wait for the sync object, use a timeout so that the thread can be shutdown if the
179  // sync is never signaled.
180  const EGLint ret = eglClientWaitSyncKHR(display, m_sync, 0 /*flags*/,
181  TimeValue::fromMSec(1).toNSec());
182  if (ret == EGL_CONDITION_SATISFIED_KHR)
183  {
184  // reset the sync object to unsignaled
185  if (!eglSignalSyncKHR(display, m_sync, EGL_UNSIGNALED_KHR))
186  ORIGINATE_ERROR("eglSignalSyncKHR failed (error 0x%04x)", eglGetError());
187 
188  hasNewFrame = true;
189  }
190  else if (ret != EGL_TIMEOUT_EXPIRED_KHR)
191  {
192  ORIGINATE_ERROR("eglClientWaitSyncKHR failed with 0x%04x (error 0x%04x)", ret,
193  eglGetError());
194  }
195  }
196  else
197  {
198  if (m_streamState == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR)
199  {
200  hasNewFrame = true;
201  }
202  else
203  {
204  // no new frame, sleep for a while
205  usleep(1000);
206  }
207  }
208 
209  if (hasNewFrame)
210  {
211  bool acquired = false;
212 
213  {
214  // hold the mutex while acquiring to avoid a race with the composer thread
215  ScopedMutex sm(m_mutex);
216  PROPAGATE_ERROR(sm.expectLocked());
217 
218  acquired = eglStreamConsumerAcquireKHR(display, m_eglStream);
219  }
220 
221  if (acquired)
222  {
223  // request a recompose from the renderer with the new image
224  PROPAGATE_ERROR(renderer.reCompose());
225  }
226  else
227  {
228  // if the stream had been disconnected request a thread shutdown
229  if (eglGetError() == EGL_BAD_STATE_KHR)
230  PROPAGATE_ERROR(requestShutdown());
231  else
232  ORIGINATE_ERROR("Failed to aquire from egl stream");
233  }
234  }
235 #else // WAR_EGL_STREAM_RACE
236  if (eglStreamConsumerAcquireKHR(display, m_eglStream))
237  {
238  // request a recompose from the renderer with the new image
239  PROPAGATE_ERROR(renderer.reCompose());
240  }
241  else
242  {
243  // if the stream had been disconnected request a thread shutdown
244  if (eglGetError() == EGL_BAD_STATE_KHR)
245  PROPAGATE_ERROR(requestShutdown());
246  else
247  ORIGINATE_ERROR("Failed to aquire from egl stream");
248  }
249 #endif // WAR_EGL_STREAM_RACE
250  }
251 
252  return true;
253 }
254 
256 {
257 #if defined(WAR_EGL_STREAM_RACE) && defined(EGL_NV_stream_sync)
258  if (m_sync)
259  {
260  if (!eglDestroySyncKHR(Renderer::getInstance().getEGLDisplay(), m_sync))
261  REPORT_ERROR("eglDestroySyncKHR failed (error 0x%04x)", eglGetError());
262  m_sync = 0;
263  }
264 #endif // WAR_EGL_STREAM_RACE && EGL_NV_stream_sync
265 
266  glDeleteTextures(1, &m_streamTexture);
267  m_streamTexture = 0;
268 
269  PROPAGATE_ERROR(m_context.cleanup());
270 
271  return true;
272 }
273 
274 }; // namespace ArgusSamples