Compare commits

..

10 commits

6 changed files with 85 additions and 22 deletions

29
image.h
View file

@ -6,16 +6,39 @@
class Image class Image
{ {
private:
ssize_t bufferSize = -1;
public: public:
enum {
FORMAT_RGB,
FORMAT_JPEG,
FORMAT_YUYV
} typedef format_t;
std::shared_ptr<unsigned char> data = nullptr; std::shared_ptr<unsigned char> data = nullptr;
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;
uint8_t channels; uint8_t channels;
const uint32_t size() format_t format = FORMAT_RGB;
const size_t size() const
{ {
return width*height*channels; return bufferSize < 0 ? width*height*channels : bufferSize;
} }
void save(const char* filename) void setSize(size_t size)
{
bufferSize = size;
}
void setBuffer(std::shared_ptr<unsigned char> buffer, size_t size, format_t inFormat = FORMAT_RGB)
{
data = buffer;
bufferSize = size;
format = inFormat;
}
void save(const char* filename) const
{ {
std::ofstream outfile(filename); std::ofstream outfile(filename);
outfile.write((char *) data.get(), size()); outfile.write((char *) data.get(), size());

View file

@ -13,7 +13,8 @@ Image decompressJpegImage(const unsigned char* buffer, size_t size)
jpeg_create_decompress( &info ); //fills info structure jpeg_create_decompress( &info ); //fills info structure
jpeg_mem_src(&info, buffer, size); jpeg_mem_src(&info, buffer, size);
jpeg_read_header( &info, true ); if(jpeg_read_header(&info, true) != JPEG_HEADER_OK)
std::cerr<<"Could not read jpeg header";
jpeg_start_decompress( &info ); jpeg_start_decompress( &info );
@ -24,7 +25,7 @@ Image decompressJpegImage(const unsigned char* buffer, size_t size)
image.channels = info.num_components; // 3 = RGB, 4 = RGBA image.channels = info.num_components; // 3 = RGB, 4 = RGBA
// read RGB(A) scanlines one at a time into jdata[] // read RGB(A) scanlines one at a time into jdata[]
image.data = std::shared_ptr<unsigned char>(new unsigned char[image.size()]); image.data = std::shared_ptr<unsigned char>(new unsigned char[image.size()], std::default_delete<unsigned char[]>());
unsigned char* rowptr; unsigned char* rowptr;
while(info.output_scanline < image.height) while(info.output_scanline < image.height)
{ {
@ -57,7 +58,7 @@ Image decompressJpegImage(FILE *file)
image.channels = info.num_components; // 3 = RGB, 4 = RGBA image.channels = info.num_components; // 3 = RGB, 4 = RGBA
// read RGB(A) scanlines one at a time into jdata[] // read RGB(A) scanlines one at a time into jdata[]
image.data = std::shared_ptr<unsigned char>(new unsigned char[image.size()]); image.data = std::shared_ptr<unsigned char>(new unsigned char[image.size()], std::default_delete<unsigned char[]>());
unsigned char* rowptr; unsigned char* rowptr;
while ( info.output_scanline < image.height ) while ( info.output_scanline < image.height )
{ {

View file

@ -41,12 +41,16 @@ int main(int argc, char* argv[])
Config config; Config config;
argp_parse(&argp, argc, argv, 0, 0, &config); argp_parse(&argp, argc, argv, 0, 0, &config);
bool passthough = config.mjpeg && config.overlay.empty();
std::cout<<"UVOS webcam sender v0.1\n"; std::cout<<"UVOS webcam sender v0.1\n";
Webcam webcam(config.device, config.Xres, config.Yres); Webcam webcam(config.device, config.Xres, config.Yres, config.mjpeg, passthough);
std::stringstream ss; std::stringstream ss;
ss<<"nc "<<config.url<<' '<<config.port; ss<<"nc "<<config.url<<' '<<config.port<<'\n';
std::cout<<"using "<<ss.str();
FILE* netcat = popen(ss.str().c_str(), "w"); FILE* netcat = popen(ss.str().c_str(), "w");
@ -54,7 +58,10 @@ int main(int argc, char* argv[])
if(!config.overlay.empty()) if(!config.overlay.empty())
{ {
if(!overlay.open(config.overlay.c_str(), config.Xres, config.Yres, 3)) if(!overlay.open(config.overlay.c_str(), config.Xres, config.Yres, 3))
{
std::cout<<"Unable to open "<<config.overlay<<'\n';
return 1; return 1;
}
} }
while (!ferror(netcat)) while (!ferror(netcat))
@ -63,7 +70,10 @@ int main(int argc, char* argv[])
Image frame = webcam.frame(10); Image frame = webcam.frame(10);
if(!config.overlay.empty()) if(!config.overlay.empty())
overlayImage(frame, overlay); overlayImage(frame, overlay);
compressJpegImage(netcat, &frame); if(!passthough)
compressJpegImage(netcat, &frame);
else
fwrite(frame.data.get(), frame.size(), 1, netcat);
fflush(netcat); fflush(netcat);
} }

View file

@ -49,6 +49,7 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state)
break; break;
case 'y': case 'y':
config->Yres = std::stoi(arg); config->Yres = std::stoi(arg);
break;
case 'o': case 'o':
config->overlay = arg; config->overlay = arg;
break; break;

View file

@ -85,11 +85,12 @@ static void v4lconvert_yuyv_to_rgb24(const unsigned char *src,
} }
} }
Webcam::Webcam(const string& device, int width, int height, bool mjpeg) : Webcam::Webcam(const string& device, int width, int height, bool mjpeg, bool passthrough) :
device(device), device(device),
xres(width), xres(width),
yres(height), yres(height),
format(mjpeg ? V4L2_PIX_FMT_JPEG : V4L2_PIX_FMT_YUYV) passthrough_format(passthrough),
format(mjpeg ? V4L2_PIX_FMT_MJPEG : V4L2_PIX_FMT_YUYV)
{ {
open_device(); open_device();
init_device(); init_device();
@ -99,7 +100,7 @@ Webcam::Webcam(const string& device, int width, int height, bool mjpeg) :
rgb_frame.width = xres; rgb_frame.width = xres;
rgb_frame.height = yres; rgb_frame.height = yres;
rgb_frame.channels = 3; rgb_frame.channels = 3;
rgb_frame.data = std::shared_ptr<unsigned char>(new unsigned char[rgb_frame.size()]); rgb_frame.data = std::shared_ptr<unsigned char>(new unsigned char[rgb_frame.size()], std::default_delete<unsigned char[]>());
start_capturing(); start_capturing();
} }
@ -138,17 +139,42 @@ const Image& Webcam::frame(int timeout)
} }
int idx = read_frame(); int idx = read_frame();
if (idx != -1) { if (idx != -1) {
if (format == V4L2_PIX_FMT_YUYV) if(!passthrough_format)
{ {
v4lconvert_yuyv_to_rgb24((unsigned char *) buffers[idx].data, if (format == V4L2_PIX_FMT_YUYV)
rgb_frame.data.get(), {
xres, v4lconvert_yuyv_to_rgb24((unsigned char *) buffers[idx].data,
yres, rgb_frame.data.get(),
stride); xres,
yres,
stride);
}
else if(format == V4L2_PIX_FMT_MJPEG)
{
rgb_frame = decompressJpegImage((unsigned char *) buffers[idx].data, buffers[idx].size);
}
else
{
assert(false);
}
} }
else if(format == V4L2_PIX_FMT_JPEG) else
{ {
rgb_frame = decompressJpegImage((unsigned char *) buffers[idx].data, buffers[idx].size); if(format == V4L2_PIX_FMT_YUYV)
{
rgb_frame.setBuffer(std::shared_ptr<unsigned char>(new unsigned char[buffers[idx].size], std::default_delete<unsigned char[]>()),
buffers[idx].size, Image::FORMAT_YUYV);
}
else if(format == V4L2_PIX_FMT_MJPEG)
{
rgb_frame.setBuffer(std::shared_ptr<unsigned char>(new unsigned char[buffers[idx].size], std::default_delete<unsigned char[]>()),
buffers[idx].size, Image::FORMAT_RGB);
}
else
{
assert(false);
}
memcpy(rgb_frame.data.get(), buffers[idx].data, buffers[idx].size);
} }
return rgb_frame; return rgb_frame;
} }
@ -336,7 +362,8 @@ void Webcam::init_device(void)
{ {
// note that libv4l2 (look for 'v4l-utils') provides helpers // note that libv4l2 (look for 'v4l-utils') provides helpers
// to manage conversions // to manage conversions
throw runtime_error("Webcam does not support the requested format. Support for more formats need to be added!"); throw runtime_error("Webcam does not support the requested format. Support for more formats need to be added! video format is "
+ std::to_string(fmt.fmt.pix.pixelformat));
} }
/* Note VIDIOC_S_FMT may change width and height. */ /* Note VIDIOC_S_FMT may change width and height. */

View file

@ -18,7 +18,7 @@ class Webcam {
public: public:
Webcam(const std::string& device = "/dev/video0", Webcam(const std::string& device = "/dev/video0",
int width = 640, int width = 640,
int height = 480, bool mjpeg = false); int height = 480, bool mjpeg = false, bool passthrough = false);
~Webcam(); ~Webcam();
@ -60,6 +60,7 @@ private:
size_t stride; size_t stride;
bool force_format = true; bool force_format = true;
bool passthrough_format;
uint32_t format; uint32_t format;
}; };