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