Program Listing for File usb_cam.hpp

Return to documentation for file (include/usb_cam/usb_cam.hpp)

// Copyright 2014 Robert Bosch, LLC
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//    * Redistributions of source code must retain the above copyright
//      notice, this list of conditions and the following disclaimer.
//
//    * Redistributions in binary form must reproduce the above copyright
//      notice, this list of conditions and the following disclaimer in the
//      documentation and/or other materials provided with the distribution.
//
//    * Neither the name of the Robert Bosch, LLC nor the names of its
//      contributors may be used to endorse or promote products derived from
//      this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.


#ifndef USB_CAM__USB_CAM_HPP_
#define USB_CAM__USB_CAM_HPP_

extern "C" {
#include <libavcodec/avcodec.h>
#include <linux/videodev2.h>
}

#include <chrono>
#include <memory>
#include <algorithm>
#include <sstream>
#include <iostream>
#include <string>
#include <vector>

#include "usb_cam/utils.hpp"
#include "usb_cam/formats/pixel_format_base.hpp"
#include "usb_cam/formats/av_pixel_format_helper.hpp"

#include "usb_cam/formats/mjpeg.hpp"
#include "usb_cam/formats/mono.hpp"
#include "usb_cam/formats/rgb.hpp"
#include "usb_cam/formats/uyvy.hpp"
#include "usb_cam/formats/yuyv.hpp"
#include "usb_cam/formats/m420.hpp"


namespace usb_cam
{

using usb_cam::utils::io_method_t;
using usb_cam::formats::pixel_format_base;

using usb_cam::formats::RGB8;
using usb_cam::formats::YUYV;
using usb_cam::formats::YUYV2RGB;
using usb_cam::formats::UYVY;
using usb_cam::formats::UYVY2RGB;
using usb_cam::formats::MONO8;
using usb_cam::formats::MONO16;
using usb_cam::formats::Y102MONO8;
using usb_cam::formats::RAW_MJPEG;
using usb_cam::formats::MJPEG2RGB;
using usb_cam::formats::M4202RGB;


std::vector<std::shared_ptr<pixel_format_base>> driver_supported_formats(
  const formats::format_arguments_t & args = formats::format_arguments_t())
{
  std::vector<std::shared_ptr<pixel_format_base>> fmts = {
    std::make_shared<RGB8>(args),
    std::make_shared<YUYV>(args),
    std::make_shared<YUYV2RGB>(args),
    std::make_shared<UYVY>(args),
    std::make_shared<UYVY2RGB>(args),
    std::make_shared<MONO8>(args),
    std::make_shared<MONO16>(args),
    std::make_shared<Y102MONO8>(args),
    std::make_shared<RAW_MJPEG>(args),
    std::make_shared<MJPEG2RGB>(args),
    std::make_shared<M4202RGB>(args),
  };
  return fmts;
}

typedef struct capture_format_t
{
  struct v4l2_fmtdesc format;
  struct v4l2_frmivalenum v4l2_fmt;
} capture_format_t;

typedef struct parameters_t
{
  std::string camera_name;
  std::string device_name;
  std::string frame_id;
  std::string io_method_name;
  std::string camera_info_url;
  std::string pixel_format_name;
  std::string av_device_format;
  int image_width;
  int image_height;
  int framerate;
  int brightness;
  int contrast;
  int saturation;
  int sharpness;
  int gain;
  int white_balance;
  int exposure;
  int focus;
  bool auto_white_balance;
  bool autoexposure;
  bool autofocus;

  parameters_t()
// *INDENT-OFF*
    : camera_name("usb_cam"),
    device_name("/dev/video0"),
    frame_id("camera"),
    io_method_name("mmap"),
    camera_info_url("package://usb_cam/config/camera_info.yaml"),
    pixel_format_name("yuyv2rgb"),
    av_device_format("YUV422P"),
    image_width(600),
    image_height(480),
    framerate(30.0),
    brightness(-1),
    contrast(-1),
    saturation(-1),
    sharpness(-1),
    gain(-1),
    white_balance(-1),
    exposure(-1),
    focus(-1),
    auto_white_balance(true),
    autoexposure(true),
    autofocus(false)
  {
  }
// *INDENT-ON*
} parameters_t;

typedef struct image_t
{
  char * data;
  size_t width;
  size_t height;
  std::shared_ptr<pixel_format_base> pixel_format;
  size_t number_of_pixels;
  size_t bytes_per_line;
  size_t size_in_bytes;
  v4l2_format v4l2_fmt;
  struct timespec stamp;

  size_t set_number_of_pixels()
  {
    number_of_pixels = width * height;
    return number_of_pixels;
  }
  size_t set_bytes_per_line()
  {
    bytes_per_line = width * pixel_format->byte_depth() * pixel_format->channels();
    return bytes_per_line;
  }
  size_t set_size_in_bytes()
  {
    size_in_bytes = height * bytes_per_line;
    return size_in_bytes;
  }

  unsigned int get_format_fourcc()
  {
    return pixel_format->v4l2();
  }
} image_t;

class UsbCam
{
public:
  UsbCam();
  ~UsbCam();

  void configure(parameters_t & parameters, const io_method_t & io_method);

  void start();

