diff --git a/src/cam.cpp b/src/cam.cpp
index d529391264bce5f63698ebd93930e7367ebde535..899911f12184f180d87669c5eda5796b1580bcd7 100644
--- a/src/cam.cpp
+++ b/src/cam.cpp
@@ -15,6 +15,8 @@
 #include <QTimer>
 #include <QDebug>
 
+
+
 using State = Cam::State;
 using namespace VmbCPP;
 using namespace std::chrono;
@@ -30,13 +32,28 @@ _state(disconnected),
 _frames(FramePtrVector(utils::threadsPerCam())),
 _payloadSize(0LL),
 _pixelFormat(""),
-_timer(new QTimer(this))
+_timer(new QTimer(this)),
+_outDir(utils::outDir() + QDir::separator() + _name + QDir::separator()) //specific for cam
 {
 	_timer->setSingleShot(true);
 	connect(_timer, SIGNAL(timeout()), this, SLOT(stopRecording()));
-	// id();
 }
 
+// Copy constructor
+Cam::Cam(const Cam& other) : IPrinter(),
+_cam(other._cam),
+_name(other._name),
+_ip(other._ip),
+_id(other._id),
+_state(other._state),
+_frames(other._frames),
+_payloadSize(other._payloadSize),
+_pixelFormat(other._pixelFormat),
+_timer(new QTimer(this)),
+_outDir(other._outDir)
+{
+	qDebug() << __LINE__ << "-" << __PRETTY_FUNCTION__ << "copy constructor";
+}
 
 //xxx add ip and name
 // Cam::Cam(CameraPtr pCamera) : IPrinter(),
@@ -56,18 +73,6 @@ _timer(new QTimer(this))
 
 // }
 
-// // Copy constructor
-// Cam::Cam(const Cam& other) :
-// IPrinter(),
-// _cam(other._cam),
-// _id(other._id),
-// _state(other._state),
-// _frames(other._frames),
-// _payloadSize(other._payloadSize),
-// _pixelFormat(other._pixelFormat)
-// {
-// 	qDebug() << __LINE__ << "-" << __PRETTY_FUNCTION__ << "copy constructor";
-// }
 
 Cam::~Cam()
 {
@@ -127,6 +132,22 @@ void Cam::close()
 	//state change will be handled by CamObserver::CameraStateChanged > Core::onCameraChanged > Cam::onChanged
 }
 
