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_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 {
100 m_wasRunning = m_running;
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 {
118 m_prevRunning = m_running;
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
133 bool TaskVideoRecord::start()
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
175 bool TaskVideoRecord::stop()
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
221 bool TaskVideoRecord::startRecording()
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
233 m_videoPipeline = new VideoPipeline;
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
275 bool TaskVideoRecord::stopRecording()
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
319 bool TaskVideoRecord::toggleRecording()
320 {
321 if (m_recording)
322 PROPAGATE_ERROR(stopRecording());
323 else
324 PROPAGATE_ERROR(startRecording());
325 return true;
326 }
327
328 bool TaskVideoRecord::shutdown()
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