Linux音视频开发之一:用V4L2采集webcam摄像头数据mjpeg 和yuv

网上v4l2介绍的文章文多,api的使用我就不再说了,只在这里贴出我的C++封装类。

源码直达:https://gitee.com/noevilme/libwebcam

webcam_v4l2.h

1/** 2 * Copyright(C)2020 NoevilMe. All rights reserved. 3 * File : webcam_v4l2.h 4 * Author : NoevilMe <surpass168@live.com> 5 * Date : 2020-05-21 23:00:54 6 * Last Modified Date: 2020-07-25 21:56:28 7 * Last Modified By : NoevilMe <surpass168@live.com> 8 */ 9#ifndef __WEBCAM_V4L2_H_ 10#define __WEBCAM_V4L2_H_ 11 12#include "log.h" 13 14#include <functional> 15#include <map> 16#include <memory> 17#include <string> 18 19#include <linux/videodev2.h> 20 21namespace noevil { 22namespace webcam { 23 24enum class WebcamFormat { 25 kFmtNone, 26 kFmtMJPG, // Motion-JPEG 27 kFmtYUYV // YUYV422 28}; 29 30struct V4l2BufUnit { 31 int index = 0; 32 uint32_t length = 0; 33 uint32_t offset = 0; 34 35 void *start = nullptr; 36 uint32_t bytes = 0; 37}; 38 39struct V4l2BufStat { 40 // for preparation 41 int count = 0; // mapped count 42 uint32_t type = 0; // enum v4l2_buf_type 43 struct V4l2BufUnit *buffer = nullptr; 44 45 // tmp 46 struct v4l2_buffer buf; 47}; 48 49class V4l2BufStatDeleter { 50public: 51 void operator()(V4l2BufStat *stat); 52}; 53 54struct V4l2Ctrl { 55 struct v4l2_queryctrl queryctrl; 56 struct v4l2_control control; 57}; 58 59class WebcamV4l2 { 60public: 61 WebcamV4l2(); 62 WebcamV4l2(const char *name); 63 WebcamV4l2(int id); 64 ~WebcamV4l2(); 65 66 bool IsOpen(); 67 68 // get error message if any interface returns false 69 std::string GetError() const; 70 71 bool Open(bool force = false); 72 bool Open(const char *name); 73 bool Close(); 74 75 // settings 76 bool Init(); 77 bool SetPixFormat(WebcamFormat fmt, uint32_t width, uint32_t height); 78 bool SetFps(uint8_t fps); 79 80 // sync mode 81 bool Start(); 82 bool Stop(); 83 84 // block, select and grab 85 // @timeout milliseconds 86 bool Grab(std::string &out, uint32_t timeout = 100); 87 // nullptr to discard 88 bool Grab(std::string *out, uint32_t timeout = 100); 89 90 // non-block, work with eventloop 91 bool Retrieve(std::string &img); 92 // nullptr to discard 93 bool Retrieve(std::string *img); 94 95 // with work callback 96 void 97 SetFrameCallback(const std::function<void(const char *const, uint32_t)> &cb) { 98 frame_cb_ = cb; 99 } 100 101 // block 102 bool Grab(uint32_t timeout = 100); 103 // non-block 104 bool Retrieve(bool discard = false); 105 106 // query util 107 bool GetControl(); 108 bool SetExposure(); 109 110 int fd() const { 111 return cam_fd_; 112 } 113 114private: 115 bool IsV4l2VideoDevice(); 116 117 bool QueryCapability(); 118 bool SetInput(const char *name = nullptr); 119 120 bool SetMMap(); 121 bool FreeMMap(); 122 123 bool StreamOn(); 124 bool StreamOff(); 125 126 std::string EnumerateMenu(uint32_t id, int32_t index_min, 127 int32_t index_max); 128 129 static std::string FormatErrno(); 130 static std::string PixFormatName(uint32_t format); 131 // TODO: 132 bool SetExtControl(); 133 134 bool ShowCtrlMenu(struct v4l2_queryctrl *queryctrl); 135 bool ShowCtrlInt(struct v4l2_queryctrl *queryctrl); 136 bool ShowControl(struct v4l2_queryctrl *queryctrl); 137 138 void Release(); 139 140 bool GrabFrame(std::string &img, uint32_t timeout = 100); 141 142private: 143 bool working_; 144 int cam_fd_; 145 uint32_t capabilities_; 146 uint32_t format_; 147 148 std::string error_; 149 std::string dev_name_; 150 std::shared_ptr<spdlog::logger> logger_; 151 152 std::map<decltype(V4l2Ctrl::queryctrl.id), V4l2Ctrl> ctrl_; 153 std::unique_ptr<V4l2BufStat, V4l2BufStatDeleter> buf_stat_; 154 std::function<void(const char *const, uint32_t)> frame_cb_; 155}; 156 157} // namespace webcam 158} // namespace noevil 159 160#endif /* __WEBCAM_V4L2_H_ */ 161

webcam_v4l2.cxx

