فهرست منبع

Initial commit

JDierkse 5 سال پیش
کامیت
4546808c73

+ 15 - 0
.gitignore

@@ -0,0 +1,15 @@
+*.o.*
+*.d.*
+*.a.*
+.*.swp
+.AppleDouble
+.debug
+.gdbinit*
+gdb*
+core-*
+*-core
+core.*
+*.core
+CameraRecorder*
+!CameraRecorder.cc
+!CameraRecorder.ini

+ 3 - 0
.gitmodules

@@ -0,0 +1,3 @@
+[submodule "Makefiles"]
+	path = Makefiles
+	url = https://gogs.dierkse.nl/JDierkse/Makefiles

+ 1 - 0
API/Makefile

@@ -0,0 +1 @@
+../Makefile

+ 94 - 0
API/WebAPI.cpp

@@ -0,0 +1,94 @@
+#include "WebAPI.h"
+#include "Recorder/Recorder.h"
+#include "Recorder/DigooRecorder.h"
+#include "Recorder/FoscamRecorder.h"
+#include "Recorder/VStarCamRecorder.h"
+#include "Recorder/WatchBotRecorder.h"
+#include "Recorder/ZModoRecorder.h"
+#include <StringAlgorithm.h>
+#include <memory>
+
+
+namespace CameraRecorder {
+namespace API {
+
+// API Description
+
+// Status Query
+// Goal:	Provide status information based on POST data
+// Use:		DomoticaSite status update
+// URL:		http://<ip>/API/<action>/<brand>/<ipaddress>/<port>/<username>/<password>
+
+WebAPI::WebAPI()
+{
+}
+
+WebAPI::~WebAPI()
+{
+}
+
+std::string WebAPI::ProcessQuery(ctpl::thread_pool* pThreadPool, Http::HttpClient* pHttpClient, const std::string& path, const std::string& ffmpeg, const std::string& uri, const std::vector<Http::HttpPostData>& postData)
+{
+	std::string output;
+
+	std::vector<std::string> uriTokens = StringAlgorithm::split(uri, '/');
+
+	if (uriTokens.size() < 7)
+			return std::string();
+
+	std::string action = uriTokens[1];
+	std::string brand = uriTokens[2];
+
+	Recorder::Settings settings;
+	settings.Path = path;
+	settings.IpAddress = uriTokens[3];
+	settings.Port = uriTokens[4];
+	settings.Username = uriTokens[5];
+	settings.Password = uriTokens[6];
+
+	if (StringAlgorithm::iequals("Digoo", brand))
+	{
+		auto recorder = Recorder::DigooRecorder(pHttpClient, settings);
+		if (StringAlgorithm::iequals("Snapshot", action))
+			output = recorder.Snapshot(pThreadPool);
+		else if (StringAlgorithm::iequals("Video", action))
+			output = recorder.Video(pThreadPool, ffmpeg);
+	}
+	else if (StringAlgorithm::iequals("Foscam", brand))
+	{
+		auto recorder = Recorder::FoscamRecorder(pHttpClient, settings);
+		if (StringAlgorithm::iequals("Snapshot", action))
+			output = recorder.Snapshot(pThreadPool);
+		else if (StringAlgorithm::iequals("Video", action))
+			output = recorder.Video(pThreadPool, ffmpeg);
+	}
+	else if (StringAlgorithm::iequals("VStarCam", brand))
+	{
+		auto recorder = Recorder::VStarCamRecorder(pHttpClient, settings);
+		if (StringAlgorithm::iequals("Snapshot", action))
+			output = recorder.Snapshot(pThreadPool);
+		else if (StringAlgorithm::iequals("Video", action))
+			output = recorder.Video(pThreadPool, ffmpeg);
+	}
+	else if (StringAlgorithm::iequals("WatchBot", brand))
+	{
+		auto recorder = Recorder::WatchBotRecorder(pHttpClient, settings);
+		if (StringAlgorithm::iequals("Snapshot", action))
+			output = recorder.Snapshot(pThreadPool);
+		else if (StringAlgorithm::iequals("Video", action))
+			output = recorder.Video(pThreadPool, ffmpeg);
+	}
+	else if (StringAlgorithm::iequals("ZModo", brand))
+	{
+		auto recorder = Recorder::ZModoRecorder(pHttpClient, settings);
+		if (StringAlgorithm::iequals("Snapshot", action))
+			output = recorder.Snapshot(pThreadPool);
+		else if (StringAlgorithm::iequals("Video", action))
+			output = recorder.Video(pThreadPool, ffmpeg);
+	}
+
+	return output;
+}
+
+} // namespace API
+} // namespace CameraRecorder