+bool Cam::checkOutDir()
+{
+	//check if dir exists and if it doesnt, create it
+	QDir dir(_outDir);
+	if (!dir.exists())
+		dir.mkpath(".");
+
+	return dir.exists();
+}
+
+QString Cam::outDir()
+{
+	return _outDir;
+}
+
+
 /* Does:
 	get Payloadsize
 	get Pixelformat - eg RGB8
@@ -145,6 +166,11 @@ void Cam::startRecording(seconds dur)
 	if(_state != opened)
 		return error("cant start recording, when cam is " + stateToString(_state));
 
+	if(!checkOutDir())
+		return error("could not create directory: " + _outDir);
+	else
+		IPrinter::info("writing frames to: " + _outDir);
+
 	try
 	{
 		FeaturePtr pFeature;
@@ -161,7 +187,7 @@ void Cam::startRecording(seconds dur)
 		for( auto& frame : _frames )
 		{
 			frame.reset(new Frame(_payloadSize));
-			g( frame->RegisterObserver( IFrameObserverPtr( new FrameObserver(_cam) )),"register frame observer");
+			g( frame->RegisterObserver( IFrameObserverPtr( new FrameObserver(std::make_shared<Cam>(*this)) )),"register frame observer");
 			g( _cam->AnnounceFrame(frame), "announce frame");
 		}
 
@@ -302,7 +328,6 @@ void Cam::onChanged(UpdateTriggerType type)
 			error("onChanged: unknown trigger type");
 	}
 }
-#include <VmbC/VmbCommonTypes.h>
 //write out settings.xml for current open cam
 void Cam::saveSettings()
 {
diff --git a/src/cam.h b/src/cam.h
index 3103aaa39ff727a083874047f24c9cb755f3ba18..9e08782910b569df76b0036d5a0f6058d3259f59 100644
--- a/src/cam.h
+++ b/src/cam.h
@@ -4,6 +4,7 @@
 
 #include <QObject>
 #include <QString>
+#include <QDir>
 
 #include <chrono>
 #include <vector>
@@ -39,8 +40,8 @@ public:
 
 	// const QString& name, const int& nThreads
 	Cam(QString name, QString ip);
+	Cam(const Cam&);
 	// Cam(CameraPtr pCamera);
-	// Cam(const Cam&);
 	~Cam();
 
 	QString id();
@@ -64,11 +65,14 @@ public:
 
 	inline CameraPtr getCameraPtr() { return _cam; }
 	inline void setCameraPtr( CameraPtr pCamera ) { _cam = pCamera; }
+	QString outDir();
 
 public slots:
 	void stopRecording();
 
 private:
+	bool checkOutDir();
+
 	CameraPtr _cam;
 	QString _name;
 	QString _ip;
@@ -78,5 +82,6 @@ private:
 	VmbInt64_t _payloadSize;
 	std::string _pixelFormat;
 	QTimer* _timer;
+	QString _outDir;
 };
 
diff --git a/src/core.cpp b/src/core.cpp
index 2147a87e072920bc7034e374593693544799b24c..03135c01ada7942d52931831c96866507231cdbf 100644
--- a/src/core.cpp
+++ b/src/core.cpp
@@ -20,7 +20,6 @@
 #include <QCoreApplication>
 
 using namespace VmbCPP;
-using namespace std::chrono_literals; //ms, s, m, h
 
 Core::Core() : IPrinter(),
 	_sys( VmbSystem::GetInstance() ), // create and get Vimba singleton
@@ -44,14 +43,13 @@ void Core::init()
 
 	//parse config file
 	QList<QPair<QString, QString>> parsedCameras;
-	if( ! utils::parseConfig(parsedCameras) )
+	if( ! utils::parseConfig(parsedCameras, _recDuration) )
 	{
 		utils::running = false;
 		qApp->quit();
 		return;
 	}
 
-
 	//add to cam list
 	for ( auto cam : parsedCameras )
 	{
diff --git a/src/frameobserver.cpp b/src/frameobserver.cpp
index 92177fbb64278e8becd37ece943126f604330bee..720a5da47391957c2fb18d655a10643a057e2bf8 100644
--- a/src/frameobserver.cpp
+++ b/src/frameobserver.cpp
@@ -11,9 +11,9 @@
 
 using namespace VmbCPP;
 
-FrameObserver::FrameObserver( CameraPtr pCamera ) : IFrameObserver( pCamera )
+FrameObserver::FrameObserver( CamPtr cam ) : IFrameObserver( cam->getCameraPtr() ),
+_cam(cam)
 {
-
 }
 
 /* Frame callback notifies about incoming frames
@@ -22,6 +22,7 @@ FrameObserver::FrameObserver( CameraPtr pCamera ) : IFrameObserver( pCamera )
 	Do not apply image processing within this callback ( performance )
 	-> create frame processor to handle frame processing asynchronously
 	- when the frame has been processed , requeue it
+	- frameprocessing is done in a separate thread
 */
 void FrameObserver::FrameReceived ( const FramePtr pframe )
 {
@@ -51,7 +52,7 @@ void FrameObserver::FrameReceived ( const FramePtr pframe )
 			break;
 	}
 
-	FrameProcessor* processor = new FrameProcessor(pframe);
+	FrameProcessor* processor = new FrameProcessor(pframe,_cam->outDir());
 	QThread *thread = new QThread;
 	processor->moveToThread(thread);
 
diff --git a/src/frameobserver.h b/src/frameobserver.h
index 6ab97f01073bdb45947d32da1dba971a6f5efc8a..c87e7c11bbcd64e512cbc953cb528bff34d456ba 100644
--- a/src/frameobserver.h
+++ b/src/frameobserver.h
@@ -1,9 +1,9 @@
 #pragma once
 
+#include "cam.h"
 #include <QObject>
 #include <VmbCPP/IFrameObserver.h>
 
-using VmbCPP::CameraPtr;
 using VmbCPP::FramePtr;
 using VmbCPP::IFrameObserver;
 
@@ -12,12 +12,15 @@ class FrameObserver : public QObject, public IFrameObserver
 	Q_OBJECT
 
 	public:
+		FrameObserver( CamPtr cam );
 		void FrameReceived( const FramePtr );
-		FrameObserver( CameraPtr pCamera );
 
 
 	public slots:
 		void queueFrame(FramePtr pframe);
 
+	private:
+		CamPtr _cam;
+
 };
 
