JeVois  1.2
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.fps = itsMapping.ofps;
73  img.buf = itsBuffer;
74  img.bufindex = 0;
75  }
76  else LFATAL("Cannot get() while not streaming");
77 }
78 
79 // ##############################################################################################################
81 {
82  if (itsSaving.load())
83  {
84  // Our thread will do the actual encoding:
85  if (itsBuf.filled_size() > 1000) LERROR("Image queue too large, video writer cannot keep up - DROPPING FRAME");
86  else itsBuf.push(jevois::rawimage::convertToCvBGR(img));
87 
88  // Nuke our buf:
89  itsBuffer.reset();
90  }
91  else LFATAL("Aborting send() while not streaming");
92 }
93 
94 // ##############################################################################################################
96 {
97  itsSaving.store(true);
98 }
99 
100 // ##############################################################################################################
102 {
103  itsSaving.store(false);
104 }
105 
106 // ##############################################################################################################
108 {
109  itsSaving.store(false);
110 
111  // Push an empty frame into our buffer to signal the end of video to our thread:
112  itsBuf.push(cv::Mat());
113 
114  // Wait for the thread to empty our image buffer:
115  while (itsBuf.filled_size())
116  {
117  LINFO("Waiting for writer thread to complete, " << itsBuf.filled_size() << " frames to go...");
118  std::this_thread::sleep_for(std::chrono::milliseconds(200));
119  }
120  LINFO("Writer thread completed. Syncing disk...");
121  if (std::system("/bin/sync")) LERROR("Error syncing disk -- IGNORED");
122  LINFO("Video " << itsFilename << " saved.");
123 }
124 
125 // ##############################################################################################################
126 void jevois::MovieOutput::run() // Runs in a thread
127 {
128  while (itsRunning.load())
129  {
130  // Create a VideoWriter here, since it has no close() function, this will ensure it gets destroyed and closes
131  // the movie once we stop the recording:
132  cv::VideoWriter writer;
133  int frame = 0;
134 
135  while (true)
136  {
137  // Get next frame from the buffer:
138  cv::Mat im = itsBuf.pop();
139 
140  // An empty image will be pushed when we are ready to close the video file:
141  if (im.empty()) break;
142 
143  // Start the encoder if it is not yet running:
144  if (writer.isOpened() == false)
145  {
146  std::string const fcc = "MJPG";
147  //std::string const fcc = "MP4V";
148  int const cvfcc = cv::VideoWriter::fourcc(fcc[0], fcc[1], fcc[2], fcc[3]);
149 
150  // Add path prefix if given filename is relative:
151  std::string fn = itsFilebase;
152  if (fn.empty()) LFATAL("Cannot save to an empty filename");
153  if (fn[0] != '/') fn = PATHPREFIX + fn;
154 
155  // Create directory just in case it does not exist:
156  std::string const cmd = "/bin/mkdir -p " + fn.substr(0, fn.rfind('/'));
157  if (std::system(cmd.c_str())) LERROR("Error running [" << cmd << "] -- IGNORED");
158 
159  // Fill in the file number; be nice and do not overwrite existing files:
160  while (true)
161  {
162  char tmp[2048];
163  std::snprintf(tmp, 2047, fn.c_str(), itsFileNum);
164  std::ifstream ifs(tmp);
165  if (ifs.is_open() == false) { itsFilename = tmp; break; }
166  ++itsFileNum;
167  }
168 
169  // Open the writer:
170  if (writer.open(itsFilename, cvfcc, itsMapping.ofps, im.size(), true) == false)
171  LFATAL("Failed to open video encoder for file [" << itsFilename << ']');
172  }
173 
174  // Write the frame:
175  writer << im;
176 
177  // Report what is going on once in a while:
178  if ((++frame % 100) == 0) LINFO("Written " << frame << " video frames");
179  }
180 
181  // Our writer runs out of scope and closes the file here.
182  ++itsFileNum;
183  }
184 }
std::string warnAndIgnoreException()
Convenience function to catch an exception, issue some LERROR (depending on type), and ignore it.
Definition: Log.C:211
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:146
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:147
virtual void streamOff() override
Stop streaming.
Definition: MovieOutput.C:107
std::shared_ptr< VideoBuf > buf
The pixel data buffer.
Definition: RawImage.H:149
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:193
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:212
virtual ~MovieOutput()
Virtual destructor for safe inheritance.
Definition: MovieOutput.C:38
virtual void streamOn() override
Start streaming.
Definition: MovieOutput.C:95
float fps
Programmed frames/s as given by current video mapping, may not be actual.
Definition: RawImage.H:148
std::string system(std::string const &cmd)
Execute a command and grab stdout output to a string.
Definition: Utils.C:189
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
Definition: Log.H:176
size_t bufindex
The index of the data buffer in the kernel driver.
Definition: RawImage.H:150
virtual void abortStream() override
Abort streaming.
Definition: MovieOutput.C:101
MovieOutput(std::string const &fn)
Constructor.
Definition: MovieOutput.C:31
unsigned int width
Image width in pixels.
Definition: RawImage.H:145
virtual void send(RawImage const &img) override
Send an image out.
Definition: MovieOutput.C:80
void run()
Use a thread to encode and save frames.
Definition: MovieOutput.C:126