+ 31 - 0
API/WebAPI.h

@@ -0,0 +1,31 @@
+#ifndef API_WEBAPI_H
+#define API_WEBAPI_H
+
+#include <HttpPostData.h>
+#include <ctpl_stl.h>
+#include <string>
+#include <vector>
+
+
+namespace Http {
+
+class HttpClient;
+
+} // namespace Http
+
+namespace CameraRecorder {
+namespace API {
+
+class WebAPI
+{
+public:
+	WebAPI();
+	~WebAPI();
+
+	static std::string ProcessQuery(ctpl::thread_pool* pThreadPool, Http::HttpClient* pHttpClient, const std::string& path, const std::string& ffmpeg, const std::string& uri, const std::vector<Http::HttpPostData>& postData);
+};
+
+} // namespace API
+} // namespace DomoticaServer
+
+#endif // API_WEBAPI_H

+ 66 - 0
Application/CameraRecorder.cc

@@ -0,0 +1,66 @@
+#include "API/WebAPI.h"
+#include <ctpl_stl.h>
+#include <INIReader.h>
+#include <Logging.h>
+#include <HttpClient.h>
+#include <HttpServer.h>
+#include <functional>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
+
+int main(int argc, char** argv)
+{
+	try
+	{
+		Logging::OpenLog();
+		Logging::SetLogMask(Logging::Severity::Debug);
+
+		INIReader iniReader("CameraRecorder.ini");
+		if (iniReader.ParseError() != 0)
+			throw std::runtime_error("Can't read CameraRecorder.ini.");
+
+		Logging::Log(Logging::Severity::Info, "Starting CameraRecorder");
+
+		int port = iniReader.GetInteger("CameraRecorder", "Port", 0);
+		if (port == 0)
+			throw std::runtime_error("Port directive missing in ini file.");
+
+		std::string path = iniReader.Get("CameraRecorder", "Path", "");
+		if (path.empty())
+			throw std::runtime_error("Path directive missing in ini file.");
+
+		std::string ffmpeg = iniReader.Get("CameraRecorder", "FFMPEG", "");
+		if (ffmpeg.empty())
+			throw std::runtime_error("FFMPEG directive missing in ini file.");
+
+		ctpl::thread_pool threadPool(6);
+		Http::HttpClient httpClient;
+		auto callback = std::bind(&CameraRecorder::API::WebAPI::ProcessQuery, &threadPool, &httpClient, path, ffmpeg, std::placeholders::_1, std::placeholders::_2);
+
+		Http::HttpServer server(port, callback);
+		Logging::Log(Logging::Severity::Info, "Startup Complete");
+		server.Wait();
+
+		Logging::Log(Logging::Severity::Info, "Stopping CameraRecorder...");
+
+		Logging::CloseLog();
+	}
+	catch (const std::exception& e)
+	{
+		std::cerr << "Exception caught" << std::endl;
+
+		std::stringstream ss;
+		ss << "Type : " << typeid(e).name() << std::endl;
+		ss << "ERROR: " << e.what() << std::endl;
+
+		Logging::Log(Logging::Severity::Error, ss.str());
+
+		Logging::CloseLog();
+
+		return -1;
+	}
+
+	return 0;
+}

+ 1 - 0
Application/Makefile

@@ -0,0 +1 @@
+../Makefile

+ 4 - 0
CameraRecorder.ini

@@ -0,0 +1,4 @@
+[CameraRecorder]
+Port =
+Target =
+FFMPEG =

+ 1 - 0
Libraries/CTPL

@@ -0,0 +1 @@
+../../Libraries/CTPL

+ 1 - 0
Libraries/Http

@@ -0,0 +1 @@
+../../Libraries/Http

+ 1 - 0
Libraries/Logging

@@ -0,0 +1 @@
+../../Libraries/Logging

+ 1 - 0
Libraries/Utilities

@@ -0,0 +1 @@
+../../Libraries/Utilities