1/** 2 * Copyright(C)2020 NoevilMe. All rights reserved. 3 * File : webcam_v4l2.cxx 4 * Author : NoevilMe <surpass168@live.com> 5 * Date : 2020-05-21 23:02:05 6 * Last Modified Date: 2020-07-26 09:33:07 7 * Last Modified By : NoevilMe <surpass168@live.com> 8 */ 9#include "webcam_v4l2.h" 10 11#include "spdlog/fmt/bundled/core.h" 12#include "spdlog/fmt/bundled/format.h" 13#include "string_util.hpp" 14 15#include <iostream> 16#include <stdexcept> 17#include <utility> 18#include <vector> 19 20#include <sys/ioctl.h> 21#include <sys/mman.h> 22#include <sys/select.h> 23 24// The SCALE macro converts a value (sv) from one range (sf -> sr) 25#define SCALE(df, dr, sf, sr, sv) (((sv - sf) * (dr - df) / (sr - sf)) + df) 26 27namespace noevil { 28namespace webcam { 29 30static constexpr auto QBUF_SIZE = 5; 31static constexpr auto VIDEO_DEV_PREFIX = "/dev/video"; 32static constexpr auto LOGGER_NAME = "webcam-v4l2"; 33 34void V4l2BufStatDeleter::operator()(V4l2BufStat *stat) { 35 if (!stat->buffer) 36 return; 37 38 for (int i = 0; i < stat->count; ++i) { 39 munmap(stat->buffer[i].start, stat->buffer[i].length); 40 } 41 42 delete[] stat->buffer; 43 stat->buffer = nullptr; 44} 45 46WebcamV4l2::WebcamV4l2() 47 : working_(false), 48 cam_fd_(-1), 49 capabilities_(0), 50 format_(0), 51 logger_(util::GetLogger(LOGGER_NAME)) {} 52 53WebcamV4l2::WebcamV4l2(int id) 54 : working_(false), 55 cam_fd_(-1), 56 capabilities_(0), 57 format_(0), 58 dev_name_(VIDEO_DEV_PREFIX + std::to_string(id)), 59 logger_(util::GetLogger(LOGGER_NAME)) {} 60 61WebcamV4l2::WebcamV4l2(const char *name) 62 : working_(false), 63 cam_fd_(-1), 64 capabilities_(0), 65 format_(0), 66 dev_name_(name), 67 logger_(util::GetLogger(LOGGER_NAME)) {} 68 69WebcamV4l2::~WebcamV4l2() { Release(); } 70 71std::string WebcamV4l2::GetError() const { return error_; } 72 73std::string WebcamV4l2::FormatErrno() { 74 return fmt::format("{} - {}", errno, strerror(errno)); 75} 76 77bool WebcamV4l2::Open(bool force) { 78 logger_->debug("check {} open", dev_name_); 79 if (IsOpen()) { 80 if (force) { 81 close(cam_fd_); 82 cam_fd_ = -1; 83 84 } else { 85 logger_->debug("{} is open", dev_name_); 86 return true; 87 } 88 } 89 90 logger_->debug("check {} stat", dev_name_); 91 struct stat st; 92 if (-1 == stat(dev_name_.data(), &st)) { 93 error_ = FormatErrno(); 94 logger_->error("can not identify {}, {}", dev_name_, error_); 95 return false; 96 } 97 98 logger_->debug("check {} type", dev_name_); 99 // check if it's device 100 if (!S_ISCHR(st.st_mode)) { 101 error_ = dev_name_ + " is not a device"; 102 logger_->error(error_); 103 return false; 104 } 105 106 logger_->debug("openning {} ", dev_name_); 107 cam_fd_ = open(dev_name_.data(), O_RDWR | O_NONBLOCK); 108 if (cam_fd_ == -1) { 109 error_ = FormatErrno(); 110 logger_->error("open {} failure: {}", dev_name_, error_); 111 return false; 112 } 113 logger_->debug("open {} success", dev_name_); 114 return true; 115} 116 117bool WebcamV4l2::Open(const char *name) { 118 if (!name) { 119 return false; 120 } 121 dev_name_ = name; 122 return Open(); 123} 124 125bool WebcamV4l2::IsOpen() { return cam_fd_ != -1; } 126 127bool WebcamV4l2::Init() { 128 logger_->debug("initializing {}", dev_name_); 129 if (!IsOpen()) { 130 error_ = "webcam is not open"; 131 logger_->error(error_); 132 return false; 133 } 134 135 if (!QueryCapability()) { 136 return false; 137 } 138 139 if (!IsV4l2VideoDevice()) { 140 error_ = fmt::format("{} is not a video device", dev_name_); 141 logger_->error(error_); 142 return false; 143 } 144 145 if (!SetInput()) { 146 return false; 147 } 148 149 return true; 150} 151 152 153bool WebcamV4l2::GrabFrame(std::string &img, uint32_t timeout) { 154 struct timeval tv; 155 tv.tv_sec = 0; 156 tv.tv_usec = timeout * 1000; 157 158 fd_set fds; 159 FD_ZERO(&fds); 160 FD_SET(cam_fd_, &fds); 161 162 int r = select(cam_fd_ + 1, &fds, nullptr, nullptr, &tv); 163 164 if (-1 == r) { 165 error_ = fmt::format("select failure, {}", FormatErrno()); 166 logger_->error(error_); 167 return false; 168 } 169 170 if (!r) { 171 error_ = fmt::format("select {} ms timeout", timeout); 172 logger_->error(error_); 173 return false; 174 } 175 176 auto buf_ptr = &buf_stat_->buf; 177 memset(buf_ptr, 0, sizeof(*buf_ptr)); 178 buf_ptr->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 179 buf_ptr->memory = V4L2_MEMORY_MMAP; 180 181 if (ioctl(cam_fd_, VIDIOC_DQBUF, buf_ptr) == -1) { 182 error_ = fmt::format("VIDIOC_DQBUF failure, {}", FormatErrno()); 183 logger_->error(error_); 184 return false; 185 } 186 187 auto index = buf_ptr->index; 188 buf_stat_->buffer[index].bytes = buf_ptr->bytesused; 189 190 img.assign((char *)buf_stat_->buffer[index].start, 191 buf_stat_->buffer[index].bytes); 192 193 if (ioctl(cam_fd_, VIDIOC_QBUF, buf_ptr) == -1) { 194 error_ = fmt::format("VIDIOC_QBUF failure, {}", FormatErrno()); 195 logger_->error(error_); 196 return false; 197 } 198 199 return true; 200} 201 202bool WebcamV4l2::Close() { 203 if (IsOpen()) { 204 close(cam_fd_); 205 cam_fd_ = -1; 206 } 207 208 return true; 209} 210 211bool WebcamV4l2::SetInput(const char *name) { 212 uint32_t match_index = 0; 213 214 struct v4l2_input cam_input; 215 cam_input.index = 0; 216 while (ioctl(cam_fd_, VIDIOC_ENUMINPUT, &cam_input) == 0) { 217 logger_->debug("enumerate input {} name: {}, type: {}", cam_input.index, 218 cam_input.name, cam_input.type); 219 if (name && strncasecmp((char *)cam_input.name, name, 32) == 0) { 220 match_index = cam_input.index; 221 } 222 223 ++cam_input.index; 224 } 225 226 if (cam_input.index == 0) { 227 error_ = fmt::format("no input on {}", dev_name_); 228 logger_->error(error_); 229 return false; 230 } 231 232 cam_input.index = match_index; 233 if (ioctl(cam_fd_, VIDIOC_ENUMINPUT, &cam_input) == -1) { 234 error_ = fmt::format("query input {} failure", match_index); 235 logger_->error(error_); 236 return false; 237 } 238 239 logger_->debug("try to set input index: {}, name: {}, type: {}", 240 cam_input.index, cam_input.name, cam_input.type); 241 242 if (ioctl(cam_fd_, VIDIOC_S_INPUT, &cam_input) == -1) { 243 error_ = fmt::format("set input {} failure: {}", cam_input.index, 244 strerror(errno)); 245 logger_->error(error_); 246 return false; 247 } 248 249 logger_->debug("set input success"); 250 251 return true; 252} 253 254bool WebcamV4l2::QueryCapability() { 255 if (!IsOpen()) { 256 error_ = fmt::format("{} is not open", dev_name_); 257 logger_->error(error_); 258 return false; 259 } 260 261 struct v4l2_capability cam_cap; 262 if (ioctl(cam_fd_, VIDIOC_QUERYCAP, &cam_cap) == -1) { 263 error_ = FormatErrno(); 264 logger_->error("query capibility failure: {}", error_); 265 return false; 266 } 267 logger_->info("capabilities: 0x{:X}", cam_cap.capabilities); 268 logger_->info("card name : {}", cam_cap.card); 269 logger_->info("driver name : {}", cam_cap.driver); 270 logger_->info("version : {}", cam_cap.version); 271 logger_->info("bus info : {}", cam_cap.bus_info); 272 273 capabilities_ = cam_cap.capabilities; 274 return true; 275} 276 277bool WebcamV4l2::IsV4l2VideoDevice() { 278 // Judge if the device is a camera device 279 return (capabilities_ & V4L2_CAP_VIDEO_CAPTURE) != 0; 280} 281 282std::string WebcamV4l2::PixFormatName(uint32_t format) { 283 char buf[20] = {0}; 284 sprintf(buf, "[0x%08X] '%c%c%c%c'", format, format >> 0, format >> 8, 285 format >> 16, format >> 24); 286 return buf; 287} 288 289bool WebcamV4l2::SetPixFormat(WebcamFormat fmt, uint32_t width, 290 uint32_t height) { 291 if (!IsV4l2VideoDevice()) { 292 error_ = fmt::format("{} is not a video device", dev_name_); 293 logger_->error(error_); 294 return false; 295 } 296 297 uint32_t pix_format = 0; 298 299 struct v4l2_fmtdesc fmt_desc; 300 fmt_desc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 301 fmt_desc.index = 0; 302 while (ioctl(cam_fd_, VIDIOC_ENUM_FMT, &fmt_desc) == 0) { 303 logger_->info("enumerate format: {}, {}", 304 PixFormatName(fmt_desc.pixelformat), 305 fmt_desc.description); 306 307 if (fmt == WebcamFormat::kFmtMJPG && 308 fmt_desc.pixelformat == V4L2_PIX_FMT_MJPEG) { 309 pix_format = V4L2_PIX_FMT_MJPEG; 310 } 311 312 if (fmt == WebcamFormat::kFmtYUYV && 313 fmt_desc.pixelformat == V4L2_PIX_FMT_YUYV) { 314 pix_format = V4L2_PIX_FMT_YUYV; 315 } 316 317 struct v4l2_frmsizeenum frmsize; 318 frmsize.pixel_format = fmt_desc.pixelformat; 319 frmsize.index = 0; 320 while (ioctl(cam_fd_, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0) { 321 logger_->debug("frame size: {}x{}", frmsize.discrete.width, 322 frmsize.discrete.height); 323 324 struct v4l2_frmivalenum frmival; 325 memset(&frmival, 0, sizeof(frmival)); 326 frmival.pixel_format = frmsize.pixel_format; 327 frmival.width = frmsize.discrete.width; 328 frmival.height = frmsize.discrete.height; 329 frmival.type = V4L2_FRMIVAL_TYPE_DISCRETE; 330 frmival.index = 0; 331 332 while (ioctl(cam_fd_, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) == 0) { 333 logger_->debug("frame interval: {:0.3f}s ({} fps)", 334 (double)frmival.discrete.numerator / 335 frmival.discrete.denominator, 336 frmival.discrete.denominator); 337 frmival.index++; 338 } 339 ++frmsize.index; 340 } 341 342 ++fmt_desc.index; 343 } 344 345 if (fmt_desc.index == 0) { 346 error_ = "no format is supported"; 347 logger_->error(error_); 348 return false; 349 } 350 351 // no match, select the 1st format 352 if (pix_format == 0) { 353 fmt_desc.index = 0; 354 if (ioctl(cam_fd_, VIDIOC_ENUM_FMT, &fmt_desc) == 0) { 355 pix_format = fmt_desc.pixelformat; 356 } else { 357 error_ = 358 fmt::format("get index 0 format failure, {}", FormatErrno()); 359 logger_->error(error_); 360 return false; 361 } 362 } 363 364 logger_->debug("try format {}, {}x{}", PixFormatName(pix_format), width, 365 height); 366 struct v4l2_format v4l2_fmt; 367 v4l2_fmt.type = V4L2_CAP_VIDEO_CAPTURE; 368 v4l2_fmt.fmt.pix.width = width; 369 v4l2_fmt.fmt.pix.height = height; 370 v4l2_fmt.fmt.pix.pixelformat = pix_format; 371 v4l2_fmt.fmt.pix.field = V4L2_FIELD_ANY; 372 373 if (ioctl(cam_fd_, VIDIOC_TRY_FMT, &v4l2_fmt) == -1) { 374 error_ = fmt::format("try format {}, {}x{} error, {}", 375 PixFormatName(pix_format), width, height, 376 FormatErrno()); 377 logger_->error(error_); 378 return false; 379 } 380 381 if (v4l2_fmt.fmt.pix.pixelformat != pix_format) { 382 error_ = fmt::format("format {} is not supported, run as {}", 383 PixFormatName(pix_format), 384 PixFormatName(v4l2_fmt.fmt.pix.pixelformat)); 385 logger_->error(error_); 386 return false; 387 } 388 389 if (v4l2_fmt.fmt.pix.width != width || v4l2_fmt.fmt.pix.height != height) { 390 logger_->info("adjust resolution from {}x{} to {}x{}", width, height, 391 v4l2_fmt.fmt.pix.width, v4l2_fmt.fmt.pix.height); 392 } 393 394 if (ioctl(cam_fd_, VIDIOC_S_FMT, &v4l2_fmt) == -1) { 395 error_ = fmt::format("set pixel format failure, {}", FormatErrno()); 396 logger_->error(error_); 397 return false; 398 } 399 400 format_ = pix_format; 401 logger_->info("enable pixel format {} {}x{}", PixFormatName(format_), 402 v4l2_fmt.fmt.pix.width, v4l2_fmt.fmt.pix.height); 403 404 return true; 405} 406 407void WebcamV4l2::Release() { 408 Stop(); 409 410 Close(); 411 412 capabilities_ = 0; 413 format_ = 0; 414} 415 416bool WebcamV4l2::SetMMap() { 417 if (buf_stat_) { 418 return true; 419 } 420 421 if ((capabilities_ & V4L2_CAP_STREAMING) == 0) { 422 error_ = fmt::format("set mmap failure, {} is not a streaming device", 423 dev_name_); 424 logger_->error(error_); 425 return false; 426 } 427 428 std::unique_ptr<V4l2BufStat, V4l2BufStatDeleter> buf_stat( 429 new V4l2BufStat, V4l2BufStatDeleter()); 430 431 // request 432 struct v4l2_requestbuffers req; 433 memset(&req, 0, sizeof(req)); 434 req.count = QBUF_SIZE; 435 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 436 req.memory = V4L2_MEMORY_MMAP; 437 438 if (ioctl(cam_fd_, VIDIOC_REQBUFS, &req) == -1) { 439 error_ = fmt::format("VIDIOC_REQBUFS failure, {}", FormatErrno()); 440 logger_->error(error_); 441 return false; 442 } 443 444 logger_->debug("mmap information:"); 445 logger_->debug("buffer for {} frames", req.count); 446 if (req.count < 2) { 447 error_ = "Insufficient buffer memory"; 448 logger_->error(error_); 449 return false; 450 } 451 452 buf_stat->type = req.type; 453 buf_stat->buffer = new V4l2BufUnit[req.count]; 454 buf_stat->count = 0; 455 456 // query and map 457 for (uint32_t i = 0; i < req.count; ++i) { 458 struct v4l2_buffer &buf = buf_stat->buf; 459 memset(&buf, 0, sizeof(struct v4l2_buffer)); 460 461 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 462 buf.memory = V4L2_MEMORY_MMAP; 463 buf.index = i; 464 465 if (ioctl(cam_fd_, VIDIOC_QUERYBUF, &buf) == -1) { 466 error_ = 467 fmt::format("query buffer {} failure, {}", i, strerror(errno)); 468 logger_->error(error_); 469 return false; 470 } 471 472 auto &unit = buf_stat->buffer[i]; 473 unit.index = i; 474 unit.length = buf.length; 475 unit.offset = buf.m.offset; 476 unit.start = mmap(nullptr, buf.length, PROT_READ | PROT_WRITE, 477 MAP_SHARED, cam_fd_, buf.m.offset); 478 479 if (unit.start == MAP_FAILED) { 480 error_ = fmt::format("map buffer {} failure, {}", i, FormatErrno()); 481 logger_->error(error_); 482 return false; 483 } 484 buf_stat->count = i + 1; 485 } 486 487 // put in queue 488 for (uint32_t i = 0; i < req.count; ++i) { 489 struct v4l2_buffer &buf = buf_stat->buf; 490 memset(&buf, 0, sizeof(struct v4l2_buffer)); 491 492 buf.index = i; 493 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 494 buf.memory = V4L2_MEMORY_MMAP; 495 496 if (ioctl(cam_fd_, VIDIOC_QBUF, &buf) == -1) { 497 error_ = fmt::format("unable to queue buffer, {}", FormatErrno()); 498 logger_->error(error_); 499 return false; 500 } 501 } 502 503 buf_stat_ = std::move(buf_stat); 504 return true; 505} 506 507bool WebcamV4l2::FreeMMap() { 508 if (buf_stat_) { 509 buf_stat_.reset(); 510 } 511 return true; 512} 513 514bool WebcamV4l2::StreamOn() { 515 if (!buf_stat_) { 516 error_ = "mmap is not ready"; 517 logger_->error(error_); 518 return false; 519 } 520 521 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 522 if (ioctl(cam_fd_, VIDIOC_STREAMON, &type) == -1) { 523 error_ = fmt::format("streamon failure, {}", FormatErrno()); 524 logger_->error(error_); 525 return false; 526 } 527 528 return true; 529} 530 531bool WebcamV4l2::StreamOff() { 532 if (!buf_stat_) { 533 error_ = "mmap is not ready"; 534 logger_->error(error_); 535 return false; 536 } 537 538 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 539 if (ioctl(cam_fd_, VIDIOC_STREAMOFF, &type) == -1) { 540 error_ = fmt::format("streamoff failure, {}", FormatErrno()); 541 logger_->error(error_); 542 return false; 543 } 544 545 return true; 546} 547 548bool WebcamV4l2::SetFps(uint8_t fps) { 549 if (!fps) { 550 return false; 551 } 552 553 struct v4l2_streamparm parm; 554 memset(&parm, 0, sizeof(struct v4l2_streamparm)); 555 parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 556 if (ioctl(cam_fd_, VIDIOC_G_PARM, &parm) == -1) { 557 error_ = fmt::format("VIDIOC_G_PARM failure, {}", FormatErrno()); 558 logger_->error(error_); 559 return false; 560 } 561 562 logger_->info("current fps {}", parm.parm.capture.timeperframe.denominator); 563 if (parm.parm.capture.timeperframe.denominator == fps) { 564 return true; 565 } 566 567 logger_->info("try to set fps {}", fps); 568 struct v4l2_streamparm setfps; 569 memset(&setfps, 0, sizeof(setfps)); 570 setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 571 setfps.parm.capture.timeperframe.numerator = 1; 572 setfps.parm.capture.timeperframe.denominator = fps; 573 if (ioctl(cam_fd_, VIDIOC_S_PARM, &setfps) == -1) { 574 /* Not fatal - just warn about it */ 575 error_ = fmt::format("set fps failure, {}", FormatErrno()); 576 logger_->warn(error_); 577 return false; 578 } 579 580 if (ioctl(cam_fd_, VIDIOC_G_PARM, &parm) == -1) { 581 error_ = fmt::format("VIDIOC_G_PARM failure, {}", FormatErrno()); 582 logger_->error(error_); 583 return false; 584 } 585 586 logger_->info("current fps {}", parm.parm.capture.timeperframe.denominator); 587 return true; 588} 589 590bool WebcamV4l2::GetControl() { 591 struct v4l2_queryctrl queryctrl; 592 memset(&queryctrl, 0, sizeof(queryctrl)); 593 queryctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL; 594 while (0 == ioctl(cam_fd_, VIDIOC_QUERYCTRL, &queryctrl)) { 595 ShowControl(&queryctrl); 596 queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; 597 } 598 599 return true; 600} 601 602bool WebcamV4l2::SetExtControl() { 603 // might be same as SetControl 604 struct v4l2_query_ext_ctrl query_ext_ctrl; 605 memset(&query_ext_ctrl, 0, sizeof(query_ext_ctrl)); 606 query_ext_ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND; 607 608 while (0 == ioctl(cam_fd_, VIDIOC_QUERY_EXT_CTRL, &query_ext_ctrl)) { 609 if (!(query_ext_ctrl.flags & V4L2_CTRL_FLAG_DISABLED)) { 610 logger_->info("Ext control {}", query_ext_ctrl.name); 611 612 if (query_ext_ctrl.type == V4L2_CTRL_TYPE_MENU) { 613 logger_->info("{}", EnumerateMenu(query_ext_ctrl.id, 614 query_ext_ctrl.minimum, 615 query_ext_ctrl.maximum)); 616 } 617 } 618 619 query_ext_ctrl.id |= 620 V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND; 621 } 622 return true; 623} 624 625std::string WebcamV4l2::EnumerateMenu(uint32_t id, int32_t index_min, 626 int32_t index_max) { 627 struct v4l2_querymenu querymenu; 628 memset(&querymenu, 0, sizeof(querymenu)); 629 querymenu.id = id; 630 631 std::vector<std::string> menu_names; 632 for (int32_t m = index_min; m <= index_max; ++m) { 633 querymenu.index = m; 634 if (0 == ioctl(cam_fd_, VIDIOC_QUERYMENU, &querymenu)) { 635 menu_names.push_back((char *)querymenu.name); 636 } 637 } 638 639 if (menu_names.empty()) { 640 return std::string(); 641 } else { 642 return string_util::join(menu_names, " | "); 643 } 644} 645 646bool WebcamV4l2::ShowCtrlMenu(struct v4l2_queryctrl *queryctrl) { 647 648 std::string options = 649 EnumerateMenu(queryctrl->id, queryctrl->minimum, queryctrl->maximum); 650 651 struct v4l2_control control; 652 memset(&control, 0, sizeof(control)); 653 control.id = queryctrl->id; 654 if (ioctl(cam_fd_, VIDIOC_G_CTRL, &control) == -1) { 655 error_ = fmt::format("read value of control {} failure, {}", 656 queryctrl->name, strerror(errno)); 657 logger_->error(error_); 658 return false; 659 } 660 661 struct v4l2_querymenu querymenu; 662 memset(&querymenu, 0, sizeof(querymenu)); 663 querymenu.id = queryctrl->id; 664 querymenu.index = control.value; 665 666 if (-1 == ioctl(cam_fd_, VIDIOC_QUERYMENU, &querymenu)) { 667 error_ = 668 fmt::format("read menu item {} value of control {} failure, {}", 669 control.value, queryctrl->name, strerror(errno)); 670 logger_->error(error_); 671 return false; 672 } 673 674 logger_->info("queryctrl menu id: 0x{:X}, name: {}, menu: {}, options: {}", 675 queryctrl->id, queryctrl->name, querymenu.name, options); 676 677 return true; 678} 679 680bool WebcamV4l2::ShowCtrlInt(struct v4l2_queryctrl *queryctrl) { 681 struct v4l2_control control; 682 memset(&control, 0, sizeof(control)); 683 control.id = queryctrl->id; 684 685 if (ioctl(cam_fd_, VIDIOC_G_CTRL, &control) == -1) { 686 logger_->error("read value of control {} failure, {}", queryctrl->name, 687 strerror(errno)); 688 return false; 689 } 690 691 if (queryctrl->maximum - queryctrl->minimum <= 10) { 692 logger_->info("queryctrl int id: 0x{:X}, name: {:<32}, value:{:<12}, " 693 "flags: {:<2} " 694 "(default: {:<4}, [{:<6}:{:<6}:{:<2}])", 695 queryctrl->id, queryctrl->name, control.value, 696 queryctrl->flags, queryctrl->default_value, 697 queryctrl->minimum, queryctrl->maximum, queryctrl->step); 698 } else { 699 logger_->info("queryctrl int id: 0x{:X}, name: {:<32}, value:{:<5} - " 700 "{:>3}%, flags: " 701 "{:<2} (default: {:<4}, [{:<6}:{:<6}:{:<2}])", 702 queryctrl->id, queryctrl->name, control.value, 703 SCALE(0, 100, queryctrl->minimum, queryctrl->maximum, 704 control.value), 705 queryctrl->flags, queryctrl->default_value, 706 queryctrl->minimum, queryctrl->maximum, queryctrl->step); 707 } 708 709 V4l2Ctrl ctrl{.queryctrl = *queryctrl, .control = control}; 710 ctrl_[control.id] = ctrl; 711 712 return true; 713} 714 715bool WebcamV4l2::ShowControl(struct v4l2_queryctrl *queryctrl) { 716 if (!queryctrl) { 717 return false; 718 } 719 720 if (queryctrl->flags & V4L2_CTRL_FLAG_DISABLED) { 721 logger_->info( 722 "queryctrl id: 0x{:X}, name: {:<32}, DISABLED, flags: {:<2} ", 723 queryctrl->id, queryctrl->name, queryctrl->flags); 724 return false; 725 } 726 727 switch (queryctrl->type) { 728 case V4L2_CTRL_TYPE_INTEGER: 729 if (!ShowCtrlInt(queryctrl)) { 730 return false; 731 } 732 break; 733 734 case V4L2_CTRL_TYPE_BOOLEAN: { 735 struct v4l2_control control; 736 memset(&control, 0, sizeof(control)); 737 738 control.id = queryctrl->id; 739 if (ioctl(cam_fd_, VIDIOC_G_CTRL, &control) == -1) { 740 error_ = fmt::format("read value of control {} failure, {}", 741 queryctrl->name, strerror(errno)); 742 logger_->error(error_); 743 return false; 744 } 745 logger_->info("queryctrl bool id: 0x{:X}, name: {:<32}, value:{:<12}, " 746 "flags: {:<2} " 747 "(default: {})", 748 queryctrl->id, queryctrl->name, 749 control.value ? "True" : "False", queryctrl->flags, 750 queryctrl->default_value ? "True" : "False"); 751 752 V4l2Ctrl ctrl{.queryctrl = *queryctrl, .control = control}; 753 ctrl_[control.id] = ctrl; 754 } break; 755 756 case V4L2_CTRL_TYPE_MENU: 757 if (!ShowCtrlMenu(queryctrl)) { 758 return false; 759 } 760 break; 761 762 case V4L2_CTRL_TYPE_BUTTON: 763 logger_->info("queryctrl btn id: 0x{:X}, name: {:<32} - [Button]", 764 queryctrl->id, queryctrl->name); 765 break; 766 767 default: 768 logger_->info("queryctrl deft id: 0x{:X}, name: {:<32} N/A [Unknown " 769 "Control Type]", 770 queryctrl->id, queryctrl->name); 771 break; 772 } 773 774 return true; 775} 776 777bool WebcamV4l2::SetExposure() { 778 int ret; 779 struct v4l2_control ctrl; 780 //得到曝光模式 781 ctrl.id = V4L2_CID_EXPOSURE_AUTO; 782 if (ioctl(cam_fd_, VIDIOC_G_CTRL, &ctrl) == -1) { 783 printf("Get exposure auto Type failed\n"); 784 return false; 785 } 786 printf("\nGet Exposure Auto Type:[%d]\n", ctrl.value); 787 788 // ctrl.id = V4L2_CID_ROTATE; 789 // ctrl.value = 90; 790 // if (ioctl(cam_fd_, VIDIOC_S_CTRL, &ctrl) == -1) { 791 // printf("Set rotate failed\n"); 792 // return false; 793 //} 794 // printf("\nSet rotate:[%d]\n", ctrl.value); 795 // struct v4l2_control ctrl; 796 // ctrl.id = V4L2_CID_EXPOSURE_AUTO; 797 // if (ioctl(cam_fd_, VIDIOC_G_CTRL, &ctrl) == -1) { 798 // logger_->warn("get exposure failure, {}", strerror(errno)); 799 //} 800 // logger_->info("exposure {}", ctrl.value); 801 return false; 802} 803 804// sync mode 805bool WebcamV4l2::Grab(std::string &out, uint32_t timeout) { 806 if (!working_) { 807 error_ = "not started"; 808 logger_->error(error_); 809 return false; 810 } 811 812 if (!buf_stat_) { 813 error_ = "v4l2 buffers are not ready"; 814 logger_->error(error_); 815 return false; 816 } 817 818 if (!GrabFrame(out, timeout)) { 819 error_ = fmt::format("grab frame failure, {}", error_); 820 logger_->error(error_); 821 return false; 822 } 823 824 return true; 825} 826 827bool WebcamV4l2::Grab(std::string *out, uint32_t timeout) { 828 if (!working_) { 829 error_ = "not started"; 830 logger_->error(error_); 831 return false; 832 } 833 834 if (!buf_stat_) { 835 error_ = "v4l2 buffers are not ready"; 836 logger_->error(error_); 837 return false; 838 } 839 840 struct timeval tv; 841 tv.tv_sec = 0; 842 tv.tv_usec = timeout * 1000; 843 844 fd_set fds; 845 FD_ZERO(&fds); 846 FD_SET(cam_fd_, &fds); 847 848 int r = select(cam_fd_ + 1, &fds, nullptr, nullptr, &tv); 849 850 if (-1 == r) { 851 error_ = fmt::format("select failure, {}", FormatErrno()); 852 logger_->error(error_); 853 return false; 854 } 855 856 if (!r) { 857 error_ = fmt::format("select {} ms timeout", timeout); 858 logger_->error(error_); 859 return false; 860 } 861 862 auto buf_ptr = &buf_stat_->buf; 863 memset(buf_ptr, 0, sizeof(*buf_ptr)); 864 buf_ptr->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 865 buf_ptr->memory = V4L2_MEMORY_MMAP; 866 867 if (ioctl(cam_fd_, VIDIOC_DQBUF, buf_ptr) == -1) { 868 logger_->error("grab VIDIOC_DQBUF failure, {}", FormatErrno()); 869 return false; 870 } 871 872 if (out) { 873 (*out).assign((char *)buf_stat_->buffer[buf_ptr->index].start, 874 buf_ptr->bytesused); 875 } 876 877 if (ioctl(cam_fd_, VIDIOC_QBUF, buf_ptr) == -1) { 878 logger_->error("grab VIDIOC_QBUF failure, {}", FormatErrno()); 879 return false; 880 } 881 882 return true; 883} 884 885// block 886bool WebcamV4l2::Grab(uint32_t timeout) { 887 if (!frame_cb_) { 888 error_ = "frame callback is null"; 889 logger_->error(error_); 890 return false; 891 } 892 893 if (!working_) { 894 error_ = "stream is not started"; 895 logger_->error(error_); 896 return false; 897 } 898 899 if (!buf_stat_) { 900 error_ = "v4l2 buffers are not ready"; 901 logger_->error(error_); 902 return false; 903 } 904 905 struct timeval tv; 906 tv.tv_sec = 0; 907 tv.tv_usec = timeout * 1000; 908 909 fd_set fds; 910 FD_ZERO(&fds); 911 FD_SET(cam_fd_, &fds); 912 913 int r = select(cam_fd_ + 1, &fds, nullptr, nullptr, &tv); 914 915 if (-1 == r) { 916 error_ = fmt::format("select failure, {}", FormatErrno()); 917 logger_->error(error_); 918 return false; 919 } 920 921 if (!r) { 922 error_ = fmt::format("select {} ms timeout", timeout); 923 logger_->error(error_); 924 return false; 925 } 926 927 auto buf_ptr = &buf_stat_->buf; 928 memset(buf_ptr, 0, sizeof(*buf_ptr)); 929 buf_ptr->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 930 buf_ptr->memory = V4L2_MEMORY_MMAP; 931 932 if (ioctl(cam_fd_, VIDIOC_DQBUF, buf_ptr) == -1) { 933 logger_->error("VIDIOC_DQBUF failure"); 934 return false; 935 } 936 937 frame_cb_((const char *)buf_stat_->buffer[buf_ptr->index].start, 938 buf_ptr->bytesused); 939 940 if (ioctl(cam_fd_, VIDIOC_QBUF, buf_ptr) == -1) { 941 logger_->error("VIDIOC_QBUF failure"); 942 return false; 943 } 944 945 return true; 946} 947 948// non-block 949bool WebcamV4l2::Retrieve(bool discard) { 950 if (!frame_cb_) { 951 error_ = "frame callback is null"; 952 logger_->error(error_); 953 return false; 954 } 955 956 if (!working_) { 957 error_ = "stream is not started"; 958 logger_->error(error_); 959 return false; 960 } 961 962 if (!buf_stat_) { 963 error_ = "v4l2 buffers are not ready"; 964 logger_->error(error_); 965 return false; 966 } 967 968 struct v4l2_buffer buf; 969 memset(&buf, 0, sizeof(buf)); 970 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 971 buf.memory = V4L2_MEMORY_MMAP; 972 973 if (ioctl(cam_fd_, VIDIOC_DQBUF, &buf) == -1) { 974 logger_->error("retrieve VIDIOC_DQBUF failure, {}", FormatErrno()); 975 return false; 976 } 977 978 if (!discard) { 979 frame_cb_((const char *)buf_stat_->buffer[buf.index].start, 980 buf.bytesused); 981 } 982 983 if (ioctl(cam_fd_, VIDIOC_QBUF, &buf) == -1) { 984 logger_->error("retrieve VIDIOC_QBUF failure, {}", FormatErrno()); 985 return false; 986 } 987 988 return true; 989} 990 991bool WebcamV4l2::Retrieve(std::string &img) { 992 if (!working_) { 993 error_ = "stream is not started"; 994 logger_->error(error_); 995 return false; 996 } 997 998 if (!buf_stat_) { 999 error_ = "v4l2 buffers are not ready"; 1000 logger_->error(error_); 1001 return false; 1002 } 1003 1004 struct v4l2_buffer buf; 1005 memset(&buf, 0, sizeof(buf)); 1006 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 1007 buf.memory = V4L2_MEMORY_MMAP; 1008 1009 if (ioctl(cam_fd_, VIDIOC_DQBUF, &buf) == -1) { 1010 logger_->error("retrieve VIDIOC_DQBUF failure, {}", FormatErrno()); 1011 return false; 1012 } 1013 1014 img.assign((char *)buf_stat_->buffer[buf.index].start, buf.bytesused); 1015 1016 if (ioctl(cam_fd_, VIDIOC_QBUF, &buf) == -1) { 1017 logger_->error("retrieve VIDIOC_QBUF failure, {}", FormatErrno()); 1018 return false; 1019 } 1020 1021 return true; 1022} 1023 1024bool WebcamV4l2::Retrieve(std::string *img) { 1025 if (img) { 1026 return Retrieve(*img); 1027 } else { // discard frame quickly 1028 if (!working_) { 1029 error_ = "stream is not started"; 1030 logger_->error(error_); 1031 return false; 1032 } 1033 1034 if (!buf_stat_) { 1035 error_ = "v4l2 buffers are not ready"; 1036 logger_->error(error_); 1037 return false; 1038 } 1039 1040 struct v4l2_buffer buf; 1041 memset(&buf, 0, sizeof(buf)); 1042 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 1043 buf.memory = V4L2_MEMORY_MMAP; 1044 1045 if (ioctl(cam_fd_, VIDIOC_DQBUF, &buf) == -1) { 1046 logger_->error("retrieve VIDIOC_DQBUF failure, {}", FormatErrno()); 1047 return false; 1048 } 1049 1050 if (ioctl(cam_fd_, VIDIOC_QBUF, &buf) == -1) { 1051 logger_->error("retrieve VIDIOC_QBUF failure, {}", FormatErrno()); 1052 return false; 1053 } 1054 1055 return true; 1056 } 1057} 1058 1059bool WebcamV4l2::Start() { 1060 if (working_) { 1061 return true; 1062 } 1063 1064 if (!SetMMap()) { 1065 return false; 1066 } 1067 1068 working_ = StreamOn(); 1069 logger_->info("start working {}", working_); 1070 return working_; 1071} 1072 1073bool WebcamV4l2::Stop() { 1074 if (!working_) { 1075 return false; 1076 } 1077 1078 StreamOff(); 1079 1080 FreeMMap(); 1081 1082 working_ = false; 1083 logger_->info("stop working"); 1084 return true; 1085} 1086 1087} // namespace webcam 1088} // namespace noevil 1089

