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-2019, 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  // Variable frame rate video encoding currently not supported, using constant frame rate.
255  // Default frame rate will always be the max possible value supported for sensor mode,
256  // hence use minimum of (default, user_provided) framerate.
257  float frameRate = std::min(dispatcher.m_frameRateRange.get().max(),
258  dispatcher.m_frameRate.get());
259 
260  PROPAGATE_ERROR(m_videoPipeline->setupForRecording(
261  Argus::interface_cast<Argus::IEGLOutputStream>(m_videoStream)->getEGLStream(),
262  outputSize.width(), outputSize.height(),
263  frameRate, fileName.str().c_str(),
264  dispatcher.m_videoFormat.get(), dispatcher.m_videoFileType.get(),
265  dispatcher.m_videoBitRate.get(), dispatcher.m_videoControlRate.get(),
266  dispatcher.m_videoTwoPassCBREnable.get()));
267 
268  // start recording
269  PROPAGATE_ERROR(m_videoPipeline->start());
270 
271  // Enable the video output stream
272  PROPAGATE_ERROR(dispatcher.enableOutputStream(m_request.get(), m_videoStream.get()));
273 
274  // restart the repeating request to ensure the changed request is executed
275  PROPAGATE_ERROR(dispatcher.startRepeat(m_request.get()));
276 
277  PROPAGATE_ERROR(dispatcher.message("Started recording video at %dx%d, saving to '%s'\n",
278  outputSize.width(), outputSize.height(), fileName.str().c_str()));
279 
280  m_recording = true;
281 
282  return true;
283 }
284 
286 {
287  if (!m_initialized)
288  ORIGINATE_ERROR("Not initialized");
289 
290  if (!m_recording)
291  ORIGINATE_ERROR("Recording had not been started, can't stop");
292 
293  Dispatcher &dispatcher = Dispatcher::getInstance();
294 
295  // stop the repeating request
296  PROPAGATE_ERROR(dispatcher.stopRepeat());
297 
298  // stop recording
299  PROPAGATE_ERROR(m_videoPipeline->stop());
300 
301  // Wait until all pending captures are done before destroying the stream
302  PROPAGATE_ERROR(dispatcher.waitForIdle());
303 
304  if (m_videoStream)
305  {
306  // disable the output stream
307  PROPAGATE_ERROR(dispatcher.disableOutputStream(m_request.get(), m_videoStream.get()));
308  m_videoStream.reset();
309  }
310 
311  // start the repeating request again to get the preview working
312  PROPAGATE_ERROR(dispatcher.startRepeat(m_request.get()));
313 
314  if (m_videoPipeline)
315  {
316  // destroy the video pipeline
317  PROPAGATE_ERROR(m_videoPipeline->destroy());
318  delete m_videoPipeline;
319  m_videoPipeline = NULL;
320  }
321 
322  PROPAGATE_ERROR(dispatcher.message("Stopped recording video\n"));
323 
324  m_recording = false;
325 
326  return true;
327 }
328 
330 {
331  if (m_recording)
332  PROPAGATE_ERROR(stopRecording());
333  else
334  PROPAGATE_ERROR(startRecording());
335  return true;
336 }
337 
339 {
340  if (!m_initialized)
341  return true;
342 
343  PROPAGATE_ERROR_CONTINUE(stop());
344 
345  PROPAGATE_ERROR_CONTINUE(m_perfTracker->shutdown());
346  m_perfTracker.reset();
347 
348  Dispatcher &dispatcher = Dispatcher::getInstance();
349 
350  PROPAGATE_ERROR_CONTINUE(dispatcher.m_outputSize.unregisterObserver(this,
351  static_cast<IObserver::CallbackFunction>(&TaskVideoRecord::restartStreams)));
352  PROPAGATE_ERROR_CONTINUE(dispatcher.m_sensorModeValid.unregisterObserver(this,
353  static_cast<IObserver::CallbackFunction>(&TaskVideoRecord::onSensorModeValidChanged)));
354  PROPAGATE_ERROR_CONTINUE(dispatcher.m_deviceOpen.unregisterObserver(this,
355  static_cast<IObserver::CallbackFunction>(&TaskVideoRecord::onDeviceOpenChanged)));
356  PROPAGATE_ERROR_CONTINUE(dispatcher.m_captureYuvFormat.unregisterObserver(this,
357  static_cast<IObserver::CallbackFunction>(&TaskVideoRecord::restartStreams)));
358 
359  m_initialized = false;
360 
361  return true;
362 }
363 
364 }; // namespace ArgusSamples