Bladeren bron

Add MultiSnapshot

JDierkse 5 jaren geleden
bovenliggende
commit
6afdf1a387

+ 108 - 94
API/WebAPI.cpp

@@ -1,94 +1,108 @@
-#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
+#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];
+
+	int numberOfImages = 5;
+	if (uriTokens.size() > 7)
+		numberOfImages = stoi(uriTokens[7]);
+
+	if (StringAlgorithm::iequals("Digoo", brand))
+	{
+		auto recorder = Recorder::DigooRecorder(pHttpClient, settings);
+		if (StringAlgorithm::iequals("Snapshot", action))
+			output = recorder.Snapshot(pThreadPool);
+		else if (StringAlgorithm::iequals("MultiSnapshot", action))
+				output = recorder.MultiSnapshot(pThreadPool, numberOfImages);
+		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("MultiSnapshot", action))
+			output = recorder.MultiSnapshot(pThreadPool, numberOfImages);
+		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("MultiSnapshot", action))
+			output = recorder.MultiSnapshot(pThreadPool, numberOfImages);
+		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("MultiSnapshot", action))
+			output = recorder.MultiSnapshot(pThreadPool, numberOfImages);
+		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("MultiSnapshot", action))
+			output = recorder.MultiSnapshot(pThreadPool, numberOfImages);
+		else if (StringAlgorithm::iequals("Video", action))
+			output = recorder.Video(pThreadPool, ffmpeg);
+	}
+
+	return output;
+}
+
+} // namespace API
+} // namespace CameraRecorder

+ 70 - 66
Application/CameraRecorder.cc

