From 89b512311e1ba976eecbea524623c6b26b42ca65 Mon Sep 17 00:00:00 2001 From: Mr-Dave Date: Sun, 12 Jun 2022 16:07:42 -0600 Subject: [PATCH] Initial libcamera support --- configure.ac | 45 ++++- src/Makefile.am | 2 +- src/conf.cpp | 30 ++++ src/conf.hpp | 3 + src/libcam.cpp | 421 ++++++++++++++++++++++++++++++++++++++++++++ src/libcam.hpp | 80 +++++++++ src/motion_loop.cpp | 37 +++- src/motionplus.cpp | 6 + src/motionplus.hpp | 5 + 9 files changed, 623 insertions(+), 6 deletions(-) create mode 100644 src/libcam.cpp create mode 100644 src/libcam.hpp diff --git a/configure.ac b/configure.ac index f88fe7b2..37e9fbb8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ AC_INIT(motionplus, esyscmd(['./scripts/version.sh'])) AM_INIT_AUTOMAKE([subdir-objects foreign]) -CXXFLAGS="$CXXFLAGS -std=c++0x" +CXXFLAGS="$CXXFLAGS -std=c++17" AC_PROG_CC AC_PROG_CXX AC_GNU_SOURCE @@ -211,6 +211,48 @@ AS_IF([test "${MMAL}" = "no"], [ ) AM_CONDITIONAL([INC_MMAL_SRC],[test x${MMAL} = xyes]) +############################################################################## +### raspberry pi libcamera - Optional. +############################################################################## +AC_ARG_WITH([libcam], + AS_HELP_STRING([--with-libcam[=DIR]],[Compile with RasperryPi libcamera support]), + [LIBCAM="$withval"], + [LIBCAM="yes"] +) + +AS_IF([test "${LIBCAM}" = "no"], [ + AC_MSG_CHECKING(for LIBCAM) + AC_MSG_RESULT(skipped) + ],[ + AC_MSG_CHECKING(LIBCAM pkg-config path) + TEMP_PATH=$PKG_CONFIG_PATH + AS_IF([test "${LIBCAM}" != "yes"], [ + PKG_CONFIG_PATH=${LIBCAM}/lib/pkgconfig:$PKG_CONFIG_PATH + LIBCAM="yes" + ],[ + PKG_CONFIG_PATH=.:/usr/lib/pkgconfig + ] + ) + export PKG_CONFIG_PATH + AC_MSG_RESULT($PKG_CONFIG_PATH) + + AC_MSG_CHECKING(for LIBCAM) + AS_IF([pkg-config libcamera], [ + TEMP_CPPFLAGS="$TEMP_CPPFLAGS "`pkg-config --cflags libcamera` + TEMP_LIBS="$TEMP_LIBS "`pkg-config --libs libcamera` + AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_LIBCAM], [1], [Define to 1 if LIBCAM is around]) + ],[ + AC_MSG_RESULT([no]) + LIBCAM="no" + ] + ) + PKG_CONFIG_PATH=$TEMP_PATH + export PKG_CONFIG_PATH + ] +) +AM_CONDITIONAL([INC_MMAL_SRC],[test x${MMAL} = xyes]) + ############################################################################## ### Check for ffmpeg ############################################################################## @@ -534,6 +576,7 @@ echo "XSI error : $XSI_STRERROR" echo "webp support : $WEBP" echo "V4L2 support : $V4L2" echo "MMAL support : $MMAL" +echo "libcamera support : $LIBCAM" echo "FFmpeg support : $FFMPEG" echo " libavformat version : $FFMPEG_VER" echo "OpenCV : $OPENCV" diff --git a/src/Makefile.am b/src/Makefile.am index 45811a79..a3bb51c1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -32,5 +32,5 @@ motionplus_SOURCES = motionplus.cpp motion_loop.cpp logger.cpp conf.cpp util.cpp video_v4l2.cpp video_common.cpp video_loopback.cpp netcam.cpp jpegutils.cpp exif.cpp \ rotate.cpp draw.cpp event.cpp movie.cpp picture.cpp dbse.cpp \ webu.cpp webu_html.cpp webu_stream.cpp webu_json.cpp webu_post.cpp webu_file.cpp \ - mmalcam.cpp $(MMAL_SRC) + libcam.cpp mmalcam.cpp $(MMAL_SRC) diff --git a/src/conf.cpp b/src/conf.cpp index 28d91fb1..504a416f 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -64,6 +64,8 @@ struct ctx_parm config_parms[] = { {"netcam_userpass", PARM_TYP_STRING, PARM_CAT_02, WEBUI_LEVEL_ADVANCED }, {"mmalcam_name", PARM_TYP_STRING, PARM_CAT_02, WEBUI_LEVEL_ADVANCED }, {"mmalcam_params", PARM_TYP_STRING, PARM_CAT_02, WEBUI_LEVEL_ADVANCED }, + {"libcam_name", PARM_TYP_STRING, PARM_CAT_02, WEBUI_LEVEL_ADVANCED }, + {"libcam_params", PARM_TYP_STRING, PARM_CAT_02, WEBUI_LEVEL_ADVANCED }, {"width", PARM_TYP_INT, PARM_CAT_03, WEBUI_LEVEL_LIMITED }, {"height", PARM_TYP_INT, PARM_CAT_03, WEBUI_LEVEL_LIMITED }, @@ -825,6 +827,32 @@ static void conf_edit_mmalcam_params(struct ctx_cam *cam, std::string &parm, enu MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,"%s:%s","mmalcam_params",_("mmalcam_params")); } +static void conf_edit_libcam_name(struct ctx_cam *cam, std::string &parm, enum PARM_ACT pact) +{ + if (pact == PARM_ACT_DFLT) { + cam->conf->libcam_name = ""; + } else if (pact == PARM_ACT_SET) { + cam->conf->libcam_name = parm; + } else if (pact == PARM_ACT_GET) { + parm = cam->conf->libcam_name; + } + return; + MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,"%s:%s","libcam_name",_("libcam_name")); +} + +static void conf_edit_libcam_params(struct ctx_cam *cam, std::string &parm, enum PARM_ACT pact) +{ + if (pact == PARM_ACT_DFLT) { + cam->conf->libcam_params = ""; + } else if (pact == PARM_ACT_SET) { + cam->conf->libcam_params = parm; + } else if (pact == PARM_ACT_GET) { + parm = cam->conf->libcam_params; + } + return; + MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,"%s:%s","libcam_params",_("libcam_params")); +} + static void conf_edit_width(struct ctx_cam *cam, std::string &parm, enum PARM_ACT pact) { int parm_in; @@ -2890,6 +2918,8 @@ static void conf_edit_cat02(struct ctx_cam *cam, std::string parm_nm } else if (parm_nm == "netcam_userpass") { conf_edit_netcam_userpass(cam, parm_val, pact); } else if (parm_nm == "mmalcam_name") { conf_edit_mmalcam_name(cam, parm_val, pact); } else if (parm_nm == "mmalcam_params") { conf_edit_mmalcam_params(cam, parm_val, pact); + } else if (parm_nm == "libcam_name") { conf_edit_libcam_name(cam, parm_val, pact); + } else if (parm_nm == "libcam_params") { conf_edit_libcam_params(cam, parm_val, pact); } } diff --git a/src/conf.hpp b/src/conf.hpp index 9e48b89e..0ed4a272 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -47,6 +47,9 @@ std::string mmalcam_name; std::string mmalcam_params; + std::string libcam_name; + std::string libcam_params; + /* Image processing configuration parameters */ int width; int height; diff --git a/src/libcam.cpp b/src/libcam.cpp new file mode 100644 index 00000000..1924aec9 --- /dev/null +++ b/src/libcam.cpp @@ -0,0 +1,421 @@ +/* + * This file is part of MotionPlus. + * + * MotionPlus is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MotionPlus is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MotionPlus. If not, see . + * + * Copyright 2020-2022 MotionMrDave@gmail.com + * + */ + +/* TODO: + * 1. Identify parameters for camera and print them out to the user. + * 2. Apply user provided parameters to the camera. + * 3. Determine if we need to have multiple requests or buffers. + * (The current logic is just a single request and buffer but + * this may need to change to allow for multiple requests or buffers + * so as to reduce latency. As of now, it is kept simple with + * a single request and buffer.) + * 4. Need to determine flags for designating start up, shutdown + * etc. and possibly add mutex locking. Startup currently has + * a SLEEP to allow for initialization but this should change + * 5. The shutdown is not completely cleaning up something with libcamera. + * Upon a stop of a camera and starting it again libcamera reports + * that the v4l2 devices are busy and can not be opened. Then it + * seg aborts upon us. + */ +#include "motionplus.hpp" +#include "conf.hpp" +#include "logger.hpp" +#include "util.hpp" +#include "rotate.hpp" +#include "video_common.hpp" +#include "libcam.hpp" + +#ifdef HAVE_LIBCAM + +using namespace libcamera; + +void cls_libcam::cam_start_params(ctx_cam *ptr) +{ + int indx; + + MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "Starting."); + + camctx = ptr; + + camctx->libcam->params = (ctx_params*)mymalloc(sizeof(struct ctx_params)); + camctx->libcam->params->update_params = true; + util_parms_parse(camctx->libcam->params, camctx->conf->libcam_params); + + for (indx = 0; indx < camctx->libcam->params->params_count; indx++) { + MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s : %s" + ,camctx->libcam->params->params_array[indx].param_name + ,camctx->libcam->params->params_array[indx].param_value + ); + } + MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "Finished."); + +} + +int cls_libcam::cam_start_mgr() +{ + int retcd; + std::string camid; + libcamera::Size picsz; + + MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "Starting."); + + cam_mgr = std::make_unique(); + retcd = cam_mgr->start(); + if (retcd != 0) { + MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO + , "Error starting camera manager. Return code: %d",retcd); + return retcd; + } + started_mgr = true; + + MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "cam_mgr started."); + + if (camctx->conf->libcam_name == "camera0"){ + camid = cam_mgr->cameras()[0]->id(); + } else { + MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO + , "Invalid libcam_name '%s'. The only name supported is 'camera0' " + ,camctx->conf->libcam_name.c_str()); + return -1; + } + camera = cam_mgr->get(camid); + camera->acquire(); + started_aqr = true; + + MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "Finished."); + + return 0; +} + +int cls_libcam::cam_start_config() +{ + int retcd; + libcamera::Size picsz; + + MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "Starting."); + + config = camera->generateConfiguration({ StreamRole::Viewfinder }); + config->at(0).pixelFormat = PixelFormat::fromString("YUV420"); + + config->at(0).size.width = camctx->conf->width; + config->at(0).size.height = camctx->conf->height; + config->at(0).bufferCount = 1; + + retcd = config->validate(); + if (retcd == CameraConfiguration::Adjusted) { + if (config->at(0).pixelFormat != PixelFormat::fromString("YUV420")) { + MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO + , "Pixel format was adjusted to %s." + , config->at(0).pixelFormat.toString().c_str()); + return -1; + } + } else if (retcd == CameraConfiguration::Invalid) { + MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO + , "Error setting configuration"); + return -1; + } + + if ((config->at(0).size.width != (unsigned int)camctx->conf->width) || + (config->at(0).size.height != (unsigned int)camctx->conf->height)) { + MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO + , "Image size adjusted from %d x %d to %d x %d" + , camctx->conf->width + , camctx->conf->height + , config->at(0).size.width + , config->at(0).size.height); + } + + camctx->imgs.width = config->at(0).size.width; + camctx->imgs.height = config->at(0).size.height; + camctx->imgs.size_norm = (camctx->imgs.width * camctx->imgs.height * 3) / 2; + camctx->imgs.motionsize = camctx->imgs.width * camctx->imgs.height; + + camera->configure(config.get()); + + MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "Finished."); + + return 0; +} + +int cls_libcam::req_add(Request *request) +{ + int retcd; + retcd = camera->queueRequest(request); + return retcd; +} + +int cls_libcam::cam_start_req() +{ + int retcd, bytes; + + MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "Starting."); + + camera->requestCompleted.connect(this, &cls_libcam::req_complete); + frmbuf = std::make_unique(camera); + + retcd = frmbuf->allocate(config->at(0).stream()); + if (retcd < 0) { + MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO + , "Buffer allocation error."); + return -1; + } + + std::unique_ptr request = camera->createRequest(); + if (!request) { + MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO + , "Create request error."); + return -1; + } + + Stream *stream = config->at(0).stream(); + const std::vector> &buffers = + frmbuf->buffers(stream); + const std::unique_ptr &buffer = buffers[0]; + + retcd = request->addBuffer(stream, buffer.get()); + if (retcd < 0) { + MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO + , "Add buffer for request error."); + return -1; + } + + const FrameBuffer::Plane &plane0 = buffer->planes()[0]; + bytes = plane0.length; + if (buffer->planes().size() > 1) { + const FrameBuffer::Plane &plane1 = buffer->planes()[1]; + bytes += plane1.length; + } + if (buffer->planes().size() > 2) { + const FrameBuffer::Plane &plane2 = buffer->planes()[2]; + bytes += plane2.length; + } + if (bytes > camctx->imgs.size_norm){ + MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO + , "Image size error. Usual size is %d but planes size is %d" + ,camctx->imgs.size_norm, bytes); + return -1; + } + + membuf.buf = (uint8_t *)mmap(NULL,bytes, PROT_READ + , MAP_SHARED, plane0.fd.get(), 0); + membuf.bufsz = bytes; + + requests.push_back(std::move(request)); + started_req = true; + + MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "Finished."); + + return 0; +} + +int cls_libcam::cam_start_capture() +{ + int retcd; + + MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "Starting."); + + retcd = camera->start(&this->controls); + if (retcd) { + MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO + , "Failed to start capture."); + return -1; + } + controls.clear(); + + for (std::unique_ptr &request : requests) { + retcd = req_add(request.get()); + if (retcd < 0) { + MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO + , "Failed to queue request."); + if (started_cam) { + camera->stop(); + } + return -1; + } + } + MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "Finished."); + + return 0; +} + +void cls_libcam::req_complete(Request *request) +{ + if (request->status() == Request::RequestCancelled) { + return; + } + req_queue.push(request); +} + +int cls_libcam::cam_start(ctx_cam *cam) +{ + int retcd; + + started_cam = false; + started_mgr = false; + started_aqr = false; + started_req = false; + + cam_start_params(cam); + + retcd = cam_start_mgr(); + if (retcd != 0) { + return -1; + } + + retcd = cam_start_config(); + if (retcd != 0) { + return -1; + } + + retcd = cam_start_req(); + if (retcd != 0) { + return -1; + } + + retcd = cam_start_capture(); + if (retcd != 0) { + return -1; + } + + SLEEP(2,0); + + started_cam = true; + + MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "Started all."); + + return 0; +} + +void cls_libcam::cam_stop() +{ + util_parms_free(camctx->libcam->params); + myfree(&camctx->libcam->params); + camctx->libcam->params = NULL; + + if (started_aqr) { + camera->stop(); + } + if (started_req) { + camera->requestCompleted.disconnect(this, &cls_libcam::req_complete); + while (req_queue.empty() == false) { + req_queue.pop(); + } + requests.clear(); + + frmbuf->free(config->at(0).stream()); + frmbuf.reset(); + } + + controls.clear(); + + if (started_aqr){ + camera->release(); + camera.reset(); + } + if (started_mgr) { + cam_mgr->stop(); + cam_mgr.reset(); + } + MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "Stopped."); +} + +/* get the image from libcam */ +int cls_libcam::cam_next(ctx_image_data *img_data) +{ + int indx; + + if (started_cam == false) { + return -1; + } + + /* Allow time for request to finish.*/ + indx=0; + while ((req_queue.empty() == true) && (indx < 50)) { + SLEEP(0,2000) + indx++; + } + + if (req_queue.empty() == false) { + Request *request = this->req_queue.front(); + + memcpy(img_data->image_norm, membuf.buf, membuf.bufsz); + + this->req_queue.pop(); + + request->reuse(Request::ReuseBuffers); + req_add(request); + return 0; + + } else { + return -1; + } +} + +#endif + +/** initialize and start libcam */ +int libcam_start(ctx_cam *cam) +{ + #ifdef HAVE_LIBCAM + int retcd; + MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "Starting experimental libcamera ."); + MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "EXPECT crashes and hung processes!!!"); + cam->libcam = new cls_libcam; + retcd = cam->libcam->cam_start(cam); + return retcd; + #else + (void)cam; + return -1; + #endif +} + +/** close and stop libcam */ +void libcam_cleanup(struct ctx_cam *cam) +{ + #ifdef HAVE_LIBCAM + cam->libcam->cam_stop(); + delete cam->libcam; + cam->libcam = nullptr; + #else + (void)cam; + #endif +} + +/** get next image from libcam */ +int libcam_next(struct ctx_cam *cam, struct ctx_image_data *img_data) +{ + #ifdef HAVE_LIBCAM + int retcd; + + if (cam->libcam == nullptr){ + return -1; + } + retcd = cam->libcam->cam_next(img_data); + if (retcd == 0) { + rotate_map(cam, img_data); + } + return retcd; + #else + (void)cam; + (void)img_data; + return -1; + #endif +} + diff --git a/src/libcam.hpp b/src/libcam.hpp new file mode 100644 index 00000000..f70da659 --- /dev/null +++ b/src/libcam.hpp @@ -0,0 +1,80 @@ +/* + * This file is part of MotionPlus. + * + * MotionPlus is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MotionPlus is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MotionPlus. If not, see . + * + * Copyright 2020-2022 MotionMrDave@gmail.com + */ + +#ifndef _INCLUDE_LIBCAM_HPP_ +#define _INCLUDE_LIBCAM_HPP_ + #ifdef HAVE_LIBCAM + #include + #include + #include + + using namespace libcamera; + + /* Buffers and sizes for planes of image*/ + struct ctx_imgmap { + uint8_t *buf; + int bufsz; + }; + + class cls_libcam { + public: + cls_libcam(){}; + ~cls_libcam(){}; + int cam_start(ctx_cam *cam); + void cam_stop(); + int cam_next(ctx_image_data *img_data); + private: + ctx_cam *camctx; + ctx_params *params; + + std::unique_ptr cam_mgr; + std::shared_ptr camera; + std::unique_ptr config; + std::unique_ptr frmbuf; + std::vector> requests; + + std::queue req_queue; + ControlList controls; + ctx_imgmap membuf; + bool started_cam; + bool started_mgr; + bool started_aqr; + bool started_req; + + void cam_start_params(ctx_cam *ptr); + int cam_start_mgr(); + int cam_start_config(); + int cam_start_req(); + int cam_start_capture(); + void req_complete(Request *request); + int req_add(Request *request); + }; + #else + class cls_libcam { + public: + cls_libcam(){}; + ~cls_libcam(){}; + } + #endif + + int libcam_start (ctx_cam *cam); + int libcam_next (ctx_cam *cam, ctx_image_data *img_data); + void libcam_cleanup (ctx_cam *cam); + +#endif /* _INCLUDE_LIBCAM_HPP_ */ diff --git a/src/motion_loop.cpp b/src/motion_loop.cpp index 770dc6d0..b8af42a1 100644 --- a/src/motion_loop.cpp +++ b/src/motion_loop.cpp @@ -26,6 +26,7 @@ #include "rotate.hpp" #include "movie.hpp" #include "mmalcam.hpp" +#include "libcam.hpp" #include "video_v4l2.hpp" #include "video_loopback.hpp" #include "netcam.hpp" @@ -364,7 +365,6 @@ static void mlp_mask_privacy(struct ctx_cam *cam) void mlp_cam_close(struct ctx_cam *cam) { - if (cam->mmalcam) { MOTION_LOG(INF, TYPE_VIDEO, NO_ERRNO,_("calling mmalcam_cleanup")); mmalcam_cleanup(cam->mmalcam); @@ -373,6 +373,12 @@ void mlp_cam_close(struct ctx_cam *cam) return; } + if (cam->libcam) { + libcam_cleanup(cam); + cam->running_cam = false; + return; + } + if (cam->netcam) { /* This also cleans up high resolution */ MOTION_LOG(INF, TYPE_VIDEO, NO_ERRNO,_("calling netcam_cleanup")); @@ -386,7 +392,7 @@ void mlp_cam_close(struct ctx_cam *cam) return; } - MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO,_("No Camera device cleanup (MMAL, Netcam, V4L2)")); + MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO,_("No Camera device cleanup")); return; } @@ -413,6 +419,16 @@ int mlp_cam_start(struct ctx_cam *cam) return dev; } + if (cam->camera_type == CAMERA_TYPE_LIBCAM) { + MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO,_("Opening Libcam")); + dev = libcam_start(cam); + if (dev < 0) { + libcam_cleanup(cam); + MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO,_("Libcam failed to open")); + } + return dev; + } + if (cam->camera_type == CAMERA_TYPE_NETCAM) { MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO,_("Opening Netcam")); dev = netcam_setup(cam); @@ -433,7 +449,7 @@ int mlp_cam_start(struct ctx_cam *cam) } MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO - ,_("No Camera device specified (MMAL, Netcam, V4L2)")); + ,_("No Camera device specified")); return dev; } @@ -448,6 +464,14 @@ int mlp_cam_next(struct ctx_cam *cam, struct ctx_image_data *img_data) return mmalcam_next(cam, img_data); } + if (cam->camera_type == CAMERA_TYPE_LIBCAM) { + if (cam->libcam == NULL) { + return NETCAM_GENERAL_ERROR; + } + return libcam_next(cam, img_data); + } + + if (cam->camera_type == CAMERA_TYPE_NETCAM) { if (cam->video_dev == -1) { return NETCAM_GENERAL_ERROR; @@ -472,6 +496,11 @@ static int init_camera_type(struct ctx_cam *cam) return 0; } + if (cam->conf->libcam_name != "") { + cam->camera_type = CAMERA_TYPE_LIBCAM; + return 0; + } + if (cam->conf->netcam_url != "") { cam->camera_type = CAMERA_TYPE_NETCAM; return 0; @@ -483,7 +512,7 @@ static int init_camera_type(struct ctx_cam *cam) } MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO - , _("Unable to determine camera type (MMAL, Netcam, V4L2)")); + , _("Unable to determine camera type")); return -1; } diff --git a/src/motionplus.cpp b/src/motionplus.cpp index 2272bc2a..dd5f74c3 100644 --- a/src/motionplus.cpp +++ b/src/motionplus.cpp @@ -344,6 +344,12 @@ static void motion_ntc(void) MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,_("mmal : not available")); #endif + #ifdef HAVE_LIBCAM + MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,_("libcam : available")); + #else + MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,_("libcam : not available")); + #endif + #ifdef HAVE_MYSQL MOTION_LOG(DBG, TYPE_DB, NO_ERRNO,_("mysql : available")); #else diff --git a/src/motionplus.hpp b/src/motionplus.hpp index 600a8e7a..019cef2e 100644 --- a/src/motionplus.hpp +++ b/src/motionplus.hpp @@ -84,6 +84,8 @@ struct ctx_algsec; struct ctx_config; struct ctx_v4l2cam; +class cls_libcam; + #define MYFFVER (LIBAVFORMAT_VERSION_MAJOR * 1000)+LIBAVFORMAT_VERSION_MINOR #define THRESHOLD_TUNE_LENGTH 256 @@ -143,6 +145,7 @@ enum CAMERA_TYPE { CAMERA_TYPE_UNKNOWN, CAMERA_TYPE_V4L2, CAMERA_TYPE_MMAL, + CAMERA_TYPE_LIBCAM, CAMERA_TYPE_NETCAM }; @@ -307,6 +310,8 @@ struct ctx_cam { struct ctx_movie *movie_timelapse; struct ctx_stream stream; + class cls_libcam *libcam; + FILE *extpipe; int extpipe_open; bool algsec_inuse; /*Bool for whether we have secondary detection*/