  void shutdown(void);

  char * get_image();

  void get_image(char * destination);

  std::vector<capture_format_t> get_supported_formats();

  // enables/disable auto focus
  bool set_auto_focus(int value);

  // Set video device parameters
  bool set_v4l_parameter(const std::string & param, int value);
  bool set_v4l_parameter(const std::string & param, const std::string & value);

  void stop_capturing();
  void start_capturing();

  inline size_t get_image_width()
  {
    return m_image.width;
  }

  inline size_t get_image_height()
  {
    return m_image.height;
  }

  inline size_t get_image_size_in_bytes()
  {
    return m_image.size_in_bytes;
  }

  inline size_t get_image_size_in_pixels()
  {
    return m_image.number_of_pixels;
  }

  inline timespec get_image_timestamp()
  {
    return m_image.stamp;
  }

  inline unsigned int get_image_step()
  {
    return m_image.bytes_per_line;
  }

  inline std::string get_device_name()
  {
    return m_device_name;
  }

  inline std::shared_ptr<pixel_format_base> get_pixel_format()
  {
    return m_image.pixel_format;
  }

  inline usb_cam::utils::io_method_t get_io_method()
  {
    return m_io;
  }

  inline int get_fd()
  {
    return m_fd;
  }

  inline std::shared_ptr<usb_cam::utils::buffer[]> get_buffers()
  {
    return m_buffers;
  }

  inline unsigned int number_of_buffers()
  {
    return m_number_of_buffers;
  }

  inline AVCodec * get_avcodec()
  {
    return m_avcodec;
  }

  inline AVDictionary * get_avoptions()
  {
    return m_avoptions;
  }

  inline AVCodecContext * get_avcodec_context()
  {
    return m_avcodec_context;
  }

  inline AVFrame * get_avframe()
  {
    return m_avframe;
  }

  inline bool is_capturing()
  {
    return m_is_capturing;
  }

  inline time_t get_epoch_time_shift_us()
  {
    return m_epoch_time_shift_us;
  }

  inline std::vector<capture_format_t> supported_formats()
  {
    if (m_supported_formats.size() == 0) {
      this->get_supported_formats();
    }

    return m_supported_formats;
  }

  inline bool set_pixel_format(const formats::format_arguments_t & args)
  {
    bool result = false;

    std::shared_ptr<pixel_format_base> found_driver_format = nullptr;

    // First check if given format is supported by this driver
    for (auto driver_fmt : driver_supported_formats(args)) {
      if (driver_fmt->name() == args.name) {
        found_driver_format = driver_fmt;
      }
    }

    if (found_driver_format == nullptr) {
      // List the supported formats of this driver for the user before throwing
      std::cerr << "This driver supports the following formats:" << std::endl;
      for (auto driver_fmt : driver_supported_formats(args)) {
        std::cerr << "\t" << driver_fmt->name() << std::endl;
      }
      throw std::invalid_argument(
              "Specified format `" + args.name + "` is unsupported by this ROS driver"
      );
    }

    std::cout << "This device supports the following formats:" << std::endl;
    for (auto fmt : this->supported_formats()) {
      // Always list the devices supported formats for the user
      std::cout << "\t" << fmt.format.description << " ";
      std::cout << fmt.v4l2_fmt.width << " x " << fmt.v4l2_fmt.height << " (";
      std::cout << fmt.v4l2_fmt.discrete.denominator / fmt.v4l2_fmt.discrete.numerator << " Hz)";
      std::cout << std::endl;

      if (fmt.v4l2_fmt.pixel_format == found_driver_format->v4l2()) {
        result = true;
        m_image.pixel_format = found_driver_format;
      }
    }

    return result;
  }

  inline std::shared_ptr<pixel_format_base> set_pixel_format(const parameters_t & parameters)
  {
    // create format arguments structure
    formats::format_arguments_t format_args({
        parameters.pixel_format_name,
        parameters.image_width,
        parameters.image_height,
        m_image.number_of_pixels,
        parameters.av_device_format,
      });

    // Look for specified pixel format
    if (!this->set_pixel_format(format_args)) {
      throw std::invalid_argument(
              "Specified format `" + parameters.pixel_format_name + "` is unsupported by the " +
              "selected device `" + parameters.device_name + "`"
      );
    }

    return m_image.pixel_format;
  }

private:
  void init_read();
  void init_mmap();
  void init_userp();
  void init_device();

  void open_device();
  void grab_image();
  void read_frame();
  void process_image(const char * src, char * & dest, const int & bytes_used);

  void uninit_device();
  void close_device();

  std::string m_device_name;
  usb_cam::utils::io_method_t m_io;
  int m_fd;
  unsigned int m_number_of_buffers;
  std::shared_ptr<usb_cam::utils::buffer[]> m_buffers;
  image_t m_image;

  AVFrame * m_avframe;
  AVCodec * m_avcodec;
  AVDictionary * m_avoptions;
  AVCodecContext * m_avcodec_context;

  bool m_is_capturing;
  int m_framerate;
  const time_t m_epoch_time_shift_us;
  std::vector<capture_format_t> m_supported_formats;
};

}  // namespace usb_cam

#endif  // USB_CAM__USB_CAM_HPP_