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