+ 1 - 0
Libraries/inih

@@ -0,0 +1 @@
+../../Libraries/inih

+ 1 - 0
Makefile

@@ -0,0 +1 @@
+Makefiles/Makefile

+ 22 - 0
Makefile.conf

@@ -0,0 +1,22 @@
+#
+# Makefile.conf
+#
+
+LFLAGS += -lHttp -lcurl
+LFLAGS += -L$(ROOTPATH)/Libraries/Http/lib/$(ARCH)
+CFLAGS += -I$(ROOTPATH)/Libraries/Http/include
+
+LFLAGS += -lLogging
+LFLAGS += -L$(ROOTPATH)/Libraries/Logging/lib/$(ARCH)
+CFLAGS += -I$(ROOTPATH)/Libraries/Logging/include
+
+LFLAGS += -lUtilities
+LFLAGS += -L$(ROOTPATH)/Libraries/Utilities/lib/$(ARCH)
+CFLAGS += -I$(ROOTPATH)/Libraries/Utilities/include
+
+CFLAGS += -I$(ROOTPATH)/Libraries/CTPL
+CFLAGS += -I$(ROOTPATH)/Libraries/inih
+
+LFLAGS += -pthread -lm
+CFLAGS += -I$(ROOTPATH) -I$(ROOTPATH)/Libraries
+DEBUGDIR := .debug

+ 13 - 0
Makefile.target

@@ -0,0 +1,13 @@
+#
+# Makefile.target
+#
+
+CameraRecorder.$(ARCH): $(OBJECTS) Application/CameraRecorder.o.$(ARCH)
+	$(call build_target_arch,$@,$^)
+
+CameraRecorder:
+	$(call build_target,$@)
+
+.DEFAULT_GOAL := CameraRecorder
+
+TARGETS += CameraRecorder

+ 1 - 0
Makefiles

@@ -0,0 +1 @@
+Subproject commit 5e113555ebd4e049b287b3c94c4ef33e151ea4e6

+ 32 - 0
Recorder/DigooRecorder.cpp

@@ -0,0 +1,32 @@
+#include "DigooRecorder.h"
+#include <HttpClient.h>
+#include <Logging.h>
+
+
+namespace CameraRecorder {
+namespace Recorder {
+
+DigooRecorder::DigooRecorder(Http::HttpClient* pHttpClient, const Settings& settings) :
+	m_pHttpClient(pHttpClient),
+	m_settings(settings)
+{
+}
+
+DigooRecorder::~DigooRecorder()
+{
+}
+
+std::string DigooRecorder::Snapshot(ctpl::thread_pool* pThreadPool)
+{
+	Logging::Log(Logging::Severity::Debug, "DigooRecorder Snapshot");
+	return std::string();
+}
+
+std::string DigooRecorder::Video(ctpl::thread_pool* pThreadPool, const std::string& ffmpeg)
+{
+	Logging::Log(Logging::Severity::Debug, "DigooRecorder Video");
+	return std::string();
+}
+
+} // namespace Recorder
+} // namespace CameraRecorder

+ 34 - 0
Recorder/DigooRecorder.h

@@ -0,0 +1,34 @@
+#ifndef RECORDER_DIGOORECORDER_H
+#define RECORDER_DIGOORECORDER_H
+
+#include "Recorder.h"
+#include "Settings.h"
+
+
+namespace Http {
+
+class HttpClient;
+
+} // namespace Http
+
+namespace CameraRecorder {
+namespace Recorder {
+
+class DigooRecorder : public virtual Recorder
+{
+public:
+	DigooRecorder(Http::HttpClient* pHttpClient, const Settings& settings);
+	~DigooRecorder();
+
+	virtual std::string Snapshot(ctpl::thread_pool* pThreadPool) override;
+	virtual std::string Video(ctpl::thread_pool* pThreadPool, const std::string& ffmpeg) override;
+
+private:
+	Http::HttpClient* m_pHttpClient;
+	Settings m_settings;
+};
+
+} // namespace Recorder
+} // namespace CameraRecorder
+
+#endif // RECORDER_DIGOORECORDER_H

+ 71 - 0
Recorder/FoscamRecorder.cpp