diff --git a/src/frameprocessor.cpp b/src/frameprocessor.cpp
index 4db68edd5792c59b799096e039b34239f805c6bf..114413a744be228828e8dcef381b4dc89a2e02af 100644
--- a/src/frameprocessor.cpp
+++ b/src/frameprocessor.cpp
@@ -1,6 +1,6 @@
 #include "frameprocessor.h"
 #include "frameobserver.h"
-// #include "qobject.h"
+#include "utils.h"
 
 #include <opencv2/imgcodecs.hpp>
 #include <opencv2/opencv.hpp>
@@ -12,8 +12,13 @@
 #include <QThread>
 #include <QObject>
 #include <QImage>
+#include <QDir>
 
-FrameProcessor::FrameProcessor(const FramePtr pframe) : m_pframe(pframe) {}
+FrameProcessor::FrameProcessor(const FramePtr pframe, const QString& outDir) :
+_pframe(pframe),
+_outDir( outDir )
+{
+}
 
 FrameProcessor::~FrameProcessor()
 {
@@ -33,11 +38,11 @@ void FrameProcessor::processFrame()
 	VmbUint64_t timestamp = 0;
 	VmbPixelFormatType pixelFormat;
 
-	m_pframe->GetWidth(width);
-	m_pframe->GetHeight(height);
-	m_pframe->GetBuffer(pdata);
-	m_pframe->GetTimestamp(timestamp);
-	m_pframe->GetPixelFormat(pixelFormat);
+	_pframe->GetWidth(width);
+	_pframe->GetHeight(height);
+	_pframe->GetBuffer(pdata);
+	_pframe->GetTimestamp(timestamp);
+	_pframe->GetPixelFormat(pixelFormat);
 
 	QString pixelFormatStr = "RGB8";
 	if (pixelFormat != VmbPixelFormatRgb8)
@@ -49,7 +54,8 @@ void FrameProcessor::processFrame()
 	cv::Mat frameMat(height, width, CV_8UC3, pdata);
 
 	std::vector<int> params = {cv::IMWRITE_JPEG_QUALITY, 99, cv::IMWRITE_JPEG_OPTIMIZE, 1, cv::IMWRITE_JPEG_RST_INTERVAL,4};
-	QString filename = QString::number(timestamp) + "_frame.jpg";
+
+	QString filename = _outDir + QString::number(timestamp) + ".jpg";
 	cv::imwrite(filename.toStdString(), frameMat, params);
 
 	qDebug() << "rcvd Frame #" << count << ": " << width << "x" << height << "px, format: " << pixelFormatStr << ", timestamp: " << timestamp << ", file: " << filename;
@@ -60,5 +66,5 @@ void FrameProcessor::processFrame()
 #endif
 
 
-	emit frameProcessed(m_pframe);
+	emit frameProcessed(_pframe);
 }
diff --git a/src/frameprocessor.h b/src/frameprocessor.h
index ffd1aa4853f75eaacf571c61bbd72694b3edeba7..10b919113d674bdf96293b2ddbfbf56172abbd8a 100644
--- a/src/frameprocessor.h
+++ b/src/frameprocessor.h
@@ -8,7 +8,7 @@ class FrameProcessor : public QObject
 	Q_OBJECT
 
 public:
-	explicit FrameProcessor(const FramePtr);
+	explicit FrameProcessor(const FramePtr, const QString&);
 	~FrameProcessor();
 
 
@@ -20,6 +20,7 @@ signals:
 
 
 private:
-	FramePtr m_pframe;
+	FramePtr _pframe;
+	const QString _outDir;
 };
 
diff --git a/src/utils.cpp b/src/utils.cpp
index e812374483715253fe21382467d098ea70198765..ddbf4e3dc9a5928ce0e09d28fc610e8162e446d2 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -6,12 +6,11 @@
 #include <iostream>
 
 #include "VmbCPP/VmbCPP.h"
-#include "qcoreapplication.h"
+#include <VmbC/VmbCommonTypes.h>
 
 #include <QDebug>
 #include <QDir>
 #include <QThread>
-
 #include <QCoreApplication>
 #include <QFile>
 #include <QJsonDocument>
@@ -103,7 +102,7 @@ const QString errorCodeToMessage( VmbError_t err )
 	return msg;
 }
 
-bool parseConfig(QList<QPair<QString,QString>>& parsedCameras)
+bool parseConfig(QList<QPair<QString,QString>>& parsedCameras, seconds& recDuration)
 {
 	qDebug() << "open " << configFile();
 	QFile file(configFile());
@@ -124,15 +123,43 @@ bool parseConfig(QList<QPair<QString,QString>>& parsedCameras)
 		return false;
 	}
 
