JeVois  1.0
JeVois Smart Embedded Machine Vision Toolkit
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 
21 #include <opencv2/imgproc/imgproc.hpp>
22 
23 #include <linux/videodev2.h> // for v4l2 pixel types
24 #include <cstdlib> // for std::system()
25 #include <cstdio> // for snprintf()
26 #include <fstream>
27 
28 static char const PATHPREFIX[] = "/jevois/data/movieout/";
29 
30 // ####################################################################################################
31 jevois::MovieOutput::MovieOutput(std::string const & fn) :
32  itsBuf(1000), itsSaving(false), itsFileNum(0), itsRunning(true), itsFilebase(fn)
33 {
34  itsRunFut = std::async(std::launch::async, &jevois::MovieOutput::run, this);
35 }
36 
37 // ####################################################################################################
39 {
40  // Signal end of run:
41  itsRunning.store(false);
42 
43  // Push an empty frame into our buffer to signal the end of video to our thread:
44  itsBuf.push(cv::Mat());
45 
46  // Wait for the thread to complete:
47  LINFO("Waiting for writer thread to complete, " << itsBuf.filled_size() << " frames to go...");
48  try { itsRunFut.get(); } catch (...) { jevois::warnAndIgnoreException(); }
49  LINFO("Writer thread completed. Syncing disk...");
50  if (std::system("/bin/sync")) LERROR("Error syncing disk -- IGNORED");
51  LINFO("Video " << itsFilename << " saved.");
52 }
53 
54 // ##############################################################################################################
56 {
57  // Store the mapping so we can check frame size and format when giving out our buffer:
58  itsMapping = m;
59 }
60 
61 // ##############################################################################################################
63 {
64  if (itsSaving.load())
65  {
66  // Reset our VideoBuf using the current format:
67  itsBuffer.reset(new jevois::VideoBuf(-1, itsMapping.osize(), 0));
68 
69  img.width = itsMapping.ow;
70  img.height = itsMapping.oh;
71  img.fmt = itsMapping.ofmt;
72  img.buf = itsBuffer;
73  img.bufindex = 0;
74  }
75  else LFATAL("Cannot get() while not streaming");
76 }
77 
78 // ##############################################################################################################
80 {
81  if (itsSaving.load())
82  {
83  // Our thread will do the actual encoding:
84  if (itsBuf.filled_size() > 1000) LERROR("Image queue too large, video writer cannot keep up - DROPPING FRAME");
85  else itsBuf.push(jevois::rawimage::convertToCvBGR(img));
86 
87  // Nuke our buf:
88  itsBuffer.reset();
89  }
90  else LFATAL("Aborting send() while not streaming");
91 }
92 
93 // ##############################################################################################################
95 {
96  itsSaving.store(true);
97 }
98 
99 // ##############################################################################################################
101 {
102  itsSaving.store(false);
103 }
104 
105 // ##############################################################################################################
107 {
108  itsSaving.store(false);
109 
110  // Push an empty frame into our buffer to signal the end of video to our thread:
111  itsBuf.push(cv::Mat());
112 
113  // Wait for the thread to empty our image buffer:
114  while (itsBuf.filled_size())
115  {
116  LINFO("Waiting for writer thread to complete, " << itsBuf.filled_size() << " frames to go...");
117  std::this_thread::sleep_for(std::chrono::milliseconds(200));
118  }
119  LINFO("Writer thread completed. Syncing disk...");
120  if (std::system("/bin/sync")) LERROR("Error syncing disk -- IGNORED");
121  LINFO("Video " << itsFilename << " saved.");
122 }
123 
124 // ##############################################################################################################
125 void jevois::MovieOutput::run() // Runs in a thread
126 {
127  while (itsRunning.load())
128  {
129  // Create a VideoWriter here, since it has no close() function, this will ensure it gets destroyed and closes
130  // the movie once we stop the recording:
131  cv::VideoWriter writer;
132  int frame = 0;
133 
134  while (true)
135  {
136  // Get next frame from the buffer:
137  cv::Mat im = itsBuf.pop();
138 
139  // An empty image will be pushed when we are ready to close the video file:
140  if (im.empty()) break;
141 
142  // Start the encoder if it is not yet running:
143  if (writer.isOpened() == false)
144  {
145  std::string const fcc = "MJPG";
146  //std::string const fcc = "MP4V";
147  int const cvfcc = cv::VideoWriter::fourcc(fcc[0], fcc[1], fcc[2], fcc[3]);
148 
149  // Add path prefix if given filename is relative:
150  std::string fn = itsFilebase;
151  if (fn.empty()) LFATAL("Cannot save to an empty filename");
152  if (fn[0] != '/') fn = PATHPREFIX + fn;
153 
154  // Create directory just in case it does not exist:
155  std::string const cmd = "/bin/mkdir -p " + fn.substr(0, fn.rfind('/'));
156  if (std::system(cmd.c_str())) LERROR("Error running [" << cmd << "] -- IGNORED");
157 
158  // Fill in the file number; be nice and do not overwrite existing files:
159  while (true)
160  {
161  char tmp[2048];
162  std::snprintf(tmp, 2047, fn.c_str(), itsFileNum);
163  std::ifstream ifs(tmp);
164  if (ifs.is_open() == false) { itsFilename = tmp; break; }
165  ++itsFileNum;
166  }
167 
168  // Open the writer:
169  if (writer.open(itsFilename, cvfcc, itsMapping.ofps, im.size(), true) == false)
170  LFATAL("Failed to open video encoder for file [" << itsFilename << ']');
171  }
172 
173  // Write the frame:
174  writer << im;
175 
176  // Report what is going on once in a while:
177  if ((++frame % 100) == 0) LINFO("Written " << frame << " video frames");
178  }
179 
180  // Our writer runs out of scope and closes the file here.
181  ++itsFileNum;
182  }
183 }
std::string warnAndIgnoreException()
Convenience function to catch an exception, issue some LERROR (depending on type), and ignore it.
Definition: Log.C:200
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:226
unsigned int height
Image height in pixels.
Definition: RawImage.H:140
A V4L2 video buffer, to be held in a shared_ptr.
Definition: VideoBuf.H:29
unsigned int fmt
Pixel format as a V4L2_PIX_FMT_XXX.
Definition: RawImage.H:141
virtual void streamOff() override
Stop streaming.
Definition: MovieOutput.C:106
std::shared_ptr< VideoBuf > buf
The pixel data buffer.
Definition: RawImage.H:142
virtual void setFormat(VideoMapping const &m) override
Set the video format and frame rate.
Definition: MovieOutput.C:55
std::future< void > itsRunFut
Future for our run() thread.
Definition: MovieOutput.H:69
#define LERROR(msg)
Convenience macro for users to print out console or syslog messages, ERROR level. ...
Definition: Log.H:186
A raw image as coming from a V4L2 Camera and/or being sent out to a USB Gadget.
Definition: RawImage.H:110
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:62
Simple struct to hold video mapping definitions for the processing Engine.
Definition: VideoMapping.H:41
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level. ...
Definition: Log.H:205
virtual ~MovieOutput()
Virtual destructor for safe inheritance.
Definition: MovieOutput.C:38
virtual void streamOn() override
Start streaming.
Definition: MovieOutput.C:94
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
Definition: Log.H:169
size_t bufindex
The index of the data buffer in the kernel driver.
Definition: RawImage.H:143
virtual void abortStream() override
Abort streaming.
Definition: MovieOutput.C:100
MovieOutput(std::string const &fn)
Constructor.
Definition: MovieOutput.C:31
unsigned int width
Image width in pixels.
Definition: RawImage.H:139
virtual void send(RawImage const &img) override
Send an image out.
Definition: MovieOutput.C:79
void run()
Use a thread to encode and save frames.
Definition: MovieOutput.C:125