@@ -0,0 +1,71 @@
+#include "FoscamRecorder.h"
+#include "Util.h"
+#include <HttpClient.h>
+#include <Logging.h>
+#include <fstream>
+#include <sstream>
+#include <thread>
+
+
+namespace CameraRecorder {
+namespace Recorder {
+
+FoscamRecorder::FoscamRecorder(Http::HttpClient* pHttpClient, const Settings& settings) :
+	m_pHttpClient(pHttpClient),
+	m_settings(settings)
+{
+}
+
+FoscamRecorder::~FoscamRecorder()
+{
+}
+
+std::string FoscamRecorder::Snapshot(ctpl::thread_pool* pThreadPool)
+{
+	Logging::Log(Logging::Severity::Debug, "FoscamRecorder Snapshot");
+	std::stringstream url;
+	url << "http://" << m_settings.IpAddress << ":" << m_settings.Port << "/cgi-bin/CGIProxy.fcgi?cmd=snapPicture2&usr=" << m_settings.Username << "&pwd=" << m_settings.Password;
+
+	std::stringstream fileName;
+	fileName << m_settings.Path << "/" << m_settings.IpAddress << "/" << GetDateTimeString() << ".jpg";
+
+	try
+	{
+		std::ofstream file(fileName.str());
+		file << m_pHttpClient->GetUrlContents(url.str());
+		file.close();
+	}
+	catch (const std::exception& e)
+	{
+		std::stringstream ss;
+		ss << "CameraDevice::PerformFoscamAction() - Error: " << e.what() << std::endl;
+		Logging::Log(Logging::Severity::Error, ss.str());
+		return std::string();
+	}
+
+	return fileName.str();
+}
+
+std::string FoscamRecorder::Video(ctpl::thread_pool* pThreadPool, const std::string& ffmpeg)
+{
+	Logging::Log(Logging::Severity::Debug, "FoscamRecorder Video");
+	int port = 65534;
+
+	std::stringstream url;
+	url << "rtsp://" << m_settings.Username << ":" << m_settings.Password << "@" << m_settings.IpAddress << ":" << port << "/videoSub";
+
+	std::stringstream fileName;
+	fileName << m_settings.Path << "/" << m_settings.IpAddress << "/" << GetDateTimeString() << ".mp4";
+
+	std::stringstream command;
+	command << ffmpeg << " -i \"" << url.str() << "\" -t 30 -c:v copy -an -strict experimental " << fileName.str() << " > /dev/null 2>&1";
+
+	std::string cmd(command.str());
+	Logging::Log(Logging::Severity::Debug, cmd);
+	pThreadPool->push( [cmd](int) { int retval = std::system(cmd.c_str()); } );
+
+	return fileName.str();
+}
+
+} // namespace Recorder
+} // namespace CameraRecorder

+ 34 - 0
Recorder/FoscamRecorder.h

@@ -0,0 +1,34 @@
+#ifndef RECORDER_FOSCAMRECORDER_H
+#define RECORDER_FOSCAMRECORDER_H
+
+#include "Recorder.h"
+#include "Settings.h"
+
+
+namespace Http {
+
+class HttpClient;
+
+} // namespace Http
+
+namespace CameraRecorder {
+namespace Recorder {
+
+class FoscamRecorder : public virtual Recorder
+{
+public:
+	FoscamRecorder(Http::HttpClient* pHttpClient, const Settings& settings);
+	~FoscamRecorder();
+
+	virtual std::string Snapshot(ctpl::thread_pool* pThreadPool) override;
+	virtual std::string Video(ctpl::thread_pool* pThreadPool, const std::string& ffmpeg) override;
+
+private:
+	Http::HttpClient* m_pHttpClient;
+	Settings m_settings;
+};
+
+} // namespace Recorder
+} // namespace CameraRecorder
+
+#endif // RECORDER_FOSCAMRECORDER_H

+ 1 - 0
Recorder/Makefile

@@ -0,0 +1 @@
+../Makefile

+ 16 - 0
Recorder/Recorder.cpp

@@ -0,0 +1,16 @@
+#include "Recorder.h"
+
+
+namespace CameraRecorder {
+namespace Recorder {
+
+Recorder::Recorder()
+{	
+}
+
+Recorder::~Recorder()
+{
+}
+
+} // namespace Recorder
+} // namespace CameraRecorder

+ 24 - 0
Recorder/Recorder.h

