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_recording(false)
48 , m_captureIndex(0)
49 , m_videoPipeline(NULL)
50 {
51 }
52
53 TaskVideoRecord::~TaskVideoRecord()
54 {
55 shutdown();
56 }
57
58 bool TaskVideoRecord::initialize()
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 {
99 m_wasRunning = m_running;
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
111 bool TaskVideoRecord::start()
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
153 bool TaskVideoRecord::stop()
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
199 bool TaskVideoRecord::startRecording()
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
211 m_videoPipeline = new VideoPipeline;
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
253 bool TaskVideoRecord::stopRecording()
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
297 bool TaskVideoRecord::toggleRecording()
298 {
299 if (m_recording)
300 PROPAGATE_ERROR(stopRecording());
301 else
302 PROPAGATE_ERROR(startRecording());
303 return true;
304 }
305
306 bool TaskVideoRecord::shutdown()
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