@@ -1,66 +1,70 @@
-#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;
-}
+#include "API/WebAPI.h"
+#include <ctpl_stl.h>
+#include <INIReader.h>
+#include <Logging.h>
+#include <HttpClient.h>
+#include <HttpServer.h>
+#include <filesystem/path.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 pathString = iniReader.Get("CameraRecorder", "Path", "");
+		if (pathString.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.");
+
+		filesystem::path path(pathString);
+		path = path.make_absolute();
+
+		ctpl::thread_pool threadPool(6);
+		Http::HttpClient httpClient;
+		auto callback = std::bind(&CameraRecorder::API::WebAPI::ProcessQuery, &threadPool, &httpClient, path.str(), 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;
+}

+ 4 - 4
CameraRecorder.ini

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

+ 1 - 0
Libraries/Image

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

+ 1 - 0
Libraries/filesystem

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

+ 5 - 0
Makefile.conf

@@ -14,7 +14,12 @@ LFLAGS += -lUtilities
 LFLAGS += -L$(ROOTPATH)/Libraries/Utilities/lib/$(ARCH)
 CFLAGS += -I$(ROOTPATH)/Libraries/Utilities/include
 
+LFLAGS += -lImage -ljpeg
+LFLAGS += -L$(ROOTPATH)/Libraries/Image/lib/$(ARCH)
+CFLAGS += -I$(ROOTPATH)/Libraries/Image/include
+
 CFLAGS += -I$(ROOTPATH)/Libraries/CTPL
+CFLAGS += -I$(ROOTPATH)/Libraries/filesystem
 CFLAGS += -I$(ROOTPATH)/Libraries/inih
 
 LFLAGS += -pthread

+ 38 - 32
Recorder/DigooRecorder.cpp

@@ -1,32 +1,38 @@
-#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
+#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::MultiSnapshot(ctpl::thread_pool* pThreadPool, int numberOfImages)
+{
+	Logging::Log(Logging::Severity::Debug, "DigooRecorder MultiSnapshot");
+	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

+ 35 - 34
Recorder/DigooRecorder.h

@@ -1,34 +1,35 @@
-#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
+#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 MultiSnapshot(ctpl::thread_pool* pThreadPool, int numberOfImages) 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

+ 118 - 71
Recorder/FoscamRecorder.cpp

@@ -1,71 +1,118 @@
-#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
+#include "FoscamRecorder.h"
+#include "Util.h"
+#include <HttpClient.h>
+#include <JpegImageCollection.h>
+#include <Logging.h>
+#include <filesystem/path.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::string dateTimeString = GetDateTimeString();
+	std::string fileName = GetFileName(m_settings, "jpg", dateTimeString);
+
+	Http::HttpClient* pHttpClient = m_pHttpClient;
+	Settings settings = m_settings;
+	pThreadPool->push( [pHttpClient, settings, dateTimeString](int) { FoscamRecorder::Snapshots(pHttpClient, settings, dateTimeString, 1); } );
+
+	return fileName;
+}
+
+std::string FoscamRecorder::MultiSnapshot(ctpl::thread_pool* pThreadPool, int numberOfImages)
+{
+	Logging::Log(Logging::Severity::Debug, "FoscamRecorder MultiSnapshot");
+
+	std::string dateTimeString = GetDateTimeString();
+	std::string fileName = GetFileName(m_settings, "jpg", dateTimeString);
+
+	Http::HttpClient* pHttpClient = m_pHttpClient;
+	Settings settings = m_settings;
+	pThreadPool->push( [pHttpClient, settings, dateTimeString, numberOfImages](int) { FoscamRecorder::Snapshots(pHttpClient, settings, dateTimeString, numberOfImages); } );
+
+	return fileName;
+}
+
+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::string dateTimeString = GetDateTimeString();
+	std::string fileName = GetFileName(m_settings, "mp4", dateTimeString);
+
+	std::stringstream command;
+	command << ffmpeg << " -i \"" << url.str() << "\" -t 30 -c:v copy -an -strict experimental " << fileName << " > /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;
+}
+
+std::string FoscamRecorder::GetFileName(const Settings& settings, const std::string& extension, const std::string& dateTimeString)
+{
+	filesystem::path fileLocation(settings.Path);
+	fileLocation = fileLocation / filesystem::path(settings.IpAddress);
+
+	std::string fileName = std::string(dateTimeString).append(".").append(extension);
+	filesystem::path fullFileName = fileLocation / filesystem::path(fileName);
+
+	return fullFileName.str();
+}
+
+void FoscamRecorder::Snapshots(Http::HttpClient* pHttpClient, const Settings& settings, std::string dateTimeString, int numberOfImages)
+{
+	std::vector<Image::JpegImage> images;
+
+	for (int i = 0; i < numberOfImages; ++i)
+		images.push_back(FoscamRecorder::Snapshot(pHttpClient, settings));
+
+	Image::JpegImageCollection collection(images);
+	collection.Save(FoscamRecorder::GetFileName(settings, "jpg", dateTimeString));
+}
+
+Image::JpegImage FoscamRecorder::Snapshot(Http::HttpClient* pHttpClient, const Settings& settings)
+{
+	std::stringstream url;
+	url << "http://" << settings.IpAddress << ":" << settings.Port << "/cgi-bin/CGIProxy.fcgi?cmd=snapPicture2&usr=" << settings.Username << "&pwd=" << settings.Password;
+
+	Image::JpegImage image;
+
+	try
+	{
+		image.SetImageData(pHttpClient->GetUrlContents(url.str()));
+	}
+	catch (const std::exception& e)
+	{
+		std::stringstream ss;
+		ss << "FoscamRecorder::Snapshot() - Error: " << e.what() << std::endl;
+		Logging::Log(Logging::Severity::Error, ss.str());
+	}
+
+	return image;
+}
+
+} // namespace Recorder
+} // namespace CameraRecorder

+ 41 - 34
Recorder/FoscamRecorder.h