1、获取jpeg图像

1#include "jpeg_transform.h" 2#include "webcam_v4l2.h" 3#include <iostream> 4#include <string> 5 6using namespace noevil::webcam; 7 8bool WriteFile(const std::string &path, const std::string &content) { 9 int fd = open(path.data(), O_RDWR | O_CREAT, 00664); 10 if (fd == -1) { 11 throw std::runtime_error( 12 fmt::format("Failed to open {}, {}", path, strerror(errno))); 13 } 14 15 int writesize = write(fd, content.data(), content.length()); 16 close(fd); 17 return true; 18} 19 20int main(int argc, char **argv) { 21 setlocale(LC_ALL, ""); 22 23 noevil::util::Init("cam.log"); 24 noevil::util::SetLevel(spdlog::level::trace); 25 26 noevil::webcam::WebcamV4l2 cam(argv[1]); 27 if (!cam.Open()) { 28 return 1; 29 } 30 if (!cam.Init()) { 31 std::cout << "init failure, " << cam.GetError() << std::endl; 32 return 1; 33 } 34 35 if (!cam.SetPixFormat(noevil::webcam::WebcamFormat::kFmtMJPG, 1920, 1080)) { 36 std::cout << "set format failure, " << cam.GetError() << std::endl; 37 return 1; 38 } 39 40 if (!cam.Start()) { 41 std::cout << "start failure, " << cam.GetError() << std::endl; 42 return 1; 43 } 44 45 std::unique_ptr<JpegTransform> transform( 46 new JpegTransform(JpegTransform::JpegTransformOp::kTransRot90)); 47 48 for (int i = 0; i < 100; ++i) { 49 std::string frm; 50 if (cam.Grab(frm)) { 51 std::string name = std::to_string(i) + ".jpg"; 52 WriteFile(name, frm); 53 54 std::string rotate; 55 transform->Transform(frm, rotate); 56 WriteFile(std::to_string(i) + "_90.jpg", rotate); 57 } 58 } 59 60 cam.Stop(); 61 62 return 0; 63} 64 65