@@ -0,0 +1,24 @@
+#ifndef RECORDER_RECORDER_H
+#define RECORDER_RECORDER_H
+
+#include <ctpl_stl.h>
+#include <string>
+
+
+namespace CameraRecorder {
+namespace Recorder {
+
+class Recorder
+{
+public:
+	Recorder();
+	~Recorder();
+
+	virtual std::string Snapshot(ctpl::thread_pool* pThreadPool) = 0;
+	virtual std::string Video(ctpl::thread_pool* pThreadPool, const std::string& ffmpeg) = 0;
+};
+
+} // namespace Recorder
+} // namespace CameraRecorder
+
+#endif // RECORDER_RECORDER_H

+ 22 - 0
Recorder/Settings.h

@@ -0,0 +1,22 @@
+#ifndef RECORDER_SETTINGS_H
+#define RECORDER_SETTINGS_H
+
+#include <string>
+
+
+namespace CameraRecorder {
+namespace Recorder {
+
+struct Settings
+{
+	std::string Path;
+	std::string IpAddress;
+	std::string Port;
+	std::string Username;
+	std::string Password;
+};
+
+} // namespace Recorder
+} // namespace CameraRecorder
+
+#endif // RECORDER_SETTINGS_H

+ 25 - 0
Recorder/Util.cpp

@@ -0,0 +1,25 @@
+#include "Util.h"
+#include <array>
+#include <ctime>
+
+
+namespace CameraRecorder {
+namespace Recorder {
+
+std::string GetDateTimeString(bool omitSeparator)
+{
+	std::array<char, 17> buffer;
+	buffer.fill(0);
+	std::time_t t = std::time(nullptr);
+	std::tm* tm = std::localtime(&t);
+
+	if (omitSeparator)
+		strftime(buffer.data(), sizeof(buffer), "%Y%m%d%H%M%S", tm);
+	else
+		strftime(buffer.data(), sizeof(buffer), "%Y%m%d-%H%M%S", tm);
+
+	return buffer.data();
+}
+
+} // namespace Recorder
+} // namespace CameraRecorder

+ 15 - 0
Recorder/Util.h

@@ -0,0 +1,15 @@
+#ifndef RECORDER_UTIL_H
+#define RECORDER_UTIL_H
+
+#include <string>
+
+
+namespace CameraRecorder {
+namespace Recorder {
+
+std::string GetDateTimeString(bool omitSeparator = false);
+
+} // namespace Recorder
+} // namespace CameraRecorder
+
+#endif // RECORDER_UTIL_H

+ 74 - 0
Recorder/VStarCamRecorder.cpp

@@ -0,0 +1,74 @@
+#include "VStarCamRecorder.h"
+#include "Util.h"
+#include <HttpClient.h>
+#include <Logging.h>
+#include <fstream>
+#include <sstream>
+
+
+namespace CameraRecorder {
+namespace Recorder {
+
+VStarCamRecorder::VStarCamRecorder(Http::HttpClient* pHttpClient, const Settings& settings) :
+	m_pHttpClient(pHttpClient),
+	m_settings(settings)
+{
+}
+
+VStarCamRecorder::~VStarCamRecorder()
+{
+}
+
+std::string VStarCamRecorder::Snapshot(ctpl::thread_pool* pThreadPool)
+{
+	Logging::Log(Logging::Severity::Debug, "VStarCamRecorder Snapshot");
+	std::stringstream url;
+	url << "http://" << m_settings.Username << ":" << m_settings.Password << "@" << m_settings.IpAddress << ":" << m_settings.Port << "/img/snapshot.cgi?res=0";
+
+	std::stringstream fileName;
+	fileName << m_settings.Path << "/" << m_settings.IpAddress << "/VSTA000000XXXXX_0_" << GetDateTimeString(true);
+
+	int i = 0;
+	while (i < 3)
+	{
+		try
+		{
+			std::ofstream file(fileName.str() + "_" + std::to_string(i) + ".jpg");
+			file << m_pHttpClient->GetUrlContents(url.str());
+			file.close();
+		}
+		catch (const std::exception& e)
+		{
+			std::stringstream ss;
+			ss << "CameraDevice::PerformVStarCamAction() - Error: " << e.what() << std::endl;
+			Logging::Log(Logging::Severity::Error, ss.str());
+		}
+		++i;
+	}
+
+	return fileName.str() + "_0.jpg";
+}
+
+std::string VStarCamRecorder::Video(ctpl::thread_pool* pThreadPool, const std::string& ffmpeg)
+{
+	Logging::Log(Logging::Severity::Debug, "VStarCamRecorder Video");
+	int port = 10554;
+
+	std::stringstream url;
+	url << "rtsp://" << m_settings.Username << ":" << m_settings.Password << "@" << m_settings.IpAddress << ":" << port << "/udp/av0_0";
+
+	std::stringstream fileName;
+	fileName << m_settings.Path << "/" << m_settings.IpAddress << "/VSTA000000XXXXX_0_" << GetDateTimeString(true) << ".mp4";
+
+	std::stringstream command;
+	command << ffmpeg << " -i \"" << url.str() << "\" -t 30 -c:v copy -c:a aac -b:a 8k -strict experimental " << fileName.str() << " > /dev/null 2>&1";
+
+	std::string cmd(command.str());
+	Logging::Log(Logging::Severity::Debug, cmd);
+	pThreadPool->push( [cmd](int) { int retval = std::system(cmd.c_str()); } );
+
+	return fileName.str();
+}
+
+} // namespace Recorder
+} // namespace CameraRecorder

