JeVois  1.20
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
MovieOutput.C
Go to the documentation of this file.
1 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2016 by Laurent Itti, the University of Southern
4 // California (USC), and iLab at USC. See http://iLab.usc.edu and http://jevois.org for information about this project.
5 //
6 // This file is part of the JeVois Smart Embedded Machine Vision Toolkit. This program is free software; you can
7 // redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
8 // Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
10 // License for more details. You should have received a copy of the GNU General Public License along with this program;
11 // if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12 //
13 // Contact information: Laurent Itti - 3641 Watt Way, HNB-07A - Los Angeles, CA 90089-2520 - USA.
14 // Tel: +1 213 740 3527 - itti@pollux.usc.edu - http://iLab.usc.edu - http://jevois.org
15 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16 /*! \file */
17 
19 #include <jevois/Debug/Log.H>
20 #include <jevois/Util/Async.H>
21 
22 #include <opencv2/imgproc/imgproc.hpp>
23 
24 #include <linux/videodev2.h> // for v4l2 pixel types
25 #include <cstdlib> // for std::system()
26 #include <cstdio> // for snprintf()
27 #include <fstream>
28 
29 static char const PATHPREFIX[] = JEVOIS_ROOT_PATH "/data/movieout/";
30 
31 // ####################################################################################################
32 jevois::MovieOutput::MovieOutput(std::string const & fn) :
33  itsBuf(1000), itsSaving(false), itsFileNum(0), itsRunning(true), itsFilebase(fn)
34 {
36 }
37 
38 // ####################################################################################################
40 {
41  // Signal end of run:
42  itsRunning.store(false);
43 
44  // Push an empty frame into our buffer to signal the end of video to our thread:
45  itsBuf.push(cv::Mat());
46 
47  // Wait for the thread to complete:
48  LINFO("Waiting for writer thread to complete, " << itsBuf.filled_size() << " frames to go...");
49  try { itsRunFut.get(); } catch (...) { jevois::warnAndIgnoreException(); }
50  LINFO("Writer thread completed. Syncing disk...");
51  if (std::system("/bin/sync")) LERROR("Error syncing disk -- IGNORED");
52  LINFO("Video " << itsFilename << " saved.");
53 }
54 
55 // ##############################################################################################################
56 void jevois::MovieOutput::setFormat(VideoMapping const & m)
57 {
58  // Store the mapping so we can check frame size and format when giving out our buffer:
59  itsMapping = m;
60 }
61 
62 // ##############################################################################################################
64 {
65  if (itsSaving.load())
66  {
67  // Reset our VideoBuf using the current format:
68  itsBuffer.reset(new jevois::VideoBuf(-1, itsMapping.osize(), 0, -1));
69 
70  img.width = itsMapping.ow;
71  img.height = itsMapping.oh;
72  img.fmt = itsMapping.ofmt;
73  img.fps = itsMapping.ofps;
74  img.buf = itsBuffer;
75  img.bufindex = 0;
76  }
77  else LFATAL("Cannot get() while not streaming");
78 }
79 
80 // ##############################################################################################################
82 {
83  if (itsSaving.load())
84  {
85  // Our thread will do the actual encoding:
86  if (itsBuf.filled_size() > 1000) LERROR("Image queue too large, video writer cannot keep up - DROPPING FRAME");
87  else itsBuf.push(jevois::rawimage::convertToCvBGR(img));
88 
89  // Nuke our buf:
90  itsBuffer.reset();
91  }
92  else LFATAL("Aborting send() while not streaming");
93 }
94 
95 // ##############################################################################################################
97 {
98  itsSaving.store(true);
99 }
100 
101 // ##############################################################################################################
103 {
104  itsSaving.store(false);
105 }
106 
107 // ##############################################################################################################
109 {
110  itsSaving.store(false);
111 
112  // Push an empty frame into our buffer to signal the end of video to our thread:
113  itsBuf.push(cv::Mat());
114 
115  // Wait for the thread to empty our image buffer:
116  while (itsBuf.filled_size())
117  {
118  LINFO("Waiting for writer thread to complete, " << itsBuf.filled_size() << " frames to go...");
119  std::this_thread::sleep_for(std::chrono::milliseconds(200));
120  }
121  LINFO("Writer thread completed. Syncing disk...");
122  if (std::system("/bin/sync")) LERROR("Error syncing disk -- IGNORED");
123  LINFO("Video " << itsFilename << " saved.");
124 }
125 
126 // ##############################################################################################################
127 void jevois::MovieOutput::run() // Runs in a thread
128 {
129  while (itsRunning.load())
130  {
131  // Create a VideoWriter here, since it has no close() function, this will ensure it gets destroyed and closes
132  // the movie once we stop the recording:
133  cv::VideoWriter writer;
134  int frame = 0;
135 
136  while (true)
137  {
138  // Get next frame from the buffer:
139  cv::Mat im = itsBuf.pop();
140 
141  // An empty image will be pushed when we are ready to close the video file:
142  if (im.empty()) break;
143 
144  // Start the encoder if it is not yet running:
145  if (writer.isOpened() == false)
146  {
147  std::string const fcc = "MJPG";
148  //std::string const fcc = "MP4V";
149  int const cvfcc = cv::VideoWriter::fourcc(fcc[0], fcc[1], fcc[2], fcc[3]);
150 
151  // Add path prefix if given filename is relative:
152  std::string fn = itsFilebase;
153  if (fn.empty()) LFATAL("Cannot save to an empty filename");
154  if (fn[0] != '/') fn = PATHPREFIX + fn;
155 
156  // Create directory just in case it does not exist:
157  std::string const cmd = "/bin/mkdir -p " + fn.substr(0, fn.rfind('/'));
158  if (std::system(cmd.c_str())) LERROR("Error running [" << cmd << "] -- IGNORED");
159 
160  // Fill in the file number; be nice and do not overwrite existing files:
161  while (true)
162  {
163  char tmp[2048];
164  std::snprintf(tmp, 2047, fn.c_str(), itsFileNum);
165  std::ifstream ifs(tmp);
166  if (ifs.is_open() == false) { itsFilename = tmp; break; }
167  ++itsFileNum;
168  }
169 
170  // Open the writer:
171  if (writer.open(itsFilename, cvfcc, itsMapping.ofps, im.size(), true) == false)
172  LFATAL("Failed to open video encoder for file [" << itsFilename << ']');
173  }
174 
175  // Write the frame:
176  writer << im;
177 
178  // Report what is going on once in a while:
179  if ((++frame % 100) == 0) LINFO("Written " << frame << " video frames");
180  }
181 
182  // Our writer runs out of scope and closes the file here.
183  ++itsFileNum;
184  }
185 }
jevois::MovieOutput::streamOn
virtual void streamOn() override
Start streaming.
Definition: MovieOutput.C:96
jevois::MovieOutput::MovieOutput
MovieOutput(std::string const &fn)
Constructor.
Definition: MovieOutput.C:32
jevois::async
std::future< std::invoke_result_t< std::decay_t< Function >, std::decay_t< Args >... > > async(Function &&f, Args &&... args)
Async execution using a thread pool.
Async.H
jevois::MovieOutput::itsRunFut
std::future< void > itsRunFut
Future for our run() thread.
Definition: MovieOutput.H:69
jevois::MovieOutput::~MovieOutput
virtual ~MovieOutput()
Virtual destructor for safe inheritance.
Definition: MovieOutput.C:39
jevois::RawImage
A raw image as coming from a V4L2 Camera and/or being sent out to a USB Gadget.
Definition: RawImage.H:110
jevois::rawimage::convertToCvBGR
cv::Mat convertToCvBGR(RawImage const &src)
Convert RawImage to OpenCV doing color conversion from any RawImage source pixel to OpenCV BGR byte.
Definition: RawImageOps.C:282
jevois::RawImage::bufindex
size_t bufindex
The index of the data buffer in the kernel driver.
Definition: RawImage.H:150
jevois::RawImage::fps
float fps
Programmed frames/s as given by current video mapping, may not be actual.
Definition: RawImage.H:148
LERROR
#define LERROR(msg)
Convenience macro for users to print out console or syslog messages, ERROR level.
Definition: Log.H:211
jevois::MovieOutput::streamOff
virtual void streamOff() override
Stop streaming.
Definition: MovieOutput.C:108
jevois::RawImage::width
unsigned int width
Image width in pixels.
Definition: RawImage.H:145
MovieOutput.H
Log.H
jevois::system
std::string system(std::string const &cmd, bool errtoo=true)
Execute a command and grab stdout output to a string.
Definition: Utils.C:461
jevois::MovieOutput::run
void run()
Use a thread to encode and save frames.
Definition: MovieOutput.C:127
jevois::MovieOutput::abortStream
virtual void abortStream() override
Abort streaming.
Definition: MovieOutput.C:102
jevois::MovieOutput::get
virtual void get(RawImage &img) override
Get a pre-allocated image so that we can fill the pixel data and later send out using send()
Definition: MovieOutput.C:63
jevois::warnAndIgnoreException
std::string warnAndIgnoreException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
Definition: Log.C:236
LFATAL
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
jevois::RawImage::height
unsigned int height
Image height in pixels.
Definition: RawImage.H:146
jevois::RawImage::fmt
unsigned int fmt
Pixel format as a V4L2_PIX_FMT_XXX.
Definition: RawImage.H:147
jevois::VideoBuf
A V4L2 video buffer, to be held in a shared_ptr.
Definition: VideoBuf.H:29
jevois::RawImage::buf
std::shared_ptr< VideoBuf > buf
The pixel data buffer.
Definition: RawImage.H:149
jevois::MovieOutput::send
virtual void send(RawImage const &img) override
Send an image out.
Definition: MovieOutput.C:81
LINFO
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
Definition: Log.H:194
jevois::MovieOutput::setFormat
virtual void setFormat(VideoMapping const &m) override
Set the video format and frame rate.
Definition: MovieOutput.C:56