Skip to content

Instantly share code, notes, and snippets.

@will127534
Created October 16, 2023 02:28
Show Gist options
  • Save will127534/6dc1b4e219be6d3afdf19e6a39a2cd98 to your computer and use it in GitHub Desktop.
Save will127534/6dc1b4e219be6d3afdf19e6a39a2cd98 to your computer and use it in GitHub Desktop.
network Preview Stage for libcamera-apps
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include <libcamera/stream.h>
#include "core/frame_info.hpp"
#include "core/libcamera_app.hpp"
#include "post_processing_stages/post_processing_stage.hpp"
using Stream = libcamera::Stream;
class networkPreviewStage : public PostProcessingStage
{
public:
networkPreviewStage(LibcameraApp *app);
~networkPreviewStage();
char const *Name() const override;
void Read(boost::property_tree::ptree const &params) override;
void Configure() override;
bool Process(CompletedRequestPtr &completed_request) override;
private:
Stream *stream_;
StreamInfo info_;
int sockfd_;
std::string ip_;
int port_;
int config_port_;
int64_t last_timestamp_ns_;
void sendInfoToServer(const std::string &ip, int port, int width, int height, int stride);
int init_socket(const std::string &ip, int port);
};
#define NAME "networkPreview"
char const *networkPreviewStage::Name() const
{
return NAME;
}
void networkPreviewStage::Read(boost::property_tree::ptree const &params)
{
ip_ = params.get<std::string>("ip");
port_ = params.get<int>("port", 255);
config_port_ = params.get<int>("config_port", 255);
}
networkPreviewStage::networkPreviewStage(LibcameraApp *app) : PostProcessingStage(app)
{
}
networkPreviewStage::~networkPreviewStage()
{
if (sockfd_ >= 0)
{
close(sockfd_);
}
}
void networkPreviewStage::sendInfoToServer(const std::string &ip, int port, int width, int height, int stride) {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Error creating socket");
return;
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
if (inet_pton(AF_INET, ip.c_str(), &server_addr.sin_addr) <= 0) {
perror("Error setting up IP address");
close(sockfd);
return;
}
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Error connecting to server");
close(sockfd);
return;
}
char buffer[256];
snprintf(buffer, sizeof(buffer), "%d,%d,%d\n", width, height, stride);
send(sockfd, buffer, strlen(buffer), 0);
close(sockfd);
}
void networkPreviewStage::Configure()
{
stream_ = app_->GetMainStream();
if (!stream_ || stream_->configuration().pixelFormat != libcamera::formats::YUV420)
throw std::runtime_error("networkPreviewStage: only YUV420 format supported");
info_ = app_->GetStreamInfo(stream_);
LOG(1, "networkPreviewStage:" << info_.width << "x" << info_.height << " " << info_.stride);
LOG(1, "Setting up NetworkPreview: " << ip_ << ":" << port_);
sendInfoToServer(ip_, config_port_, info_.width, info_.height, info_.stride);
sockfd_ = init_socket(ip_, port_);
}
bool networkPreviewStage::Process(CompletedRequestPtr &completed_request)
{
BufferWriteSync w(app_, completed_request->buffers[stream_]);
libcamera::Span<uint8_t> buffer = w.Get()[0];
int64_t timestamp_ns = completed_request->buffers[stream_]->metadata().timestamp;
float framerate = 1000000000.0 / (timestamp_ns - last_timestamp_ns_);
LOG(1, "Sending Frame:" << timestamp_ns <<" buffer size:" << buffer.size() << " " << framerate);
last_timestamp_ns_ = timestamp_ns;
if (sockfd_ >= 0)
{
send(sockfd_, buffer.data(), buffer.size(), 0);
return false;
}
return true;
}
int networkPreviewStage::init_socket(const std::string &ip, int port)
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("Error creating socket");
return -1;
}
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
inet_pton(AF_INET, ip.c_str(), &server_addr.sin_addr);
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
perror("Error connecting to server");
close(sockfd);
return -1;
}
int flag = 1;
setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int));
int bufsize = 32*1024*1024; // e.g., 1MB
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize));
return sockfd;
}
static PostProcessingStage *Create(LibcameraApp *app)
{
return new networkPreviewStage(app);
}
static RegisterStage reg(NAME, &Create);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment