Argus Camera Sample Argus Camera Sample

- Main Page
- Related Pages
- Namespaces
- Classes
- Files

- File List
- File Members

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
43 TaskVideoRecord::TaskVideoRecord()
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
54 TaskVideoRecord::~TaskVideoRecord()
55 {
56 shutdown();
57 }
58
59 bool TaskVideoRecord::initialize()
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 {
102 m_wasRunning = m_running;
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 {
120 m_prevRunning = m_running;
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
135 bool TaskVideoRecord::start()
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
178 bool TaskVideoRecord::stop()
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
225 bool TaskVideoRecord::startRecording()
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
237 m_videoPipeline = new VideoPipeline;
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
277 bool TaskVideoRecord::stopRecording()
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
321 bool TaskVideoRecord::toggleRecording()
322 {
323 if (m_recording)
324 PROPAGATE_ERROR(stopRecording());
325 else
326 PROPAGATE_ERROR(startRecording());
327 return true;
328 }
329
330 bool TaskVideoRecord::shutdown()
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