mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-10-12 23:03:52 -04:00
feat: remove the Libvlc capture method from the daemons and build
Ffmpeg reaches the same network cameras libvlc did, so carrying a second streaming library -- with its own threading, its own bugs and its own build dependency -- buys nothing. The preceding migration moves existing monitors to Ffmpeg first, so Monitor::Load cannot meet a type it no longer recognises, which is Fatal and would stop zmc rather than skip the monitor. Drops LibvlcCamera, the LIBVLC camera type and its parse and construction arms, LIBVLC_SRC and Camera::IsLibvlc, the ZM_NO_LIBVLC option and the libvlc probe, and the HAVE_LIBVLC and HAVE_VLC_VLC_H defines. Since the camera resolved libvlc through dlopen rather than linking it, this removes a headers-only build dependency and a runtime one, and nothing else needed relinking. kOptionSeparators stays -- the Ffmpeg path still parses the Options field with it -- but it is no longer a contract shared between two readers, so its comment and the test that pins its behaviour are reworded rather than removed. Built clean from a fresh cmake configure, which no longer lists libVLC among optional libraries; unit suite passes (147 cases, 12531 assertions); no libvlc strings remain in zmc. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015Y6FieTwEXuLhhR4e2yiax
This commit is contained in:
1 parent
7db7430914
commit
b0645ae512
10 files changed
+7
-502
No files matched your search
@@ -260,8 +260,6 @@ set(ZM_MYSQL_ENGINE "InnoDB" CACHE STRING
|
||||
set(ZM_NO_MMAP "OFF" CACHE BOOL
|
||||
"Set to ON to not use mmap shared memory. Shouldn't be enabled unless you
|
||||
experience problems with the shared memory. default: OFF")
|
||||
set(ZM_NO_LIBVLC "OFF" CACHE BOOL
|
||||
"Set to ON to skip libvlc checks and force building ZM without libvlc. default: OFF")
|
||||
set(ZM_NO_LIBVNC "OFF" CACHE BOOL
|
||||
"Set to ON to skip libvnc checks and force building ZM without libvnc. default: OFF")
|
||||
set(ZM_NO_CURL "OFF" CACHE BOOL
|
||||
@@ -630,26 +628,6 @@ if(FFMPEG_EXECUTABLE)
|
||||
mark_as_advanced(FFMPEG_EXECUTABLE)
|
||||
endif()
|
||||
|
||||
# Do not check for libvlc if ZM_NO_LIBVLC is on
|
||||
if(NOT ZM_NO_LIBVLC)
|
||||
# libvlc (using find_library and find_path)
|
||||
find_library(LIBVLC_LIBRARIES vlc)
|
||||
if(LIBVLC_LIBRARIES)
|
||||
set(HAVE_LIBVLC 1)
|
||||
#list(APPEND ZM_BIN_LIBS "${LIBVLC_LIBRARIES}")
|
||||
find_path(LIBVLC_INCLUDE_DIR "vlc/vlc.h")
|
||||
if(LIBVLC_INCLUDE_DIR)
|
||||
list(APPEND ZM_INCLUDE_DIRS "${LIBVLC_INCLUDE_DIR}")
|
||||
set(CMAKE_REQUIRED_INCLUDES "${LIBVLC_INCLUDE_DIR}")
|
||||
endif()
|
||||
mark_as_advanced(FORCE LIBVLC_LIBRARIES LIBVLC_INCLUDE_DIR)
|
||||
check_include_file("vlc/vlc.h" HAVE_VLC_VLC_H)
|
||||
set(optlibsfound "${optlibsfound} libVLC")
|
||||
else()
|
||||
set(optlibsnotfound "${optlibsnotfound} libVLC")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT ZM_NO_LIBVNC)
|
||||
# libvncclient (using find_library and find_path)
|
||||
find_library(LIBVNC_LIBRARIES vncclient)
|
||||
|
||||
@@ -33,7 +33,6 @@ set(ZM_BIN_SRC_FILES
|
||||
zm_group.cpp
|
||||
zm_image.cpp
|
||||
zm_jpeg.cpp
|
||||
zm_libvlc_camera.cpp
|
||||
zm_libvnc_camera.cpp
|
||||
zm_local_camera.cpp
|
||||
zm_monitor.cpp
|
||||
|
||||
+1
-2
@@ -36,7 +36,7 @@ class ZMPacket;
|
||||
//
|
||||
class Camera {
|
||||
protected:
|
||||
typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC, VNC_SRC } SourceType;
|
||||
typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, CURL_SRC, VNC_SRC } SourceType;
|
||||
|
||||
const Monitor *monitor;
|
||||
SourceType type;
|
||||
@@ -95,7 +95,6 @@ class Camera {
|
||||
bool IsRemote() const { return type == REMOTE_SRC; }
|
||||
bool IsFile() const { return type == FILE_SRC; }
|
||||
bool IsFfmpeg() const { return type == FFMPEG_SRC; }
|
||||
bool IsLibvlc() const { return type == LIBVLC_SRC; }
|
||||
bool IscURL() const { return type == CURL_SRC; }
|
||||
bool IsVNC() const { return type == VNC_SRC; }
|
||||
unsigned int Width() const { return width; }
|
||||
|
||||
@@ -1,346 +0,0 @@
|
||||
/*
|
||||
* ZoneMinder Libvlc Camera Class Implementation, $Date$, $Revision$
|
||||
* Copyright (C) 2001-2008 Philip Coombes
|
||||
*
|
||||
* This program 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 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "zm_libvlc_camera.h"
|
||||
|
||||
#include "zm_packet.h"
|
||||
#include "zm_signal.h"
|
||||
#include <dlfcn.h>
|
||||
|
||||
#if HAVE_LIBVLC
|
||||
static void *libvlc_lib = nullptr;
|
||||
static void (*libvlc_media_player_release_f)(libvlc_media_player_t* ) = nullptr;
|
||||
static void (*libvlc_media_release_f)(libvlc_media_t* ) = nullptr;
|
||||
static void (*libvlc_release_f)(libvlc_instance_t* ) = nullptr;
|
||||
static void (*libvlc_media_player_stop_f)(libvlc_media_player_t* ) = nullptr;
|
||||
static libvlc_instance_t* (*libvlc_new_f)(int, const char* const *) = nullptr;
|
||||
static void (*libvlc_log_set_f)(libvlc_instance_t*, libvlc_log_cb, void *) = nullptr;
|
||||
static libvlc_media_t* (*libvlc_media_new_location_f)(libvlc_instance_t*, const char*) = nullptr;
|
||||
static libvlc_media_player_t* (*libvlc_media_player_new_from_media_f)(libvlc_media_t*) = nullptr;
|
||||
static void (*libvlc_video_set_format_f)(libvlc_media_player_t*, const char*, unsigned, unsigned, unsigned) = nullptr;
|
||||
static void (*libvlc_video_set_callbacks_f)(libvlc_media_player_t*, libvlc_video_lock_cb, libvlc_video_unlock_cb, libvlc_video_display_cb, void*) = nullptr;
|
||||
static int (*libvlc_media_player_play_f)(libvlc_media_player_t *) = nullptr;
|
||||
static const char* (*libvlc_errmsg_f)(void) = nullptr;
|
||||
static const char* (*libvlc_get_version_f)(void) = nullptr;
|
||||
|
||||
void bind_libvlc_symbols() {
|
||||
if(libvlc_lib != nullptr) // Safe-check
|
||||
return;
|
||||
|
||||
libvlc_lib = dlopen("libvlc.so", RTLD_LAZY | RTLD_GLOBAL);
|
||||
if(!libvlc_lib) {
|
||||
Error("Error loading libvlc: %s", dlerror());
|
||||
return;
|
||||
}
|
||||
|
||||
*(void**) (&libvlc_media_player_release_f) = dlsym(libvlc_lib, "libvlc_media_player_release");
|
||||
*(void**) (&libvlc_media_release_f) = dlsym(libvlc_lib, "libvlc_media_release");
|
||||
*(void**) (&libvlc_release_f) = dlsym(libvlc_lib, "libvlc_release");
|
||||
*(void**) (&libvlc_media_player_stop_f) = dlsym(libvlc_lib, "libvlc_media_player_stop");
|
||||
*(void**) (&libvlc_new_f) = dlsym(libvlc_lib, "libvlc_new");
|
||||
*(void**) (&libvlc_log_set_f) = dlsym(libvlc_lib, "libvlc_log_set");
|
||||
*(void**) (&libvlc_media_new_location_f) = dlsym(libvlc_lib, "libvlc_media_new_location");
|
||||
*(void**) (&libvlc_media_player_new_from_media_f) = dlsym(libvlc_lib, "libvlc_media_player_new_from_media");
|
||||
*(void**) (&libvlc_video_set_format_f) = dlsym(libvlc_lib, "libvlc_video_set_format");
|
||||
*(void**) (&libvlc_video_set_callbacks_f) = dlsym(libvlc_lib, "libvlc_video_set_callbacks");
|
||||
*(void**) (&libvlc_media_player_play_f) = dlsym(libvlc_lib, "libvlc_media_player_play");
|
||||
*(void**) (&libvlc_errmsg_f) = dlsym(libvlc_lib, "libvlc_errmsg");
|
||||
*(void**) (&libvlc_get_version_f) = dlsym(libvlc_lib, "libvlc_get_version");
|
||||
}
|
||||
// Do all the buffer checking work here to avoid unnecessary locking
|
||||
void* LibvlcLockBuffer(void* opaque, void** planes) {
|
||||
LibvlcPrivateData* data = reinterpret_cast<LibvlcPrivateData*>(opaque);
|
||||
data->mutex.lock();
|
||||
|
||||
uint8_t* buffer = data->buffer;
|
||||
data->buffer = data->prevBuffer;
|
||||
data->prevBuffer = buffer;
|
||||
|
||||
*planes = data->buffer;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void LibvlcUnlockBuffer(void* opaque, void* picture, void *const *planes) {
|
||||
LibvlcPrivateData* data = reinterpret_cast<LibvlcPrivateData*>(opaque);
|
||||
|
||||
bool newFrame = false;
|
||||
for( size_t i=0; i < data->bufferSize; i++ ) {
|
||||
if ( data->buffer[i] != data->prevBuffer[i] ) {
|
||||
newFrame = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
data->mutex.unlock();
|
||||
|
||||
time_t now;
|
||||
time(&now);
|
||||
// Return frames slightly faster than 1fps (if time() supports greater than one second resolution)
|
||||
if ( newFrame || difftime(now, data->prevTime) >= 0.8 ) {
|
||||
data->prevTime = now;
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(data->newImageMutex);
|
||||
data->newImage = true;
|
||||
}
|
||||
data->newImageCv.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
LibvlcCamera::LibvlcCamera(
|
||||
const Monitor *monitor,
|
||||
const std::string &p_path,
|
||||
const std::string &p_user,
|
||||
const std::string &p_pass,
|
||||
const std::string &p_method,
|
||||
const std::string &p_options,
|
||||
int p_width,
|
||||
int p_height,
|
||||
int p_colours,
|
||||
int p_brightness,
|
||||
int p_contrast,
|
||||
int p_hue,
|
||||
int p_colour,
|
||||
bool p_capture,
|
||||
bool p_record_audio
|
||||
) :
|
||||
Camera(
|
||||
monitor,
|
||||
LIBVLC_SRC,
|
||||
p_width,
|
||||
p_height,
|
||||
p_colours,
|
||||
ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours),
|
||||
p_brightness,
|
||||
p_contrast,
|
||||
p_hue,
|
||||
p_colour,
|
||||
p_capture,
|
||||
p_record_audio
|
||||
),
|
||||
mPath(p_path),
|
||||
mUser(UriEncode(p_user)),
|
||||
mPass(UriEncode(p_pass)),
|
||||
mMethod(p_method),
|
||||
mOptions(p_options) {
|
||||
mLibvlcInstance = nullptr;
|
||||
mLibvlcMedia = nullptr;
|
||||
mLibvlcMediaPlayer = nullptr;
|
||||
mLibvlcData.buffer = nullptr;
|
||||
mLibvlcData.prevBuffer = nullptr;
|
||||
mOptArgV = nullptr;
|
||||
|
||||
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
||||
if ( zm_is_rgb32(pixelFormat) ) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_BGRA;
|
||||
pixelFormat = zm_pixformat_from_colours(colours, subpixelorder);
|
||||
mTargetChroma = "RV32";
|
||||
mBpp = 4;
|
||||
} else if ( zm_is_rgb24(pixelFormat) ) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_BGR;
|
||||
pixelFormat = zm_pixformat_from_colours(colours, subpixelorder);
|
||||
mTargetChroma = "RV24";
|
||||
mBpp = 3;
|
||||
} else if ( pixelFormat == AV_PIX_FMT_GRAY8 ) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
||||
pixelFormat = zm_pixformat_from_colours(colours, subpixelorder);
|
||||
mTargetChroma = "GREY";
|
||||
mBpp = 1;
|
||||
} else {
|
||||
mBpp = 0;
|
||||
Panic("Unexpected colours: %d",colours);
|
||||
}
|
||||
|
||||
if ( capture ) {
|
||||
Initialise();
|
||||
}
|
||||
}
|
||||
|
||||
LibvlcCamera::~LibvlcCamera() {
|
||||
if ( capture ) {
|
||||
Terminate();
|
||||
}
|
||||
|
||||
mLibvlcData.newImageCv.notify_all(); // to unblock on termination (zm_terminate)
|
||||
|
||||
if ( mLibvlcMediaPlayer != nullptr ) {
|
||||
(*libvlc_media_player_release_f)(mLibvlcMediaPlayer);
|
||||
mLibvlcMediaPlayer = nullptr;
|
||||
}
|
||||
if ( mLibvlcMedia != nullptr ) {
|
||||
(*libvlc_media_release_f)(mLibvlcMedia);
|
||||
mLibvlcMedia = nullptr;
|
||||
}
|
||||
if ( mLibvlcInstance != nullptr ) {
|
||||
(*libvlc_release_f)(mLibvlcInstance);
|
||||
mLibvlcInstance = nullptr;
|
||||
}
|
||||
if (libvlc_lib) {
|
||||
dlclose(libvlc_lib);
|
||||
libvlc_lib = nullptr;
|
||||
}
|
||||
if ( mOptArgV != nullptr ) {
|
||||
delete[] mOptArgV;
|
||||
}
|
||||
}
|
||||
|
||||
void LibvlcCamera::Initialise() {
|
||||
bind_libvlc_symbols();
|
||||
}
|
||||
|
||||
void LibvlcCamera::Terminate() {
|
||||
if (libvlc_media_player_stop_f && mLibvlcMediaPlayer)
|
||||
(*libvlc_media_player_stop_f)(mLibvlcMediaPlayer);
|
||||
if ( mLibvlcData.buffer ) {
|
||||
zm_freealigned(mLibvlcData.buffer);
|
||||
mLibvlcData.buffer = nullptr;
|
||||
}
|
||||
|
||||
if ( mLibvlcData.prevBuffer ) {
|
||||
zm_freealigned(mLibvlcData.prevBuffer);
|
||||
mLibvlcData.prevBuffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int LibvlcCamera::PrimeCapture() {
|
||||
Debug(1, "Priming capture from %s, libvlc version %s", mPath.c_str(), (*libvlc_get_version_f)());
|
||||
|
||||
opVect = Split(Options(), kOptionSeparators);
|
||||
|
||||
Debug(1, "Method: '%s'", Method().c_str());
|
||||
|
||||
// Set transport method as specified by method field, rtpUni is default
|
||||
if ( Method() == "rtpMulti" )
|
||||
opVect.push_back("--rtsp-mcast");
|
||||
else if ( Method() == "rtpRtsp" )
|
||||
opVect.push_back("--rtsp-tcp");
|
||||
else if ( Method() == "rtpRtspHttp" )
|
||||
opVect.push_back("--rtsp-http");
|
||||
|
||||
opVect.push_back("--no-audio");
|
||||
|
||||
if ( opVect.size() > 0 ) {
|
||||
mOptArgV = new char*[opVect.size()];
|
||||
Debug(2, "Number of Options: %zu", opVect.size());
|
||||
for (size_t i=0; i< opVect.size(); i++) {
|
||||
opVect[i] = TrimSpaces(opVect[i]);
|
||||
mOptArgV[i] = (char *)opVect[i].c_str();
|
||||
Debug(2, "set option %zu to '%s'", i, opVect[i].c_str());
|
||||
}
|
||||
}
|
||||
|
||||
mLibvlcInstance = (*libvlc_new_f)(opVect.size(), (const char* const*)mOptArgV);
|
||||
if ( mLibvlcInstance == nullptr ) {
|
||||
Error("Unable to create libvlc instance due to: %s", (*libvlc_errmsg_f)());
|
||||
return -1;
|
||||
}
|
||||
(*libvlc_log_set_f)(mLibvlcInstance, LibvlcCamera::log_callback, nullptr);
|
||||
|
||||
// recreate the path with encoded authentication info
|
||||
if( mUser.length() > 0 ) {
|
||||
std::string mMaskedPath = remove_authentication(mPath);
|
||||
|
||||
std::string protocol = StringToUpper(mPath.substr(0, 4));
|
||||
if ( protocol == "RTSP" ) {
|
||||
// build the actual uri string with encoded parameters (from the user and pass fields)
|
||||
mPath = StringToLower(protocol) + "://" + mUser + ":" + mPass + "@" + mMaskedPath.substr(7, std::string::npos);
|
||||
Debug(1, "Rebuilt URI with encoded parameters: '%s'", mPath.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
mLibvlcMedia = (*libvlc_media_new_location_f)(mLibvlcInstance, mPath.c_str());
|
||||
if ( mLibvlcMedia == nullptr ) {
|
||||
Error("Unable to open input %s due to: %s", mPath.c_str(), (*libvlc_errmsg_f)());
|
||||
return -1;
|
||||
}
|
||||
|
||||
mLibvlcMediaPlayer = (*libvlc_media_player_new_from_media_f)(mLibvlcMedia);
|
||||
if ( mLibvlcMediaPlayer == nullptr ) {
|
||||
Error("Unable to create player for %s due to: %s", mPath.c_str(), (*libvlc_errmsg_f)());
|
||||
return -1;
|
||||
}
|
||||
|
||||
(*libvlc_video_set_format_f)(mLibvlcMediaPlayer, mTargetChroma.c_str(), width, height, width * mBpp);
|
||||
(*libvlc_video_set_callbacks_f)(mLibvlcMediaPlayer, &LibvlcLockBuffer, &LibvlcUnlockBuffer, nullptr, &mLibvlcData);
|
||||
|
||||
mLibvlcData.bufferSize = static_cast<size_t>(width) * height * mBpp;
|
||||
// Libvlc wants 32 byte alignment for images (should in theory do this for all image lines)
|
||||
mLibvlcData.buffer = (uint8_t*)zm_mallocaligned(64, mLibvlcData.bufferSize);
|
||||
mLibvlcData.prevBuffer = (uint8_t*)zm_mallocaligned(64, mLibvlcData.bufferSize);
|
||||
|
||||
mLibvlcData.newImage = false;
|
||||
|
||||
(*libvlc_media_player_play_f)(mLibvlcMediaPlayer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int LibvlcCamera::PreCapture() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Should not return -1 as cancels capture. Always wait for image if available.
|
||||
int LibvlcCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
|
||||
// newImage is a mutex/condition based flag to tell us when there is an image available
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(mLibvlcData.newImageMutex);
|
||||
mLibvlcData.newImageCv.wait(lck, [&] { return mLibvlcData.newImage || zm_terminate; });
|
||||
mLibvlcData.newImage = false;
|
||||
}
|
||||
|
||||
if (zm_terminate)
|
||||
return 0;
|
||||
|
||||
mLibvlcData.mutex.lock();
|
||||
zm_packet->image->Assign(width, height, colours, subpixelorder, mLibvlcData.buffer, mLibvlcData.bufferSize);
|
||||
zm_packet->packet->stream_index = mVideoStreamId;
|
||||
zm_packet->stream = mVideoStream;
|
||||
mLibvlcData.mutex.unlock();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int LibvlcCamera::PostCapture() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LibvlcCamera::log_callback(void *ptr, int level, const libvlc_log_t *ctx, const char *fmt, va_list vargs) {
|
||||
Logger *log = Logger::fetch();
|
||||
int log_level = Logger::NOLOG;
|
||||
if ( level == LIBVLC_ERROR ) {
|
||||
log_level = Logger::WARNING; // ffmpeg outputs a lot of errors that don't really affect anything.
|
||||
//log_level = Logger::ERROR;
|
||||
} else if ( level == LIBVLC_WARNING ) {
|
||||
log_level = Logger::INFO;
|
||||
//log_level = Logger::WARNING;
|
||||
} else if ( level == LIBVLC_NOTICE ) {
|
||||
log_level = Logger::DEBUG1;
|
||||
//log_level = Logger::INFO;
|
||||
} else if ( level == LIBVLC_DEBUG ) {
|
||||
log_level = Logger::DEBUG3;
|
||||
} else {
|
||||
Error("Unknown log level %d", level);
|
||||
}
|
||||
|
||||
if ( log ) {
|
||||
char logString[8192];
|
||||
vsnprintf(logString, sizeof(logString) - 1, fmt, vargs);
|
||||
log->logPrint(false, __FILE__, __LINE__, log_level, "%s", logString);
|
||||
}
|
||||
}
|
||||
#endif // HAVE_LIBVLC
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* ZoneMinder Libvlc Camera Class Interface, $Date$, $Revision$
|
||||
* Copyright (C) 2001-2008 Philip Coombes
|
||||
*
|
||||
* This program 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 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef ZM_LIBVLC_CAMERA_H
|
||||
#define ZM_LIBVLC_CAMERA_H
|
||||
|
||||
#include "zm_camera.h"
|
||||
#include "zm_utils.h"
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
#if HAVE_LIBVLC
|
||||
|
||||
#if HAVE_VLC_VLC_H
|
||||
#include "vlc/vlc.h"
|
||||
#endif
|
||||
|
||||
// Used by libvlc callbacks
|
||||
struct LibvlcPrivateData {
|
||||
uint8_t* buffer;
|
||||
uint8_t* prevBuffer;
|
||||
time_t prevTime;
|
||||
size_t bufferSize;
|
||||
std::mutex mutex;
|
||||
|
||||
bool newImage;
|
||||
std::mutex newImageMutex;
|
||||
std::condition_variable newImageCv;
|
||||
};
|
||||
|
||||
class LibvlcCamera : public Camera {
|
||||
private:
|
||||
static void log_callback( void *ptr, int level, const libvlc_log_t *ctx, const char *format, va_list vargs );
|
||||
protected:
|
||||
std::string mPath;
|
||||
std::string mUser;
|
||||
std::string mPass;
|
||||
std::string mMethod;
|
||||
std::string mOptions;
|
||||
StringVector opVect; // mOptArgV will point into opVect so it needs to hang around
|
||||
char **mOptArgV;
|
||||
LibvlcPrivateData mLibvlcData;
|
||||
std::string mTargetChroma;
|
||||
uint8_t mBpp;
|
||||
|
||||
libvlc_instance_t *mLibvlcInstance;
|
||||
libvlc_media_t *mLibvlcMedia;
|
||||
libvlc_media_player_t *mLibvlcMediaPlayer;
|
||||
|
||||
public:
|
||||
LibvlcCamera( const Monitor *monitor, const std::string &path, const std::string &user,const std::string &pass, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio );
|
||||
~LibvlcCamera();
|
||||
|
||||
const std::string &Path() const { return mPath; }
|
||||
const std::string &Options() const { return mOptions; }
|
||||
const std::string &Method() const { return mMethod; }
|
||||
|
||||
void Initialise();
|
||||
void Terminate();
|
||||
|
||||
int PrimeCapture() override;
|
||||
int PreCapture() override;
|
||||
int Capture(std::shared_ptr<ZMPacket> &p) override;
|
||||
int PostCapture() override;
|
||||
int Close() override { return 0; };
|
||||
};
|
||||
|
||||
#endif // HAVE_LIBVLC
|
||||
#endif // ZM_LIBVLC_CAMERA_H
|
||||
@@ -39,10 +39,6 @@
|
||||
#include "zm_local_camera.h"
|
||||
#endif // ZM_HAS_V4L2
|
||||
|
||||
#if HAVE_LIBVLC
|
||||
#include "zm_libvlc_camera.h"
|
||||
#endif // HAVE_LIBVLC
|
||||
|
||||
#if HAVE_LIBVNC
|
||||
#include "zm_libvnc_camera.h"
|
||||
#endif // HAVE_LIBVNC
|
||||
@@ -126,7 +122,6 @@ std::string CameraType_Strings[] = {
|
||||
"Remote",
|
||||
"File",
|
||||
"Ffmpeg",
|
||||
"LibVLC",
|
||||
"LibCURL",
|
||||
"VNC"
|
||||
};
|
||||
@@ -392,8 +387,6 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
||||
type = REMOTE;
|
||||
} else if (!strcmp(dbrow[col], "File")) {
|
||||
type = FILE;
|
||||
} else if (!strcmp(dbrow[col], "Libvlc")) {
|
||||
type = LIBVLC;
|
||||
} else if (!strcmp(dbrow[col], "VNC")) {
|
||||
type = VNC;
|
||||
} else {
|
||||
@@ -877,36 +870,6 @@ void Monitor::LoadCamera() {
|
||||
);
|
||||
break;
|
||||
}
|
||||
case LIBVLC: {
|
||||
// libvlc reads the same network streams ffmpeg does, through a second
|
||||
// library with its own build dependency, its own threading and its own
|
||||
// bugs. Ffmpeg is the maintained path and every url that works here works
|
||||
// there, so this capture method is on its way out.
|
||||
Warning("Monitor %u (%s): the Libvlc capture method is deprecated and will be removed. "
|
||||
"Change this monitor to Type 'Ffmpeg', keeping the same Source Path",
|
||||
id, name.c_str());
|
||||
#if HAVE_LIBVLC
|
||||
camera = zm::make_unique<LibvlcCamera>(this,
|
||||
path.c_str(),
|
||||
user,
|
||||
pass,
|
||||
method,
|
||||
options,
|
||||
camera_width,
|
||||
camera_height,
|
||||
colours,
|
||||
brightness,
|
||||
contrast,
|
||||
hue,
|
||||
colour,
|
||||
purpose == CAPTURE,
|
||||
record_audio
|
||||
);
|
||||
#else // HAVE_LIBVLC
|
||||
Error("You must have vlc libraries installed to use vlc cameras for monitor %d", id);
|
||||
#endif // HAVE_LIBVLC
|
||||
break;
|
||||
}
|
||||
case VNC: {
|
||||
#if HAVE_LIBVNC
|
||||
camera = zm::make_unique<VncCamera>(this,
|
||||
|
||||
@@ -130,7 +130,6 @@ class Monitor : public std::enable_shared_from_this<Monitor> {
|
||||
REMOTE,
|
||||
FILE,
|
||||
FFMPEG,
|
||||
LIBVLC,
|
||||
LIBCURL,
|
||||
VNC,
|
||||
} CameraType;
|
||||
|
||||
+2
-3
@@ -99,9 +99,8 @@ std::string ClientAddress(const std::string &remote_addr,
|
||||
|
||||
// Separator characters between entries in a monitor's Options field. The ui
|
||||
// offers a textarea, so an option may be on its own line as well as after a
|
||||
// comma. Both consumers - av_dict_parse_string() for Ffmpeg and Split() for
|
||||
// Libvlc - take a set of separator characters and skip empty entries, so a
|
||||
// blank line, crlf or a trailing newline all just work.
|
||||
// comma. av_dict_parse_string() takes a set of separator characters and skips
|
||||
// empty entries, so a blank line, crlf or a trailing newline all just work.
|
||||
constexpr const char kOptionSeparators[] = ",\r\n";
|
||||
|
||||
inline bool StartsWith(const std::string &haystack, const std::string &needle) {
|
||||
|
||||
+4
-3
@@ -129,9 +129,10 @@ TEST_CASE("Split (string delimiter)") {
|
||||
items = Split("a b c", " ", 2);
|
||||
REQUIRE(items == std::vector<std::string>{"a", "b c"});
|
||||
|
||||
// LibvlcCamera splits the monitor Options field this way. The field is a
|
||||
// textarea, so an option may be on its own line rather than after a comma,
|
||||
// and runs of separators (a blank line, or crlf) must not become entries.
|
||||
// A monitor's Options field is a textarea, so an entry may be on its own
|
||||
// line rather than after a comma, and runs of separators (a blank line, or
|
||||
// crlf) must not become empty entries. Split() is no longer the reader of
|
||||
// that field, but this is the contract kOptionSeparators is chosen for.
|
||||
items = Split("--rate=1\n--no-audio", kOptionSeparators);
|
||||
REQUIRE(items == std::vector<std::string>{"--rate=1", "--no-audio"});
|
||||
|
||||
|
||||
@@ -38,8 +38,6 @@
|
||||
#cmakedefine HAVE_MYSQL_OPT_SSL_MODE 1
|
||||
#cmakedefine HAVE_MYSQL_OPT_SSL_VERIFY_SERVER_CERT 1
|
||||
#cmakedefine HAVE_LIBAVUTIL_HWCONTEXT_H 1
|
||||
#cmakedefine HAVE_LIBVLC 1
|
||||
#cmakedefine HAVE_VLC_VLC_H 1
|
||||
#cmakedefine HAVE_LIBVNC 1
|
||||
#cmakedefine HAVE_RFB_RFB_H 1
|
||||
#cmakedefine HAVE_LIBJWT 1
|
||||
|
||||
Reference in new issue
Block a user