+ 34 - 0
Recorder/VStarCamRecorder.h

@@ -0,0 +1,34 @@
+#ifndef RECORDER_VSTARCAMRECORDER_H
+#define RECORDER_VSTARCAMRECORDER_H
+
+#include "Recorder.h"
+#include "Settings.h"
+
+
+namespace Http {
+
+class HttpClient;
+
+} // namespace Http
+
+namespace CameraRecorder {
+namespace Recorder {
+
+class VStarCamRecorder : public virtual Recorder
+{
+public:
+	VStarCamRecorder(Http::HttpClient* pHttpClient, const Settings& settings);
+	~VStarCamRecorder();
+
+	virtual std::string Snapshot(ctpl::thread_pool* pThreadPool) override;
+	virtual std::string Video(ctpl::thread_pool* pThreadPool, const std::string& ffmpeg) override;
+
+private:
+	Http::HttpClient* m_pHttpClient;
+	Settings m_settings;
+};
+
+} // namespace Recorder
+} // namespace CameraRecorder
+
+#endif // RECORDER_VSTARCAMRECORDER_H

+ 71 - 0
Recorder/WatchBotRecorder.cpp

@@ -0,0 +1,71 @@
+#include "WatchBotRecorder.h"
+#include "Util.h"
+#include <HttpClient.h>
+#include <Logging.h>
+#include <fstream>
+#include <sstream>
+
+
+namespace CameraRecorder {
+namespace Recorder {
+
+WatchBotRecorder::WatchBotRecorder(Http::HttpClient* pHttpClient, const Settings& settings) :
+	m_pHttpClient(pHttpClient),
+	m_settings(settings)
+{
+}
+
+WatchBotRecorder::~WatchBotRecorder()
+{
+}
+
+std::string WatchBotRecorder::Snapshot(ctpl::thread_pool* pThreadPool)
+{
+	Logging::Log(Logging::Severity::Debug, "WatchBotRecorder Snapshot");
+	std::stringstream url;
+	url << "http://" << m_settings.IpAddress << ":" << m_settings.Port << "/snapshot.cgi?user=" << m_settings.Username << "&pwd=" << m_settings.Password;
+
+	std::stringstream fileName;
+	fileName << m_settings.Path << "/" << m_settings.IpAddress << "/" << GetDateTimeString() << ".jpg";
+
+	try
+	{
+		std::ofstream file(fileName.str());
+		file << m_pHttpClient->GetUrlContents(url.str());
+		file.close();
+	}
+	catch (const std::exception& e)
+	{
+		std::stringstream ss;
+		ss << "CameraDevice::PerformWatchBotAction() - Error: " << e.what() << std::endl;
+		Logging::Log(Logging::Severity::Error, ss.str());
+		return std::string();
+	}
+
+	return fileName.str();
+}
+
+std::string WatchBotRecorder::Video(ctpl::thread_pool* pThreadPool, const std::string& ffmpeg)
+{
+	Logging::Log(Logging::Severity::Debug, "WatchBotRecorder Video");
+/*
+	std::stringstream url;
+	url << "http://" << m_settings.IpAddress << ":" << m_settings.Port << "/videostream.asf?user=" << m_settings.Username << "&pwd=" << m_settings.Password << "&resolution=64&rate=0";
+
+	std::stringstream fileName;
+	fileName << m_settings.Path << "/" << m_settings.IpAddress << "/" << GetDateTimeString() << ".mp4";
+
+	std::stringstream command;
+	command << "ffmpeg -i \"" << url.str() << "\" -t 30 -c:v copy -c:a aac -b:a 8k -strict experimental " << fileName.str() << " > /dev/null 2>&1";
+
+	std::string cmd(command.str());
+	Logging::Log(Logging::Severity::Debug, cmd);
+	pThreadPool->push( [cmd](int) { int retval = std::system(cmd.c_str()); } );
+
+	return fileName.str();
+*/
+	return std::string();
+}
+
+} // namespace Recorder
+} // namespace CameraRecorder

