@@ -19,6 +19,8 @@
namespace libcamera {
+enum class Orientation;
+
class CameraControlValidator;
class PipelineHandler;
class Stream;
@@ -65,6 +67,7 @@ private:
std::string id_;
std::set<Stream *> streams_;
std::set<const Stream *> activeStreams_;
+ Orientation orientation_;
bool disconnected_;
std::atomic<State> state_;
@@ -9,6 +9,7 @@
#include <memory>
#include <queue>
+#include <set>
#include <string>
#include <sys/types.h>
#include <vector>
@@ -18,8 +19,12 @@
#include <libcamera/controls.h>
#include <libcamera/stream.h>
+#include "libcamera/internal/yaml_emitter.h"
+
namespace libcamera {
+enum class Orientation;
+
class Camera;
class CameraConfiguration;
class CameraManager;
@@ -68,6 +73,9 @@ public:
CameraManager *cameraManager() const { return manager_; }
+ void dumpConfiguration(const std::set<const Stream *> &streams,
+ const Orientation &orientation);
+
protected:
void registerCamera(std::shared_ptr<Camera> camera);
void hotplugMediaDevice(MediaDevice *media);
@@ -81,6 +89,11 @@ protected:
CameraManager *manager_;
private:
+ enum DumpMode {
+ Controls,
+ Metadata,
+ };
+
void unlockMediaDevices();
void mediaDeviceDisconnected(MediaDevice *media);
@@ -89,6 +102,8 @@ private:
void doQueueRequest(Request *request);
void doQueueRequests();
+ void dumpRequest(Request *request, DumpMode mode);
+
std::vector<std::shared_ptr<MediaDevice>> mediaDevices_;
std::vector<std::weak_ptr<Camera>> cameras_;
@@ -97,6 +112,14 @@ private:
const char *name_;
unsigned int useCount_;
+ YamlRoot controlsEmitter_;
+ YamlDict controlsDict_;
+ YamlList controlsList_;
+
+ YamlRoot metadataEmitter_;
+ YamlDict metadataDict_;
+ YamlList metadataList_;
+
friend class PipelineHandlerFactoryBase;
};
@@ -1215,6 +1215,9 @@ int Camera::configure(CameraConfiguration *config)
d->activeStreams_.insert(stream);
}
+ /* TODO Save sensor configuration for dumping it to capture script */
+ d->orientation_ = config->orientation;
+
d->setState(Private::CameraConfigured);
return 0;
@@ -1356,6 +1359,16 @@ int Camera::start(const ControlList *controls)
ASSERT(d->requestSequence_ == 0);
+ /*
+ * Invoke method in blocking mode to avoid the risk of writing after
+ * streaming has started.
+ * This needs to be here as PipelineHandler::start is a virtual function
+ * so it is impractical to add the dumping there.
+ * TODO Pass the sensor configuration, once it is supported
+ */
+ d->pipe_->invokeMethod(&PipelineHandler::dumpConfiguration,
+ ConnectionTypeBlocking, d->activeStreams_, d->orientation_);
+
ret = d->pipe_->invokeMethod(&PipelineHandler::start,
ConnectionTypeBlocking, this, controls);
if (ret)
@@ -8,6 +8,7 @@
#include "libcamera/internal/pipeline_handler.h"
#include <chrono>
+#include <fstream>
#include <sys/stat.h>
#include <sys/sysmacros.h>
@@ -464,6 +465,8 @@ void PipelineHandler::doQueueRequest(Request *request)
request->_d()->sequence_ = data->requestSequence_++;
+ dumpRequest(request, DumpMode::Controls);
+
if (request->_d()->cancelled_) {
completeRequest(request);
return;
@@ -555,6 +558,8 @@ void PipelineHandler::completeRequest(Request *request)
request->_d()->complete();
+ dumpRequest(request, DumpMode::Metadata);
+
Camera::Private *data = camera->_d();
while (!data->queuedRequests_.empty()) {
@@ -758,6 +763,94 @@ void PipelineHandler::disconnect()
* \return The CameraManager for this pipeline handler
*/
+/**
+ * \brief Dump the camera configuration to YAML format
+ *
+ * Dump to the file path specified in the LIBCAMERA_DUMP_CAPTURE_SCRIPT
+ * environment variable, if any, the Camera configuration in YAML format.
+ */
+void PipelineHandler::dumpConfiguration(const std::set<const Stream *> &streams,
+ const Orientation &orientation)
+{
+ const char *file = utils::secure_getenv("LIBCAMERA_DUMP_CAPTURE_SCRIPT");
+ if (!file)
+ return;
+
+ std::string filePath(file);
+ LOG(Pipeline, Debug) << "Dumping controls in YAML format to: "
+ << filePath;
+
+ /* Create the YAML roots for controls and metadata output files. */
+
+ controlsEmitter_ = YamlEmitter::root(filePath);
+ controlsDict_ = controlsEmitter_.dict();
+
+ /*
+ * Metadata needs to go into a separate file because otherwise it'll
+ * flood the capture script
+ */
+ filePath += ".metadata";
+ LOG(Pipeline, Debug) << "Dumping metadata in YAML format to: "
+ << filePath;
+ metadataEmitter_ = YamlEmitter::root(filePath);
+ metadataDict_ = metadataEmitter_.dict();
+ metadataList_ = metadataDict_.list("frames");
+
+ YamlDict configurationDict = controlsDict_.dict("configuration");
+ std::stringstream o;
+ o << orientation;
+ configurationDict["orientation"] = o.str();
+
+ /* \todo Dump Sensor configuration */
+
+ YamlList streamsList = configurationDict.list("streams");
+
+ for (const auto &stream : streams) {
+ const StreamConfiguration &streamConfig = stream->configuration();
+ YamlDict yamlStream = streamsList.dict();
+
+ yamlStream["pixelformat"] = streamConfig.pixelFormat.toString();
+ yamlStream["size"] = streamConfig.size.toString();
+ yamlStream["stride"] = std::to_string(streamConfig.stride);
+ yamlStream["frameSize"] = std::to_string(streamConfig.frameSize);
+ yamlStream["bufferCount"] = std::to_string(streamConfig.bufferCount);
+
+ if (streamConfig.colorSpace)
+ yamlStream["colorSpace"] =
+ streamConfig.colorSpace->toString();
+ }
+}
+
+void PipelineHandler::dumpRequest(Request *request, DumpMode mode)
+{
+ if (!controlsEmitter_.valid())
+ return;
+
+ ControlList &controls = mode == DumpMode::Controls ? request->controls()
+ : request->metadata();
+ if (controls.empty())
+ return;
+
+ YamlDict yamlFrame;
+ if (mode == DumpMode::Controls) {
+ if (!controlsList_.valid())
+ controlsList_ = controlsDict_.list("frames");
+
+ yamlFrame = controlsList_.dict();
+ } else {
+ yamlFrame = metadataList_.dict();
+ }
+
+ YamlDict yamlCtrls = yamlFrame.dict(std::to_string(request->sequence()));
+
+ const ControlIdMap *idMap = controls.idMap();
+ for (const auto &pair : controls) {
+ const ControlId *ctrlId = idMap->at(pair.first);
+
+ yamlCtrls[ctrlId->name()] = pair.second.toString();
+ }
+}
+
/**
* \class PipelineHandlerFactoryBase
* \brief Base class for pipeline handler factories