我附带了一个图像旋转的包装类,需要libturbojpeg0-dev库。

2、获取yuv图像

1 noevil::webcam::WebcamV4l2 cam(argv[1]); 2 if (!cam.Open()) { 3 return 1; 4 } 5 if (!cam.Init()) { 6 std::cout << "init failure, " << cam.GetError() << std::endl; 7 return 1; 8 } 9 10 if (!cam.SetPixFormat(noevil::webcam::WebcamFormat::kFmtYUYV, 1280, 720)) { 11 std::cout << "set format failure, " << cam.GetError() << std::endl; 12 return 1; 13 } 14 15 if (!cam.Start()) { 16 std::cout << "start failure, " << cam.GetError() << std::endl; 17 return 1; 18 } 19 for (int i = 0; i < 100; ++i) { 20 std::string frm; 21 if (cam.Grab(frm)) { 22 23 std::string name = std::to_string(i) + ".yuv"; 24 WriteFile(name, frm); 25 } 26 } 27 28 cam.Stop();

3、获取yuv视频

1noevil::webcam::WebcamV4l2 cam(argv[1]); 2 if (!cam.Open()) { 3 return 1; 4 } 5 if (!cam.Init()) { 6 std::cout << "init failure, " << cam.GetError() << std::endl; 7 return 1; 8 } 9 10 if (!cam.SetPixFormat(noevil::webcam::WebcamFormat::kFmtYUYV, 1280, 720)) { 11 std::cout << "set format failure, " << cam.GetError() << std::endl; 12 return 1; 13 } 14 15 if (!cam.Start()) { 16 std::cout << "start failure, " << cam.GetError() << std::endl; 17 return 1; 18 } 19 20 FILE *fp = fopen("video_yuv422.yuv", "wb+"); 21 22 auto cb=[&](const char* const data, uint32_t size) 23 { 24 fwrite(data, 1, size, fp); 25 }; 26 27 cam.SetFrameCallback(cb); 28 // discard 29 for (int i = 0; i < 100; ++i) { 30 cam.Grab(nullptr, 200); 31 } 32 33 for (int i = 0; i < 100; ++i) { 34 cam.Grab(200); 35 } 36 37 cam.Stop(); 38 39 fclose(fp);

摄像头启动的前面一些帧光线太暗,直接跳过,后面的帧直接存储成yuv, 用pyuv打开

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

swap空间的增减方法

(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap

Linux音视频开发之一:用V4L2采集webcam摄像头数据mjpeg 和yuv - HelloWorld