+ 34 - 0
Recorder/WatchBotRecorder.h

@@ -0,0 +1,34 @@
+#ifndef RECORDER_WATCHBOTRECORDER_H
+#define RECORDER_WATCHBOTRECORDER_H
+
+#include "Recorder.h"
+#include "Settings.h"
+
+
+namespace Http {
+
+class HttpClient;
+
+} // namespace Http
+
+namespace CameraRecorder {
+namespace Recorder {
+
+class WatchBotRecorder : public virtual Recorder
+{
+public:
+	WatchBotRecorder(Http::HttpClient* pHttpClient, const Settings& settings);
+	~WatchBotRecorder();
+
+	virtual std::string Snapshot(ctpl::thread_pool* pThreadPool) override;
+	virtual std::string Video(ctpl::thread_pool* pThreadPool, const std::string& ffmpeg) override;
+
+private:
+	Http::HttpClient* m_pHttpClient;
+	Settings m_settings;
+};
+
+} // namespace Recorder
+} // namespace CameraRecorder
+
+#endif // RECORDER_WATCHBOTRECORDER_H

+ 32 - 0
Recorder/ZModoRecorder.cpp

@@ -0,0 +1,32 @@
+#include "ZModoRecorder.h"
+#include <HttpClient.h>
+#include <Logging.h>
+
+
+namespace CameraRecorder {
+namespace Recorder {
+
+ZModoRecorder::ZModoRecorder(Http::HttpClient* pHttpClient, const Settings& settings) :
+	m_pHttpClient(pHttpClient),
+	m_settings(settings)
+{
+}
+
+ZModoRecorder::~ZModoRecorder()
+{
+}
+
+std::string ZModoRecorder::Snapshot(ctpl::thread_pool* pThreadPool)
+{
+	Logging::Log(Logging::Severity::Debug, "ZModoRecorder Snapshot");
+	return std::string();
+}
+
+std::string ZModoRecorder::Video(ctpl::thread_pool* pThreadPool, const std::string& ffmpeg)
+{
+	Logging::Log(Logging::Severity::Debug, "ZModoRecorder Video");
+	return std::string();
+}
+
+} // namespace Recorder
+} // namespace CameraRecorder

+ 34 - 0
Recorder/ZModoRecorder.h

@@ -0,0 +1,34 @@
+#ifndef RECORDER_ZMODORECORDER_H
+#define RECORDER_ZMODORECORDER_H
+
+#include "Recorder.h"
+#include "Settings.h"
+
+
+namespace Http {
+
+class HttpClient;
+
+} // namespace Http
+
+namespace CameraRecorder {
+namespace Recorder {
+
+class ZModoRecorder : public virtual Recorder
+{
+public:
+	ZModoRecorder(Http::HttpClient* pHttpClient, const Settings& settings);
+	~ZModoRecorder();
+
+	virtual std::string Snapshot(ctpl::thread_pool* pThreadPool) override;
+	virtual std::string Video(ctpl::thread_pool* pThreadPool, const std::string& ffmpeg) override;
+
+private:
+	Http::HttpClient* m_pHttpClient;
+	Settings m_settings;
+};
+
+} // namespace Recorder
+} // namespace CameraRecorder
+
+#endif // RECORDER_ZMODORECORDER_H