@@ -1,34 +1,41 @@
-#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
+#ifndef RECORDER_FOSCAMRECORDER_H
+#define RECORDER_FOSCAMRECORDER_H
+
+#include "Recorder.h"
+#include "Settings.h"
+#include <JpegImage.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 MultiSnapshot(ctpl::thread_pool* pThreadPool, int numberOfImages) override;
+	virtual std::string Video(ctpl::thread_pool* pThreadPool, const std::string& ffmpeg) override;
+
+private:
+	static std::string GetFileName(const Settings& settings, const std::string& extension, const std::string& dateTimeString);
+	static void Snapshots(Http::HttpClient* pHttpClient, const Settings& settings, std::string dateTimeString, int numberOfImages);
+	static Image::JpegImage Snapshot(Http::HttpClient* pHttpClient, const Settings& settings);
+
+private:
+	Http::HttpClient* m_pHttpClient;
+	Settings m_settings;
+};
+
+} // namespace Recorder
+} // namespace CameraRecorder
+
+#endif // RECORDER_FOSCAMRECORDER_H

+ 16 - 16
Recorder/Recorder.cpp

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

+ 25 - 24
Recorder/Recorder.h

@@ -1,24 +1,25 @@
-#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
+#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 MultiSnapshot(ctpl::thread_pool* pThreadPool, int numberOfImages) = 0;
+	virtual std::string Video(ctpl::thread_pool* pThreadPool, const std::string& ffmpeg) = 0;
+};
+
+} // namespace Recorder
+} // namespace CameraRecorder
+
+#endif // RECORDER_RECORDER_H

+ 22 - 22
Recorder/Settings.h

@@ -1,22 +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
+#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 - 25
Recorder/Util.cpp

@@ -1,25 +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
+#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 - 15
Recorder/Util.h

@@ -1,15 +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
+#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

+ 108 - 74
Recorder/VStarCamRecorder.cpp

@@ -1,74 +1,108 @@
-#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 -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
+#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::string dateTimeString = GetDateTimeString(true);
+	std::string fileName = GetFileName(m_settings, "jpg", dateTimeString, 0);
+
+	Http::HttpClient* pHttpClient = m_pHttpClient;
+	Settings settings = m_settings;
+	pThreadPool->push( [pHttpClient, settings, dateTimeString](int) { VStarCamRecorder::Snapshots(pHttpClient, settings, dateTimeString, 1); } );
+
+	return fileName;
+}
+
+std::string VStarCamRecorder::MultiSnapshot(ctpl::thread_pool* pThreadPool, int numberOfImages)
+{
+	Logging::Log(Logging::Severity::Debug, "VStarCamRecorder MultiSnapshot");
+
+	std::string dateTimeString = GetDateTimeString(true);
+	std::string fileName = GetFileName(m_settings, "jpg", dateTimeString, 0);
+
+	Http::HttpClient* pHttpClient = m_pHttpClient;
+	Settings settings = m_settings;
+	pThreadPool->push( [pHttpClient, settings, dateTimeString, numberOfImages](int) { VStarCamRecorder::Snapshots(pHttpClient, settings, dateTimeString, numberOfImages); } );
+
+	return fileName;
+}
+
+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::string dateTimeString = GetDateTimeString(true);
+	std::string fileName = GetFileName(m_settings, "mp4", dateTimeString, 0);
+
+	std::stringstream command;
+	command << ffmpeg << " -i \"" << url.str() << "\" -t 30 -c:v copy -c:a aac -strict experimental " << fileName << " > /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;
+}
+
+std::string VStarCamRecorder::GetFileName(const Settings& settings, const std::string& extension, const std::string& dateTimeString, int imageIndex)
+{
+	filesystem::path fileLocation(settings.Path);
+	fileLocation = fileLocation / filesystem::path(settings.IpAddress);
+
+	std::string fileName = std::string("VSTA000000XXXXX_0_").append(dateTimeString).append("_").append(std::to_string(imageIndex)).append(".").append(extension);
+	filesystem::path fullFileName = fileLocation / filesystem::path(fileName);
+
+	return fullFileName.str();
+}
+
+void VStarCamRecorder::Snapshots(Http::HttpClient* pHttpClient, const Settings& settings, std::string dateTimeString, int numberOfImages)
+{
+	for (int i = 0; i < numberOfImages; ++i)
+		VStarCamRecorder::Snapshot(pHttpClient, settings, VStarCamRecorder::GetFileName(settings, "jpg", dateTimeString, i));
+}
+
+void VStarCamRecorder::Snapshot(Http::HttpClient* pHttpClient, const Settings& settings, const filesystem::path& fileName)
+{
+	std::stringstream url;
+	url << "http://" << settings.Username << ":" << settings.Password << "@" << settings.IpAddress << ":" << settings.Port << "/img/snapshot.cgi?res=0";
+
+	try
+	{
+		std::ofstream file(fileName.str());
+		file << pHttpClient->GetUrlContents(url.str());
+		file.close();
+	}
+	catch (const std::exception& e)
+	{
+		std::stringstream ss;
+		ss << "VStarCamRecorder::Snapshot() - Error: " << e.what() << std::endl;
+		Logging::Log(Logging::Severity::Error, ss.str());
+	}
+}
+
+} // namespace Recorder
+} // namespace CameraRecorder

