refactor(streamer): adopt proxy backends and typed statuses

This commit is contained in:
2026-03-10 23:29:59 +08:00
parent 6af97ee5d3
commit 0ad6887095
22 changed files with 1686 additions and 275 deletions
@@ -25,6 +25,12 @@ enum class RtmpMode {
Domestic,
};
enum class RtmpTransportType {
Libavformat,
FfmpegProcess,
LegacyCustom,
};
enum class EncoderBackendType {
Auto,
FFmpeg,
@@ -58,6 +64,8 @@ struct EncoderConfig {
struct RtmpOutputConfig {
bool enabled{false};
std::vector<std::string> urls{};
RtmpTransportType transport{RtmpTransportType::Libavformat};
std::string ffmpeg_path{"ffmpeg"};
RtmpMode mode{RtmpMode::Enhanced};
};
@@ -112,6 +120,7 @@ struct RuntimeConfig {
std::string_view to_string(CodecType codec);
std::string_view to_string(RunMode mode);
std::string_view to_string(RtmpMode mode);
std::string_view to_string(RtmpTransportType transport);
std::string_view to_string(EncoderBackendType backend);
std::string_view to_string(EncoderDeviceType device);
std::string_view to_string(McapCompression compression);
+114
View File
@@ -0,0 +1,114 @@
#pragma once
#include <expected>
#include <string>
#include <string_view>
#include <utility>
namespace cvmmap_streamer {
using error_t = int;
enum class ErrorCode : error_t {
Ok = 0,
InvalidArgument,
InvalidConfig,
Unsupported,
NotReady,
BackendUnavailable,
AllocationFailed,
Io,
Network,
Protocol,
Encoder,
ExternalLibrary,
Serialization,
ChildProcess,
EndOfStream,
Internal,
};
inline constexpr ErrorCode ERR_OK = ErrorCode::Ok;
inline constexpr ErrorCode ERR_INVALID_ARGUMENT = ErrorCode::InvalidArgument;
inline constexpr ErrorCode ERR_INVALID_CONFIG = ErrorCode::InvalidConfig;
inline constexpr ErrorCode ERR_UNSUPPORTED = ErrorCode::Unsupported;
inline constexpr ErrorCode ERR_NOT_READY = ErrorCode::NotReady;
inline constexpr ErrorCode ERR_BACKEND_UNAVAILABLE = ErrorCode::BackendUnavailable;
inline constexpr ErrorCode ERR_ALLOCATION_FAILED = ErrorCode::AllocationFailed;
inline constexpr ErrorCode ERR_IO = ErrorCode::Io;
inline constexpr ErrorCode ERR_NETWORK = ErrorCode::Network;
inline constexpr ErrorCode ERR_PROTOCOL = ErrorCode::Protocol;
inline constexpr ErrorCode ERR_ENCODER = ErrorCode::Encoder;
inline constexpr ErrorCode ERR_EXTERNAL_LIBRARY = ErrorCode::ExternalLibrary;
inline constexpr ErrorCode ERR_SERIALIZATION = ErrorCode::Serialization;
inline constexpr ErrorCode ERR_CHILD_PROCESS = ErrorCode::ChildProcess;
inline constexpr ErrorCode ERR_END_OF_STREAM = ErrorCode::EndOfStream;
inline constexpr ErrorCode ERR_INTERNAL = ErrorCode::Internal;
struct Error {
ErrorCode code{ERR_OK};
std::string detail{};
};
template <typename T>
using Result = std::expected<T, Error>;
using Status = Result<void>;
[[nodiscard]]
inline std::unexpected<Error> unexpected_error(ErrorCode code, std::string detail = {}) {
return std::unexpected(Error{
.code = code,
.detail = std::move(detail),
});
}
[[nodiscard]]
inline std::string_view error_code_name(ErrorCode code) {
switch (code) {
case ERR_OK:
return "ok";
case ERR_INVALID_ARGUMENT:
return "invalid_argument";
case ERR_INVALID_CONFIG:
return "invalid_config";
case ERR_UNSUPPORTED:
return "unsupported";
case ERR_NOT_READY:
return "not_ready";
case ERR_BACKEND_UNAVAILABLE:
return "backend_unavailable";
case ERR_ALLOCATION_FAILED:
return "allocation_failed";
case ERR_IO:
return "io";
case ERR_NETWORK:
return "network";
case ERR_PROTOCOL:
return "protocol";
case ERR_ENCODER:
return "encoder";
case ERR_EXTERNAL_LIBRARY:
return "external_library";
case ERR_SERIALIZATION:
return "serialization";
case ERR_CHILD_PROCESS:
return "child_process";
case ERR_END_OF_STREAM:
return "end_of_stream";
case ERR_INTERNAL:
return "internal";
default:
return "unknown";
}
}
[[nodiscard]]
inline std::string format_error(const Error &error) {
if (error.detail.empty()) {
return std::string(error_code_name(error.code));
}
return std::string(error_code_name(error.code)) + ": " + error.detail;
}
}
@@ -7,6 +7,22 @@
namespace cvmmap_streamer::encode {
enum class EncodedBitstreamFormat {
AnnexB,
};
struct EncodedStreamInfo {
CodecType codec{CodecType::H264};
std::uint32_t width{0};
std::uint32_t height{0};
std::uint32_t time_base_num{1};
std::uint32_t time_base_den{1'000'000'000u};
std::uint32_t frame_rate_num{30};
std::uint32_t frame_rate_den{1};
EncodedBitstreamFormat bitstream_format{EncodedBitstreamFormat::AnnexB};
std::vector<std::uint8_t> decoder_config{};
};
struct EncodedAccessUnit {
CodecType codec{CodecType::H264};
std::uint64_t source_timestamp_ns{0};
@@ -1,17 +1,17 @@
#pragma once
#include "cvmmap_streamer/config/runtime_config.hpp"
#include "cvmmap_streamer/core/status.hpp"
#include "cvmmap_streamer/encode/encoded_access_unit.hpp"
#include "cvmmap_streamer/ipc/contracts.hpp"
#include <cstdint>
#include <expected>
#include <memory>
#include <span>
#include <string>
#include <string_view>
#include <vector>
#include <proxy/proxy.h>
namespace cvmmap_streamer::encode {
struct RawVideoFrame {
@@ -20,35 +20,31 @@ struct RawVideoFrame {
std::span<const std::uint8_t> bytes{};
};
class EncoderBackend {
public:
virtual ~EncoderBackend() = default;
PRO_DEF_MEM_DISPATCH(MemBackendName, backend_name);
PRO_DEF_MEM_DISPATCH(MemUsingHardware, using_hardware);
PRO_DEF_MEM_DISPATCH(MemInit, init);
PRO_DEF_MEM_DISPATCH(MemStreamInfo, stream_info);
PRO_DEF_MEM_DISPATCH(MemPoll, poll);
PRO_DEF_MEM_DISPATCH(MemPushFrame, push_frame);
PRO_DEF_MEM_DISPATCH(MemDrain, drain);
PRO_DEF_MEM_DISPATCH(MemFlush, flush);
PRO_DEF_MEM_DISPATCH(MemShutdown, shutdown);
[[nodiscard]]
virtual std::string_view backend_name() const = 0;
struct EncoderBackendFacade : pro::facade_builder
::add_convention<MemBackendName, std::string_view() const>
::add_convention<MemUsingHardware, bool() const>
::add_convention<MemInit, Status(const RuntimeConfig &, const ipc::FrameInfo &)>
::add_convention<MemStreamInfo, Result<EncodedStreamInfo>() const>
::add_convention<MemPoll, Status()>
::add_convention<MemPushFrame, Status(const RawVideoFrame &)>
::add_convention<MemDrain, Result<std::vector<EncodedAccessUnit>>()>
::add_convention<MemFlush, Result<std::vector<EncodedAccessUnit>>()>
::add_convention<MemShutdown, void()>
::build {};
[[nodiscard]]
virtual bool using_hardware() const = 0;
[[nodiscard]]
virtual std::expected<void, std::string> init(const RuntimeConfig &config, const ipc::FrameInfo &frame_info) = 0;
[[nodiscard]]
virtual std::expected<void, std::string> poll() = 0;
[[nodiscard]]
virtual std::expected<void, std::string> push_frame(const RawVideoFrame &frame) = 0;
[[nodiscard]]
virtual std::expected<std::vector<EncodedAccessUnit>, std::string> drain() = 0;
[[nodiscard]]
virtual std::expected<std::vector<EncodedAccessUnit>, std::string> flush() = 0;
virtual void shutdown() = 0;
};
using EncoderBackend = pro::proxy<EncoderBackendFacade>;
[[nodiscard]]
std::expected<std::unique_ptr<EncoderBackend>, std::string> make_encoder_backend(const RuntimeConfig &config);
Result<EncoderBackend> make_encoder_backend(const RuntimeConfig &config);
}
@@ -0,0 +1,30 @@
#pragma once
#include "cvmmap_streamer/config/runtime_config.hpp"
#include "cvmmap_streamer/core/status.hpp"
#include "cvmmap_streamer/encode/encoded_access_unit.hpp"
#include <string_view>
#include <proxy/proxy.h>
namespace cvmmap_streamer::protocol {
PRO_DEF_MEM_DISPATCH(MemBackendName, backend_name);
PRO_DEF_MEM_DISPATCH(MemPublishAccessUnit, publish_access_unit);
PRO_DEF_MEM_DISPATCH(MemLogMetrics, log_metrics);
struct RtmpOutputFacade : pro::facade_builder
::add_convention<MemBackendName, std::string_view() const>
::add_convention<MemPublishAccessUnit, Status(const encode::EncodedAccessUnit &)>
::add_convention<MemLogMetrics, void() const>
::build {};
using RtmpOutput = pro::proxy<RtmpOutputFacade>;
[[nodiscard]]
Result<RtmpOutput> make_rtmp_output(
const RuntimeConfig &config,
const encode::EncodedStreamInfo &stream_info);
}