-	if (!doc.isArray())
+	if (!doc.isObject())
+	{
+		qWarning() << "JSON content is not an object";
+		return false;
+	}
+
+	QJsonObject jsonObject = doc.object();
+
+	// Parse recordDurationInSeconds
+	if (!jsonObject.contains("recordDurationInSeconds") || !jsonObject["recordDurationInSeconds"].isDouble())
+	{
+		qWarning() << "Missing or invalid recordDurationInSeconds";
+		return false;
+	}
+
+	recDuration = jsonObject["recordDurationInSeconds"].toInt() * 1s; //convert to s
+	qDebug() << "parsed recDuration:" << recDuration.count();
+
+	// Parse outDir
+	if (!jsonObject.contains("outDir") || !jsonObject["outDir"].isString())
 	{
-		qWarning() << "JSON content is not an array";
+		qWarning() << "Missing or invalid outDir";
+		return false;
+	}
+	outDir(jsonObject["outDir"].toString()); //set global
+	qDebug() << "parsed outDir:" << outDir();
+
+	// Parse cameras
+	if (!jsonObject.contains("cameras") || !jsonObject["cameras"].isArray())
+	{
+		qWarning() << "Missing or invalid cameras array";
 		return false;
 	}
 
 	parsedCameras.clear();
-	QJsonArray jsonArray = doc.array();
-	for (const QJsonValue& value : jsonArray)
+	QJsonArray camerasArray = jsonObject["cameras"].toArray();
+	for (const QJsonValue& value : camerasArray)
 	{
 		if (!value.isObject())
 		{
@@ -147,11 +174,25 @@ bool parseConfig(QList<QPair<QString,QString>>& parsedCameras)
 		parsedCameras.append(qMakePair(name, ip));
 	}
 
-	// for (const auto& cam : parsedCameras)
-	// 	qDebug() << "Camera Name:" << cam.first << ", IP:" << cam.second;
 	return true;
 }
 
+//general basedir
+QString outDir(QString dirname)
+{
+	static QString _outDir;
+	if( _outDir.isEmpty() )
+	{
+		if( dirname.isEmpty() )
+			_outDir = QCoreApplication::applicationDirPath();
+		else
+			_outDir = dirname;
+	}
+
+	qDebug() << "outDir:" << _outDir;
+	return _outDir;
+}
+
 // needed to find cams via ip
 QString configFile(QString filename)
 {
@@ -167,7 +208,6 @@ QString getFirstFileInAppDirEndingWith( QString const& suffix )
 	QDir dir(QCoreApplication::applicationDirPath());
 	QStringList files = dir.entryList(QStringList() << "*."+suffix, QDir::Files);
 	// QStringList const folders = source.entryList(QDir::NoDot | QDir::NoDotDot | QDir::Dirs);
-	// auto file =  QCoreApplication::applicationDirPath()+QDir::separator()+files.first();
 	auto file =  files.first();
 	return files.isEmpty() || suffix.isEmpty() ? QString() : file;
 }
diff --git a/src/utils.h b/src/utils.h
index b5b1afc2e35904c68c6c01bd52ba0baa099c85e3..996ca5e2234e58706a109f3b65f1b8906c99279d 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -2,20 +2,25 @@
 #define UTILS_H
 
 #include "VmbC/VmbCommonTypes.h" //VmbError_t -> cant really fwd-decl typedef...??
+
 #include <atomic>
+#include <chrono>
 
 #include <QString>
 #include <QList>
 #include <QPair>
 
 class QDir;
+using std::chrono::seconds;
+using namespace std::chrono_literals;
 
 namespace utils
 {
 
-bool parseConfig(QList<QPair<QString,QString>>&);
+bool parseConfig(QList<QPair<QString,QString>>&, seconds&);
 QString configFile(QString filename="");
 QString settingsFile(QString filename="");
+QString outDir(QString dirname="");
 QString getFirstFileInAppDirEndingWith( QString const&);
 
 
@@ -23,7 +28,7 @@ const QStringList getVersions();
 const QString getVersion();
 const QString errorCodeToMessage( VmbError_t );
 
-extern std::atomic<bool> running; //global flag to sync threads and ensure proper exit
+extern std::atomic<bool> running; //global flag to ensure proper exit of threads
 int threadsPerCam();
 int ncam();