+ 41 - 34
Recorder/VStarCamRecorder.h

@@ -1,34 +1,41 @@
-#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
+#ifndef RECORDER_VSTARCAMRECORDER_H
+#define RECORDER_VSTARCAMRECORDER_H
+
+#include "Recorder.h"
+#include "Settings.h"
+#include <filesystem/path.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 MultiSnapshot(ctpl::thread_pool* pThreadPool, int numberOfImages) override;
+	virtual std::string Video(ctpl::thread_pool* pThreadPool, const std::string& ffmpeg) override;
+
+private:
+	static std::string GetFileName(const Settings& settings, const std::string& extension, const std::string& dateTimeString, int imageIndex);
+	static void Snapshots(Http::HttpClient* pHttpClient, const Settings& settings, std::string dateTimeString, int numberOfImages);
+	static void Snapshot(Http::HttpClient* pHttpClient, const Settings& settings, const filesystem::path& fileName);
+
+private:
+	Http::HttpClient* m_pHttpClient;
+	Settings m_settings;
+};
+
+} // namespace Recorder
+} // namespace CameraRecorder
+
+#endif // RECORDER_VSTARCAMRECORDER_H

+ 117 - 69
Recorder/WatchBotRecorder.cpp

@@ -1,69 +1,117 @@
-#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;
-
-	std::stringstream fileName;
-	fileName << m_settings.Path << "/" << m_settings.IpAddress << "/" << GetDateTimeString() << ".mp4";
-
-	std::stringstream command;
-	command << ffmpeg << " -i \"" << url.str() << "\" -t 30 -c:v libx264 -pix_fmt yuv420p -profile:v main -level:v 3.1 -c:a aac -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
+#include "WatchBotRecorder.h"
+#include "Util.h"
+#include <HttpClient.h>
+#include <JpegImageCollection.h>
+#include <Logging.h>
+#include <filesystem/path.h>
+#include <fstream>
+#include <sstream>
+#include <vector>
+
+
+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::string dateTimeString = GetDateTimeString();
+	std::string fileName = GetFileName(m_settings, "jpg", dateTimeString);
+
+	Http::HttpClient* pHttpClient = m_pHttpClient;
+	Settings settings = m_settings;
+	pThreadPool->push( [pHttpClient, settings, dateTimeString](int) { WatchBotRecorder::Snapshots(pHttpClient, settings, dateTimeString, 1); } );
+
+	return fileName;
+}
+
+std::string WatchBotRecorder::MultiSnapshot(ctpl::thread_pool* pThreadPool, int numberOfImages)
+{
+	Logging::Log(Logging::Severity::Debug, "WatchBotRecorder MultiSnapshot");
+
+	std::string dateTimeString = GetDateTimeString();
+	std::string fileName = GetFileName(m_settings, "jpg", dateTimeString);
+
+	Http::HttpClient* pHttpClient = m_pHttpClient;
+	Settings settings = m_settings;
+	pThreadPool->push( [pHttpClient, settings, dateTimeString, numberOfImages](int) { WatchBotRecorder::Snapshots(pHttpClient, settings, dateTimeString, numberOfImages); } );
+
+	return fileName;
+}
+
+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;
+
+	std::string dateTimeString = GetDateTimeString();
+	std::string fileName = GetFileName(m_settings, "mp4", dateTimeString);
+
+	std::stringstream command;
+	command << ffmpeg << " -i \"" << url.str() << "\" -t 30 -c:v libx264 -pix_fmt yuv420p -profile:v main -level:v 3.1 -c:a aac -strict experimental " << fileName << " > /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;
+}
+
+std::string WatchBotRecorder::GetFileName(const Settings& settings, const std::string& extension, const std::string& dateTimeString)
+{
+	filesystem::path fileLocation(settings.Path);
+	fileLocation = fileLocation / filesystem::path(settings.IpAddress);
+
+	std::string fileName = std::string(dateTimeString).append(".").append(extension);
+	filesystem::path fullFileName = fileLocation / filesystem::path(fileName);
+
+	return fullFileName.str();
+}
+
+void WatchBotRecorder::Snapshots(Http::HttpClient* pHttpClient, const Settings& settings, std::string dateTimeString, int numberOfImages)
+{
+	std::vector<Image::JpegImage> images;
+
+	for (int i = 0; i < numberOfImages; ++i)
+		images.push_back(WatchBotRecorder::Snapshot(pHttpClient, settings));
+
+	Image::JpegImageCollection collection(images);
+	collection.Save(WatchBotRecorder::GetFileName(settings, "jpg", dateTimeString));
+}
+
+Image::JpegImage WatchBotRecorder::Snapshot(Http::HttpClient* pHttpClient, const Settings& settings)
+{
+	std::stringstream url;
+	url << "http://" << settings.IpAddress << ":" << settings.Port << "/snapshot.cgi?user=" << settings.Username << "&pwd=" << settings.Password;
+
+	Image::JpegImage image;
+
+	try
+	{
+		image.SetImageData(pHttpClient->GetUrlContents(url.str()));
+	}
+	catch (const std::exception& e)
+	{
+		std::stringstream ss;
+		ss << "WatchBotRecorder::Snapshot() - Error: " << e.what() << std::endl;
+		Logging::Log(Logging::Severity::Error, ss.str());
+	}
+
+	return image;
+}
+
+} // namespace Recorder
+} // namespace CameraRecorder

