Argus Camera Sample
Argus Camera Sample
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
VideoRecord.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 
30 #include <sstream>
31 #include <iomanip>
32 
33 #include "VideoRecord.h"
34 #include "Composer.h"
35 #include "Dispatcher.h"
36 #include "Error.h"
37 #include "EventThread.h"
38 #include "PerfTracker.h"
39 
40 namespace ArgusSamples
41 {
42 
44  : m_initialized(false)
45  , m_running(false)
46  , m_wasRunning(false)
47  , m_prevRunning(false)
48  , m_recording(false)
49  , m_captureIndex(0)
50  , m_videoPipeline(NULL)
51 {
52 }
53 
55 {
56  shutdown();
57 }
58 
60 {
61  if (m_initialized)
62  return true;
63 
64  Dispatcher &dispatcher = Dispatcher::getInstance();
65 
66  PROPAGATE_ERROR(dispatcher.m_deviceOpen.registerObserver(this,
67  static_cast<IObserver::CallbackFunction>(&TaskVideoRecord::onDeviceOpenChanged)));
68  PROPAGATE_ERROR(dispatcher.m_sensorModeValid.registerObserver(this,
69  static_cast<IObserver::CallbackFunction>(&TaskVideoRecord::onSensorModeValidChanged)));
70  PROPAGATE_ERROR(dispatcher.m_outputSize.registerObserver(this,
71  static_cast<IObserver::CallbackFunction>(&TaskVideoRecord::restartStreams)));
72  PROPAGATE_ERROR(dispatcher.m_captureYuvFormat.registerObserver(this,
73  static_cast<IObserver::CallbackFunction>(&TaskVideoRecord::restartStreams)));
74 
75  m_perfTracker.reset(new SessionPerfTracker());
76  if (!m_perfTracker)
77  ORIGINATE_ERROR("Out of memory");
78 
79  m_initialized = true;
80 
81  return true;
82 }
83 
84 bool TaskVideoRecord::restartStreams(__attribute__((unused)) const Observed &source)
85 {
86  if (m_running)
87  {
88  PROPAGATE_ERROR(stop());
89  PROPAGATE_ERROR(start());
90  }
91  return true;
92 }
93 
94 bool TaskVideoRecord::onDeviceOpenChanged(const Observed &source)
95 {
96  const bool isOpen = static_cast<const Value<bool>&>(source).get();
97 
98  // If the current device is closed the request needs to be recreated on the new device. Stop
99  // and then start when the device is opened again.
100  if (!isOpen)
101  {
103  PROPAGATE_ERROR(stop());
104  }
105  else if (m_wasRunning)
106  {
107  m_wasRunning = false;
108  PROPAGATE_ERROR(start());
109  }
110 
111  return true;
112 }
113 
114 bool TaskVideoRecord::onSensorModeValidChanged(const Observed &source)
115 {
116  const bool isTrue = static_cast<const Value<bool>&>(source).get();
117 
118  if (!isTrue)
119  {
121  if (m_running)
122  {
123  PROPAGATE_ERROR(stop());
124  }
125  }
126  else if (m_prevRunning)
127  {
128  m_prevRunning = false;
129  PROPAGATE_ERROR(start());
130  }
131 
132  return true;
133 }
134 
136 {
137  if (!m_initialized)
138  ORIGINATE_ERROR("Not initialized");
139 
140  if (m_running)
141  return true;
142 
143  PROPAGATE_ERROR(m_perfTracker->onEvent(SESSION_EVENT_TASK_START));
144 
145  Dispatcher &dispatcher = Dispatcher::getInstance();
146  Composer &composer = Composer::getInstance();
147 
148  PROPAGATE_ERROR(dispatcher.createRequest(m_request, Argus::CAPTURE_INTENT_VIDEO_RECORD));
149 
150  // Create the preview stream
151  PROPAGATE_ERROR(dispatcher.createOutputStream(m_request.get(), false, m_previewStream));
152 
153  // bind the preview stream to the composer
154  Argus::IEGLOutputStream *iEGLOutputStream =
155  Argus::interface_cast<Argus::IEGLOutputStream>(m_previewStream.get());
156  if (!iEGLOutputStream)
157  ORIGINATE_ERROR("Failed to get IEGLOutputStream interface");
158 
159  PROPAGATE_ERROR(composer.bindStream(iEGLOutputStream->getEGLStream()));
160 
161  // Enable the preview stream
162  PROPAGATE_ERROR(dispatcher.enableOutputStream(m_request.get(), m_previewStream.get()));
163 
164  const Argus::Size2D<uint32_t> streamSize = iEGLOutputStream->getResolution();
165  PROPAGATE_ERROR(composer.setStreamAspectRatio(iEGLOutputStream->getEGLStream(),
166  (float)streamSize.width() / (float)streamSize.height()));
167  PROPAGATE_ERROR(composer.setStreamActive(iEGLOutputStream->getEGLStream(), true));
168 
169  // start the preview
170  PROPAGATE_ERROR(m_perfTracker->onEvent(SESSION_EVENT_ISSUE_CAPTURE));
171  PROPAGATE_ERROR(dispatcher.startRepeat(m_request.get()));
172 
173  m_running = true;
174 
175  return true;
176 }
177 
179 {
180  if (!m_initialized)
181  ORIGINATE_ERROR("Not initialized");
182  if (!m_running)
183  return true;
184 
185  PROPAGATE_ERROR(m_perfTracker->onEvent(SESSION_EVENT_CLOSE_REQUESTED));
186 
187  if (m_recording)
188  PROPAGATE_ERROR(stopRecording());
189 
190  Dispatcher &dispatcher = Dispatcher::getInstance();
191 
192  // stop the repeating request
193  PROPAGATE_ERROR(dispatcher.stopRepeat());
194 
195  PROPAGATE_ERROR(dispatcher.waitForIdle());
196  PROPAGATE_ERROR(m_perfTracker->onEvent(SESSION_EVENT_FLUSH_DONE));
197 
198  if (m_previewStream)
199  {
200  if (m_request)
201  PROPAGATE_ERROR(dispatcher.disableOutputStream(m_request.get(), m_previewStream.get()));
202 
203  Argus::IEGLOutputStream *iEGLOutputStream =
204  Argus::interface_cast<Argus::IEGLOutputStream>(m_previewStream);
205  if (!iEGLOutputStream)
206  ORIGINATE_ERROR("Failed to get IEGLOutputStream interface");
207 
208  // disconnect the EGL stream
209  iEGLOutputStream->disconnect();
210 
211  // unbind the preview stream from the composer
212  PROPAGATE_ERROR(Composer::getInstance().unbindStream(iEGLOutputStream->getEGLStream()));
213 
214  m_previewStream.reset();
215  }
216  PROPAGATE_ERROR_CONTINUE(m_request.reset());
217 
218  PROPAGATE_ERROR(m_perfTracker->onEvent(SESSION_EVENT_CLOSE_DONE));
219 
220  m_running = false;
221 
222  return true;
223 }
224 
226 {
227  if (!m_initialized)
228  ORIGINATE_ERROR("Not initialized");
229  if (!m_running)
230  ORIGINATE_ERROR("Not running");
231  if (m_recording)
232  ORIGINATE_ERROR("Recording had already been started, can't start again");
233 
234  Dispatcher &dispatcher = Dispatcher::getInstance();
235 
236  // setup the video pipeline with the video stream
238  if (!m_videoPipeline)
239  ORIGINATE_ERROR("Out of memory");
240 
241  // Create the video output stream
242  PROPAGATE_ERROR(dispatcher.createOutputStream(m_request.get(), false, m_videoStream));
243 
244  Argus::Size2D<uint32_t> outputSize;
245  PROPAGATE_ERROR(dispatcher.getOutputSize(&outputSize));
246 
247  // build the file name
248  std::ostringstream fileName;
249  fileName << dispatcher.m_outputPath.get();
250  if (dispatcher.m_outputPath.get() != "/dev/null")
251  fileName << "/video" << std::setfill('0') << std::setw(4) << m_captureIndex;
252  ++m_captureIndex;
253 
254  PROPAGATE_ERROR(m_videoPipeline->setupForRecording(
255  Argus::interface_cast<Argus::IEGLOutputStream>(m_videoStream)->getEGLStream(),
256  outputSize.width(), outputSize.height(),
257  dispatcher.m_frameRate.get(), fileName.str().c_str(), dispatcher.m_videoFormat.get(),
258  dispatcher.m_videoFileType.get(), dispatcher.m_videoBitRate.get()));
259 
260  // start recording
261  PROPAGATE_ERROR(m_videoPipeline->start());
262 
263  // Enable the video output stream
264  PROPAGATE_ERROR(dispatcher.enableOutputStream(m_request.get(), m_videoStream.get()));
265 
266  // restart the repeating request to ensure the changed request is executed
267  PROPAGATE_ERROR(dispatcher.startRepeat(m_request.get()));
268 
269  PROPAGATE_ERROR(dispatcher.message("Started recording video at %dx%d, saving to '%s'\n",
270  outputSize.width(), outputSize.height(), fileName.str().c_str()));
271 
272  m_recording = true;
273 
274  return true;
275 }
276 
278 {
279  if (!m_initialized)
280  ORIGINATE_ERROR("Not initialized");
281 
282  if (!m_recording)
283  ORIGINATE_ERROR("Recording had not been started, can't stop");
284 
285  Dispatcher &dispatcher = Dispatcher::getInstance();
286 
287  // stop the repeating request
288  PROPAGATE_ERROR(dispatcher.stopRepeat());
289 
290  // stop recording
291  PROPAGATE_ERROR(m_videoPipeline->stop());
292 
293  // Wait until all pending captures are done before destroying the stream
294  PROPAGATE_ERROR(dispatcher.waitForIdle());
295 
296  if (m_videoStream)
297  {
298  // disable the output stream
299  PROPAGATE_ERROR(dispatcher.disableOutputStream(m_request.get(), m_videoStream.get()));
300  m_videoStream.reset();
301  }
302 
303  // start the repeating request again to get the preview working
304  PROPAGATE_ERROR(dispatcher.startRepeat(m_request.get()));
305 
306  if (m_videoPipeline)
307  {
308  // destroy the video pipeline
309  PROPAGATE_ERROR(m_videoPipeline->destroy());
310  delete m_videoPipeline;
311  m_videoPipeline = NULL;
312  }
313 
314  PROPAGATE_ERROR(dispatcher.message("Stopped recording video\n"));
315 
316  m_recording = false;
317 
318  return true;
319 }
320 
322 {
323  if (m_recording)
324  PROPAGATE_ERROR(stopRecording());
325  else
326  PROPAGATE_ERROR(startRecording());
327  return true;
328 }
329 
331 {
332  if (!m_initialized)
333  return true;
334 
335  PROPAGATE_ERROR_CONTINUE(stop());
336 
337  PROPAGATE_ERROR_CONTINUE(m_perfTracker->shutdown());
338  m_perfTracker.reset();
339 
340  Dispatcher &dispatcher = Dispatcher::getInstance();
341 
342  PROPAGATE_ERROR_CONTINUE(dispatcher.m_outputSize.unregisterObserver(this,
343  static_cast<IObserver::CallbackFunction>(&TaskVideoRecord::restartStreams)));
344  PROPAGATE_ERROR_CONTINUE(dispatcher.m_sensorModeValid.unregisterObserver(this,
345  static_cast<IObserver::CallbackFunction>(&TaskVideoRecord::onSensorModeValidChanged)));
346  PROPAGATE_ERROR_CONTINUE(dispatcher.m_deviceOpen.unregisterObserver(this,
347  static_cast<IObserver::CallbackFunction>(&TaskVideoRecord::onDeviceOpenChanged)));
348  PROPAGATE_ERROR_CONTINUE(dispatcher.m_captureYuvFormat.unregisterObserver(this,
349  static_cast<IObserver::CallbackFunction>(&TaskVideoRecord::restartStreams)));
350 
351  m_initialized = false;
352 
353  return true;
354 }
355 
356 }; // namespace ArgusSamples