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