+ 41 - 34
Recorder/WatchBotRecorder.h

@@ -1,34 +1,41 @@
-#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
+#ifndef RECORDER_WATCHBOTRECORDER_H
+#define RECORDER_WATCHBOTRECORDER_H
+
+#include "Recorder.h"
+#include "Settings.h"
+#include <JpegImage.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 MultiSnapshot(ctpl::thread_pool* pThreadPool, int numberOfImages) override;
+	virtual std::string Video(ctpl::thread_pool* pThreadPool, const std::string& ffmpeg) override;
+
+private:
+	static std::string GetFileName(const Settings& settings, const std::string& extension, const std::string& dateTimeString);
+	static void Snapshots(Http::HttpClient* pHttpClient, const Settings& settings, std::string dateTimeString, int numberOfImages);
+	static Image::JpegImage Snapshot(Http::HttpClient* pHttpClient, const Settings& settings);
+
+private:
+	Http::HttpClient* m_pHttpClient;
+	Settings m_settings;
+};
+
+} // namespace Recorder
+} // namespace CameraRecorder
+
+#endif // RECORDER_WATCHBOTRECORDER_H

+ 38 - 32
Recorder/ZModoRecorder.cpp

@@ -1,32 +1,38 @@
-#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
+#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::MultiSnapshot(ctpl::thread_pool* pThreadPool, int numberOfImages)
+{
+	Logging::Log(Logging::Severity::Debug, "ZModoRecorder MultiSnapshot");
+	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

+ 35 - 34
Recorder/ZModoRecorder.h

@@ -1,34 +1,35 @@
-#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
+#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 MultiSnapshot(ctpl::thread_pool* pThreadPool, int numberOfImages) 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