fix wrong path

This commit is contained in:
Exil Productions
2025-12-26 22:52:08 +01:00
parent 8c0cf3789f
commit 3cc76b4d99
6 changed files with 2206 additions and 2513 deletions

0
build.sh Normal file → Executable file
View File

View File

@@ -1,6 +1,5 @@
#include "../include/rtmp_capi.h" #include "../include/rtmp_capi.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <unistd.h> #include <unistd.h>
static void on_connect_cb(const char* ip, void* data) static void on_connect_cb(const char* ip, void* data)

View File

@@ -1,8 +1,8 @@
#ifndef RTMP_CAPI_H #ifndef RTMP_CAPI_H
#define RTMP_CAPI_H #define RTMP_CAPI_H
#include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -10,38 +10,40 @@ extern "C" {
typedef void *RtmpServerHandle; typedef void *RtmpServerHandle;
enum RtmpLogLevel enum RtmpLogLevel {
{ RTMP_LOG_ERROR = 0,
RTMP_LOG_ERROR = 0, RTMP_LOG_WARN = 1,
RTMP_LOG_WARN = 1, RTMP_LOG_INFO = 2,
RTMP_LOG_INFO = 2, RTMP_LOG_DEBUG = 3
RTMP_LOG_DEBUG = 3
}; };
struct RtmpStreamStats struct RtmpStreamStats {
{ uint64_t bytes_sent;
uint64_t bytes_sent; uint64_t bytes_received;
uint64_t bytes_received; uint32_t video_frames;
uint32_t video_frames; uint32_t audio_frames;
uint32_t audio_frames; uint32_t dropped_frames;
uint32_t dropped_frames; double bitrate_kbps;
double bitrate_kbps; double uptime_seconds;
double uptime_seconds;
}; };
typedef void (*RtmpOnConnectCallback)(const char* client_ip, void* user_data); typedef void (*RtmpOnConnectCallback)(const char *client_ip, void *user_data);
typedef void (*RtmpOnPublishCallback)(const char* client_ip, const char* app, typedef void (*RtmpOnPublishCallback)(const char *client_ip, const char *app,
const char* stream_key, void* user_data); const char *stream_key, void *user_data);
typedef void (*RtmpOnPlayCallback)(const char* client_ip, const char* app, typedef void (*RtmpOnPlayCallback)(const char *client_ip, const char *app,
const char* stream_key, void* user_data); const char *stream_key, void *user_data);
typedef void (*RtmpOnAudioDataCallback)(const char* app, const char* stream_key, typedef void (*RtmpOnAudioDataCallback)(const char *app, const char *stream_key,
const uint8_t* data, uint32_t length, uint32_t timestamp, void* user_data); const uint8_t *data, uint32_t length,
typedef void (*RtmpOnVideoDataCallback)(const char* app, const char* stream_key, uint32_t timestamp, void *user_data);
const uint8_t* data, uint32_t length, uint32_t timestamp, void* user_data); typedef void (*RtmpOnVideoDataCallback)(const char *app, const char *stream_key,
typedef void (*RtmpOnDisconnectCallback)(const char* client_ip, const char* app, const uint8_t *data, uint32_t length,
const char* stream_key, bool was_publishing, bool was_playing, void* user_data); uint32_t timestamp, void *user_data);
typedef bool (*RtmpAuthCallback)(const char* app, const char* stream_key, typedef void (*RtmpOnDisconnectCallback)(const char *client_ip, const char *app,
const char* client_ip, void* user_data); const char *stream_key,
bool was_publishing, bool was_playing,
void *user_data);
typedef bool (*RtmpAuthCallback)(const char *app, const char *stream_key,
const char *client_ip, void *user_data);
// Create and destroy // Create and destroy
RtmpServerHandle rtmp_server_create(int port); RtmpServerHandle rtmp_server_create(int port);
@@ -52,24 +54,25 @@ bool rtmp_server_is_running(RtmpServerHandle handle);
// Callbacks // Callbacks
void rtmp_server_set_on_connect(RtmpServerHandle handle, void rtmp_server_set_on_connect(RtmpServerHandle handle,
RtmpOnConnectCallback cb, void* user_data); RtmpOnConnectCallback cb, void *user_data);
void rtmp_server_set_on_publish(RtmpServerHandle handle, void rtmp_server_set_on_publish(RtmpServerHandle handle,
RtmpOnPublishCallback cb, void* user_data); RtmpOnPublishCallback cb, void *user_data);
void rtmp_server_set_on_play(RtmpServerHandle handle, RtmpOnPlayCallback cb, void rtmp_server_set_on_play(RtmpServerHandle handle, RtmpOnPlayCallback cb,
void* user_data); void *user_data);
void rtmp_server_set_on_audio_data(RtmpServerHandle handle, void rtmp_server_set_on_audio_data(RtmpServerHandle handle,
RtmpOnAudioDataCallback cb, void* user_data); RtmpOnAudioDataCallback cb, void *user_data);
void rtmp_server_set_on_video_data(RtmpServerHandle handle, void rtmp_server_set_on_video_data(RtmpServerHandle handle,
RtmpOnVideoDataCallback cb, void* user_data); RtmpOnVideoDataCallback cb, void *user_data);
void rtmp_server_set_on_disconnect(RtmpServerHandle handle, void rtmp_server_set_on_disconnect(RtmpServerHandle handle,
RtmpOnDisconnectCallback cb, void* user_data); RtmpOnDisconnectCallback cb,
void *user_data);
void rtmp_server_set_auth_callback(RtmpServerHandle handle, RtmpAuthCallback cb, void rtmp_server_set_auth_callback(RtmpServerHandle handle, RtmpAuthCallback cb,
void* user_data); void *user_data);
// Configuration // Configuration
void rtmp_server_enable_gop_cache(RtmpServerHandle handle, bool enable); void rtmp_server_enable_gop_cache(RtmpServerHandle handle, bool enable);
void rtmp_server_set_max_publishers_per_stream(RtmpServerHandle handle, void rtmp_server_set_max_publishers_per_stream(RtmpServerHandle handle,
int max); int max);
void rtmp_server_set_max_players_per_stream(RtmpServerHandle handle, int max); void rtmp_server_set_max_players_per_stream(RtmpServerHandle handle, int max);
void rtmp_server_set_max_total_connections(RtmpServerHandle handle, int max); void rtmp_server_set_max_total_connections(RtmpServerHandle handle, int max);
void rtmp_server_set_connection_timeout(RtmpServerHandle handle, int seconds); void rtmp_server_set_connection_timeout(RtmpServerHandle handle, int seconds);
@@ -81,26 +84,28 @@ int rtmp_server_get_active_publishers(RtmpServerHandle handle);
int rtmp_server_get_active_players(RtmpServerHandle handle); int rtmp_server_get_active_players(RtmpServerHandle handle);
int rtmp_server_get_total_connections(RtmpServerHandle handle); int rtmp_server_get_total_connections(RtmpServerHandle handle);
struct RtmpStreamStats rtmp_server_get_stream_stats(RtmpServerHandle handle, struct RtmpStreamStats rtmp_server_get_stream_stats(RtmpServerHandle handle,
const char* app, const char* stream_key); const char *app,
const char *stream_key);
// Recording // Recording
bool rtmp_server_start_recording(RtmpServerHandle handle, const char* app, bool rtmp_server_start_recording(RtmpServerHandle handle, const char *app,
const char* stream_key, const char* filename); const char *stream_key, const char *filename);
void rtmp_server_stop_recording(RtmpServerHandle handle, const char* app, void rtmp_server_stop_recording(RtmpServerHandle handle, const char *app,
const char* stream_key); const char *stream_key);
bool rtmp_server_is_recording(RtmpServerHandle handle, const char* app, bool rtmp_server_is_recording(RtmpServerHandle handle, const char *app,
const char* stream_key); const char *stream_key);
// Broadcasting // Broadcasting
bool rtmp_server_broadcast_audio(RtmpServerHandle handle, const char* app, bool rtmp_server_broadcast_audio(RtmpServerHandle handle, const char *app,
const char* stream_key, const uint8_t* data, uint32_t length, const char *stream_key, const uint8_t *data,
uint32_t timestamp); uint32_t length, uint32_t timestamp);
bool rtmp_server_broadcast_video(RtmpServerHandle handle, const char* app, bool rtmp_server_broadcast_video(RtmpServerHandle handle, const char *app,
const char* stream_key, const uint8_t* data, uint32_t length, const char *stream_key, const uint8_t *data,
uint32_t timestamp); uint32_t length, uint32_t timestamp);
// FIXED: Added missing declaration // FIXED: Added missing declaration
bool rtmp_server_broadcast_metadata(RtmpServerHandle handle, const char* app, bool rtmp_server_broadcast_metadata(RtmpServerHandle handle, const char *app,
const char* stream_key, const uint8_t* data, uint32_t length); const char *stream_key, const uint8_t *data,
uint32_t length);
// Logger // Logger
void rtmp_logger_set_level(enum RtmpLogLevel level); void rtmp_logger_set_level(enum RtmpLogLevel level);

View File

@@ -1,586 +1,468 @@
#ifndef RTMP_SERVER_H #ifndef RTMP_SERVER_H
#define RTMP_SERVER_H #define RTMP_SERVER_H
#include <string> #include <arpa/inet.h>
#include <vector> #include <chrono>
#include <cstdint>
#include <cstring>
#include <fcntl.h>
#include <fstream>
#include <functional>
#include <iostream>
#include <map> #include <map>
#include <memory> #include <memory>
#include <functional>
#include <cstdint>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>
#include <iostream>
#include <thread>
#include <mutex> #include <mutex>
#include <netinet/in.h>
#include <queue> #include <queue>
#include <chrono> #include <string>
#include <fstream> #include <sys/socket.h>
#include <thread>
#include <unistd.h>
#include <vector>
namespace rtmp namespace rtmp {
{
// RTMP Message Types // RTMP Message Types
enum class MessageType : uint8_t enum class MessageType : uint8_t {
{ SET_CHUNK_SIZE = 1,
SET_CHUNK_SIZE = 1, ABORT_MESSAGE = 2,
ABORT_MESSAGE = 2, ACKNOWLEDGEMENT = 3,
ACKNOWLEDGEMENT = 3, USER_CONTROL = 4,
USER_CONTROL = 4, WINDOW_ACK_SIZE = 5,
WINDOW_ACK_SIZE = 5, SET_PEER_BANDWIDTH = 6,
SET_PEER_BANDWIDTH = 6, AUDIO = 8,
AUDIO = 8, VIDEO = 9,
VIDEO = 9, DATA_AMF3 = 15,
DATA_AMF3 = 15, SHARED_OBJECT_AMF3 = 16,
SHARED_OBJECT_AMF3 = 16, COMMAND_AMF3 = 17,
COMMAND_AMF3 = 17, DATA_AMF0 = 18,
DATA_AMF0 = 18, SHARED_OBJECT_AMF0 = 19,
SHARED_OBJECT_AMF0 = 19, COMMAND_AMF0 = 20,
COMMAND_AMF0 = 20, AGGREGATE = 22
AGGREGATE = 22 };
};
// User Control Message Types // User Control Message Types
enum class UserControlType : uint16_t enum class UserControlType : uint16_t {
{ STREAM_BEGIN = 0,
STREAM_BEGIN = 0, STREAM_EOF = 1,
STREAM_EOF = 1, STREAM_DRY = 2,
STREAM_DRY = 2, SET_BUFFER_LENGTH = 3,
SET_BUFFER_LENGTH = 3, STREAM_IS_RECORDED = 4,
STREAM_IS_RECORDED = 4, PING_REQUEST = 6,
PING_REQUEST = 6, PING_RESPONSE = 7
PING_RESPONSE = 7 };
};
// AMF0 Data Types // AMF0 Data Types
enum class AMF0Type : uint8_t enum class AMF0Type : uint8_t {
{ NUMBER = 0x00,
NUMBER = 0x00, BOOLEAN = 0x01,
BOOLEAN = 0x01, STRING = 0x02,
STRING = 0x02, OBJECT = 0x03,
OBJECT = 0x03, NULL_TYPE = 0x05,
NULL_TYPE = 0x05, UNDEFINED = 0x06,
UNDEFINED = 0x06, ECMA_ARRAY = 0x08,
ECMA_ARRAY = 0x08, OBJECT_END = 0x09
OBJECT_END = 0x09 };
};
// Log Levels // Log Levels
enum class LogLevel enum class LogLevel { ERROR = 0, WARN = 1, INFO = 2, DEBUG = 3 };
{
ERROR = 0,
WARN = 1,
INFO = 2,
DEBUG = 3
};
// AMF0 Value // AMF0 Value
class AMF0Value class AMF0Value {
{ public:
public: AMF0Type type;
AMF0Type type; double number;
double number; bool boolean;
bool boolean; std::string string;
std::string string; std::map<std::string, std::shared_ptr<AMF0Value>> object;
std::map<std::string, std::shared_ptr<AMF0Value>> object;
AMF0Value() : type(AMF0Type::NULL_TYPE), number(0), boolean(false) {} AMF0Value() : type(AMF0Type::NULL_TYPE), number(0), boolean(false) {}
}; };
// RTMP Chunk Header // RTMP Chunk Header
struct ChunkHeader struct ChunkHeader {
{ uint8_t fmt;
uint8_t fmt; uint32_t csid;
uint32_t csid; uint32_t timestamp;
uint32_t timestamp; uint32_t msg_length;
uint32_t msg_length; uint8_t msg_type_id;
uint8_t msg_type_id; uint32_t msg_stream_id;
uint32_t msg_stream_id; bool has_extended_timestamp;
bool has_extended_timestamp; };
};
// RTMP Message // RTMP Message
struct RTMPMessage struct RTMPMessage {
{ ChunkHeader header;
ChunkHeader header; std::vector<uint8_t> payload;
std::vector<uint8_t> payload; };
};
// Stream Information // Stream Information
struct StreamInfo struct StreamInfo {
{ std::string app;
std::string app; std::string stream_key;
std::string stream_key; bool is_publishing;
bool is_publishing; bool is_playing;
bool is_playing; int client_fd;
int client_fd; uint32_t stream_id;
uint32_t stream_id; std::string client_ip;
std::string client_ip; };
};
// Stream Statistics // Stream Statistics
struct StreamStatistics struct StreamStatistics {
{ uint64_t bytes_sent = 0;
uint64_t bytes_sent = 0; uint64_t bytes_received = 0;
uint64_t bytes_received = 0; uint32_t video_frames = 0;
uint32_t video_frames = 0; uint32_t audio_frames = 0;
uint32_t audio_frames = 0; uint32_t dropped_frames = 0;
uint32_t dropped_frames = 0; std::chrono::steady_clock::time_point start_time;
std::chrono::steady_clock::time_point start_time;
StreamStatistics() : start_time(std::chrono::steady_clock::now()) {} StreamStatistics() : start_time(std::chrono::steady_clock::now()) {}
double getBitrate() const double getBitrate() const {
{ auto now = std::chrono::steady_clock::now();
auto now = std::chrono::steady_clock::now(); auto duration =
auto duration = std::chrono::duration_cast<std::chrono::seconds>( std::chrono::duration_cast<std::chrono::seconds>(now - start_time)
now - start_time).count(); .count();
if (duration == 0) return 0; if (duration == 0)
return (bytes_sent * 8.0) / duration / 1000.0; // kbps return 0;
} return (bytes_sent * 8.0) / duration / 1000.0; // kbps
}
double getUptime() const double getUptime() const {
{ auto now = std::chrono::steady_clock::now();
auto now = std::chrono::steady_clock::now(); return std::chrono::duration_cast<std::chrono::seconds>(now - start_time)
return std::chrono::duration_cast<std::chrono::seconds>( .count();
now - start_time).count(); }
} };
};
// GOP Cache for instant playback // GOP Cache for instant playback
class GOPCache class GOPCache {
{ public:
public: void addVideoFrame(const std::vector<uint8_t> &data, uint32_t timestamp);
void addVideoFrame(const std::vector<uint8_t> &data, uint32_t timestamp); void addAudioFrame(const std::vector<uint8_t> &data, uint32_t timestamp);
void addAudioFrame(const std::vector<uint8_t> &data, uint32_t timestamp); void addMetadata(const std::vector<uint8_t> &data);
void addMetadata(const std::vector<uint8_t> &data); void sendToPlayer(class RTMPSession *session);
void sendToPlayer(class RTMPSession* session); void clear();
void clear(); bool hasKeyframe() const { return has_keyframe; }
bool hasKeyframe() const
{
return has_keyframe;
}
private: private:
struct CachedFrame struct CachedFrame {
{ MessageType type;
MessageType type; std::vector<uint8_t> data;
std::vector<uint8_t> data; uint32_t timestamp;
uint32_t timestamp; };
};
std::vector<CachedFrame> frames; std::vector<CachedFrame> frames;
std::vector<uint8_t> metadata; std::vector<uint8_t> metadata;
bool has_keyframe = false; bool has_keyframe = false;
std::mutex cache_mutex; std::mutex cache_mutex;
bool isKeyframe(const std::vector<uint8_t> &data); bool isKeyframe(const std::vector<uint8_t> &data);
}; };
// FLV File Recorder // FLV File Recorder
class FLVRecorder class FLVRecorder {
{ public:
public: FLVRecorder(const std::string &filename);
FLVRecorder(const std::string& filename); ~FLVRecorder();
~FLVRecorder();
bool start(); bool start();
void stop(); void stop();
bool isRecording() const bool isRecording() const { return recording; }
{
return recording;
}
void writeAudioFrame(const std::vector<uint8_t> &data, uint32_t timestamp); void writeAudioFrame(const std::vector<uint8_t> &data, uint32_t timestamp);
void writeVideoFrame(const std::vector<uint8_t> &data, uint32_t timestamp); void writeVideoFrame(const std::vector<uint8_t> &data, uint32_t timestamp);
void writeMetadata(const std::map<std::string, std::shared_ptr<AMF0Value>> void writeMetadata(
&metadata); const std::map<std::string, std::shared_ptr<AMF0Value>> &metadata);
private: private:
std::string filename; std::string filename;
std::ofstream file; std::ofstream file;
bool recording = false; bool recording = false;
uint32_t last_timestamp = 0; uint32_t last_timestamp = 0;
std::mutex file_mutex; std::mutex file_mutex;
void writeFLVHeader(); void writeFLVHeader();
void writeFLVTag(uint8_t tag_type, const std::vector<uint8_t> &data, void writeFLVTag(uint8_t tag_type, const std::vector<uint8_t> &data,
uint32_t timestamp); uint32_t timestamp);
std::vector<uint8_t> encodeMetadata(const std::vector<uint8_t> encodeMetadata(
std::map<std::string, std::shared_ptr<AMF0Value>> &metadata); const std::map<std::string, std::shared_ptr<AMF0Value>> &metadata);
}; };
// RTMP Client Session // RTMP Client Session
class RTMPSession class RTMPSession {
{ public:
public: RTMPSession(int fd, const std::string &client_ip);
RTMPSession(int fd, const std::string& client_ip); ~RTMPSession();
~RTMPSession();
bool handshake(); bool handshake();
bool receiveChunk(); bool receiveChunk();
bool sendMessage(const RTMPMessage& msg); bool sendMessage(const RTMPMessage &msg);
bool sendChunk(uint32_t csid, uint32_t timestamp, uint8_t msg_type, bool sendChunk(uint32_t csid, uint32_t timestamp, uint8_t msg_type,
uint32_t stream_id, const std::vector<uint8_t> &data); uint32_t stream_id, const std::vector<uint8_t> &data);
int getFd() const int getFd() const { return client_fd; }
{ const StreamInfo &getStreamInfo() const { return stream_info; }
return client_fd; StreamInfo &getStreamInfo() { return stream_info; }
}
const StreamInfo &getStreamInfo() const
{
return stream_info;
}
StreamInfo &getStreamInfo()
{
return stream_info;
}
void setChunkSize(uint32_t size) void setChunkSize(uint32_t size) { chunk_size = size; }
{ uint32_t getChunkSize() const { return chunk_size; }
chunk_size = size;
}
uint32_t getChunkSize() const
{
return chunk_size;
}
// Acknowledgement handling // Acknowledgement handling
void onBytesReceived(size_t bytes); void onBytesReceived(size_t bytes);
bool shouldSendAck() const; bool shouldSendAck() const;
void sendAcknowledgement(); void sendAcknowledgement();
// Ping/Pong // Ping/Pong
void sendPing(uint32_t timestamp); void sendPing(uint32_t timestamp);
void sendPong(uint32_t timestamp); void sendPong(uint32_t timestamp);
// Statistics // Statistics
StreamStatistics &getStats() StreamStatistics &getStats() { return stats; }
{ const StreamStatistics &getStats() const { return stats; }
return stats;
}
const StreamStatistics &getStats() const
{
return stats;
}
// Message queue access for server // Message queue access for server
std::queue<RTMPMessage> &getMessageQueue() std::queue<RTMPMessage> &getMessageQueue() { return message_queue; }
{ std::mutex &getQueueMutex() { return queue_mutex; }
return message_queue;
}
std::mutex &getQueueMutex()
{
return queue_mutex;
}
// Last activity tracking // Last activity tracking
std::chrono::steady_clock::time_point getLastActivity() const std::chrono::steady_clock::time_point getLastActivity() const {
{ return last_activity;
return last_activity; }
} void updateActivity() { last_activity = std::chrono::steady_clock::now(); }
void updateActivity()
{
last_activity = std::chrono::steady_clock::now();
}
// AMF0 public access for server // AMF0 public access for server
std::shared_ptr<AMF0Value> decodeAMF0(const uint8_t* data, size_t len, std::shared_ptr<AMF0Value> decodeAMF0(const uint8_t *data, size_t len,
size_t &offset); size_t &offset);
std::vector<uint8_t> encodeAMF0(const AMF0Value& value); std::vector<uint8_t> encodeAMF0(const AMF0Value &value);
// FIXED: Moved to public section for RTMPServer access // FIXED: Moved to public section for RTMPServer access
bool sendErrorResponse(const std::string& command, double transaction_id, bool sendErrorResponse(const std::string &command, double transaction_id,
const std::string& description); const std::string &description);
private: private:
int client_fd; int client_fd;
uint32_t chunk_size; uint32_t chunk_size;
uint32_t window_ack_size; uint32_t window_ack_size;
uint32_t peer_bandwidth; uint32_t peer_bandwidth;
uint32_t bytes_received; uint32_t bytes_received;
uint32_t last_ack_sent; uint32_t last_ack_sent;
std::map<uint32_t, ChunkHeader> prev_headers; std::map<uint32_t, ChunkHeader> prev_headers;
std::map<uint32_t, std::vector<uint8_t>> incomplete_chunks; std::map<uint32_t, std::vector<uint8_t>> incomplete_chunks;
StreamInfo stream_info; StreamInfo stream_info;
std::queue<RTMPMessage> message_queue; std::queue<RTMPMessage> message_queue;
std::mutex queue_mutex; std::mutex queue_mutex;
StreamStatistics stats; StreamStatistics stats;
std::chrono::steady_clock::time_point last_activity; std::chrono::steady_clock::time_point last_activity;
bool readExactly(uint8_t* buf, size_t len); bool readExactly(uint8_t *buf, size_t len);
bool writeExactly(const uint8_t* buf, size_t len); bool writeExactly(const uint8_t *buf, size_t len);
bool parseChunkHeader(ChunkHeader& header); bool parseChunkHeader(ChunkHeader &header);
bool processMessage(const RTMPMessage& msg); bool processMessage(const RTMPMessage &msg);
bool handleCommand(const RTMPMessage& msg); bool handleCommand(const RTMPMessage &msg);
bool handleAudioMessage(const RTMPMessage& msg); bool handleAudioMessage(const RTMPMessage &msg);
bool handleVideoMessage(const RTMPMessage& msg); bool handleVideoMessage(const RTMPMessage &msg);
bool handleDataMessage(const RTMPMessage& msg); bool handleDataMessage(const RTMPMessage &msg);
bool handleUserControl(const RTMPMessage& msg); bool handleUserControl(const RTMPMessage &msg);
bool handleAcknowledgement(const RTMPMessage& msg); bool handleAcknowledgement(const RTMPMessage &msg);
// AMF0 Encoding/Decoding // AMF0 Encoding/Decoding
std::vector<uint8_t> encodeAMF0String(const std::string& str); std::vector<uint8_t> encodeAMF0String(const std::string &str);
std::vector<uint8_t> encodeAMF0Number(double num); std::vector<uint8_t> encodeAMF0Number(double num);
std::vector<uint8_t> encodeAMF0Object(const std::vector<uint8_t> encodeAMF0Object(
std::map<std::string, std::shared_ptr<AMF0Value>> &obj); const std::map<std::string, std::shared_ptr<AMF0Value>> &obj);
// Command handlers // Command handlers
bool handleConnect(const std::vector<std::shared_ptr<AMF0Value>> &args); bool handleConnect(const std::vector<std::shared_ptr<AMF0Value>> &args);
bool handleReleaseStream(const std::vector<std::shared_ptr<AMF0Value>> &args); bool handleReleaseStream(const std::vector<std::shared_ptr<AMF0Value>> &args);
bool handleFCPublish(const std::vector<std::shared_ptr<AMF0Value>> &args); bool handleFCPublish(const std::vector<std::shared_ptr<AMF0Value>> &args);
bool handleCreateStream(const std::vector<std::shared_ptr<AMF0Value>> &args); bool handleCreateStream(const std::vector<std::shared_ptr<AMF0Value>> &args);
bool handlePublish(const std::vector<std::shared_ptr<AMF0Value>> &args); bool handlePublish(const std::vector<std::shared_ptr<AMF0Value>> &args);
bool handlePlay(const std::vector<std::shared_ptr<AMF0Value>> &args); bool handlePlay(const std::vector<std::shared_ptr<AMF0Value>> &args);
bool handleDeleteStream(const std::vector<std::shared_ptr<AMF0Value>> &args); bool handleDeleteStream(const std::vector<std::shared_ptr<AMF0Value>> &args);
// Response helpers // Response helpers
bool sendConnectResponse(double transaction_id); bool sendConnectResponse(double transaction_id);
bool sendCreateStreamResponse(double transaction_id, double stream_id); bool sendCreateStreamResponse(double transaction_id, double stream_id);
bool sendPublishResponse(); bool sendPublishResponse();
bool sendPlayResponse(); bool sendPlayResponse();
}; };
// Callback types // Callback types
using OnConnectCallback = std::function<void(std::shared_ptr<RTMPSession>)>; using OnConnectCallback = std::function<void(std::shared_ptr<RTMPSession>)>;
using OnPublishCallback = using OnPublishCallback =
std::function<void(std::shared_ptr<RTMPSession>, const std::string& app, const std::string& stream_key)>; std::function<void(std::shared_ptr<RTMPSession>, const std::string &app,
using OnPlayCallback = const std::string &stream_key)>;
std::function<void(std::shared_ptr<RTMPSession>, const std::string& app, const std::string& stream_key)>; using OnPlayCallback =
using OnAudioDataCallback = std::function<void(std::shared_ptr<RTMPSession>, const std::string &app,
std::function<void(std::shared_ptr<RTMPSession>, const std::vector<uint8_t>& data, uint32_t timestamp)>; const std::string &stream_key)>;
using OnVideoDataCallback = using OnAudioDataCallback =
std::function<void(std::shared_ptr<RTMPSession>, const std::vector<uint8_t>& data, uint32_t timestamp)>; std::function<void(std::shared_ptr<RTMPSession>,
using OnMetaDataCallback = const std::vector<uint8_t> &data, uint32_t timestamp)>;
std::function<void(std::shared_ptr<RTMPSession>, const std::map<std::string, std::shared_ptr<AMF0Value>>& metadata)>; using OnVideoDataCallback =
using OnDisconnectCallback = std::function<void(std::shared_ptr<RTMPSession>)>; std::function<void(std::shared_ptr<RTMPSession>,
using AuthCallback = const std::vector<uint8_t> &data, uint32_t timestamp)>;
std::function<bool(const std::string& app, const std::string& stream_key, const std::string& client_ip)>; using OnMetaDataCallback = std::function<void(
std::shared_ptr<RTMPSession>,
const std::map<std::string, std::shared_ptr<AMF0Value>> &metadata)>;
using OnDisconnectCallback = std::function<void(std::shared_ptr<RTMPSession>)>;
using AuthCallback =
std::function<bool(const std::string &app, const std::string &stream_key,
const std::string &client_ip)>;
// Logger // Logger
class Logger class Logger {
{ public:
public: static Logger &getInstance() {
static Logger &getInstance() static Logger instance;
{ return instance;
static Logger instance; }
return instance;
}
void setLevel(LogLevel level) void setLevel(LogLevel level) { current_level = level; }
{ LogLevel getLevel() const { return current_level; }
current_level = level;
}
LogLevel getLevel() const
{
return current_level;
}
void error(const std::string& msg); void error(const std::string &msg);
void warn(const std::string& msg); void warn(const std::string &msg);
void info(const std::string& msg); void info(const std::string &msg);
void debug(const std::string& msg); void debug(const std::string &msg);
private: private:
Logger() : current_level(LogLevel::INFO) {} Logger() : current_level(LogLevel::INFO) {}
LogLevel current_level; LogLevel current_level;
std::mutex log_mutex; std::mutex log_mutex;
void log(LogLevel level, const std::string& msg); void log(LogLevel level, const std::string &msg);
}; };
// RTMP Server // RTMP Server
class RTMPServer class RTMPServer {
{ public:
public: RTMPServer(int port = 1935);
RTMPServer(int port = 1935); ~RTMPServer();
~RTMPServer();
bool start(); bool start();
void stop(); void stop();
bool isRunning() const bool isRunning() const { return running; }
{
return running;
}
// Callbacks // Callbacks
void setOnConnect(OnConnectCallback cb) void setOnConnect(OnConnectCallback cb) { on_connect = cb; }
{ void setOnPublish(OnPublishCallback cb) { on_publish = cb; }
on_connect = cb; void setOnPlay(OnPlayCallback cb) { on_play = cb; }
} void setOnAudioData(OnAudioDataCallback cb) { on_audio_data = cb; }
void setOnPublish(OnPublishCallback cb) void setOnVideoData(OnVideoDataCallback cb) { on_video_data = cb; }
{ void setOnMetaData(OnMetaDataCallback cb) { on_metadata = cb; }
on_publish = cb; void setOnDisconnect(OnDisconnectCallback cb) { on_disconnect = cb; }
} void setAuthCallback(AuthCallback cb) { auth_callback = cb; }
void setOnPlay(OnPlayCallback cb)
{
on_play = cb;
}
void setOnAudioData(OnAudioDataCallback cb)
{
on_audio_data = cb;
}
void setOnVideoData(OnVideoDataCallback cb)
{
on_video_data = cb;
}
void setOnMetaData(OnMetaDataCallback cb)
{
on_metadata = cb;
}
void setOnDisconnect(OnDisconnectCallback cb)
{
on_disconnect = cb;
}
void setAuthCallback(AuthCallback cb)
{
auth_callback = cb;
}
// GOP Cache // GOP Cache
void enableGOPCache(bool enable) void enableGOPCache(bool enable) { use_gop_cache = enable; }
{ bool isGOPCacheEnabled() const { return use_gop_cache; }
use_gop_cache = enable;
}
bool isGOPCacheEnabled() const
{
return use_gop_cache;
}
// Recording // Recording
bool startRecording(const std::string& app, const std::string& stream_key, bool startRecording(const std::string &app, const std::string &stream_key,
const std::string& filename); const std::string &filename);
void stopRecording(const std::string& app, const std::string& stream_key); void stopRecording(const std::string &app, const std::string &stream_key);
bool isRecording(const std::string& app, const std::string& stream_key) const; bool isRecording(const std::string &app, const std::string &stream_key) const;
// Statistics // Statistics
StreamStatistics getStreamStats(const std::string& app, StreamStatistics getStreamStats(const std::string &app,
const std::string& stream_key) const; const std::string &stream_key) const;
std::vector<std::pair<std::string, StreamStatistics>> getAllStreamStats() const; std::vector<std::pair<std::string, StreamStatistics>>
int getActivePublishers() const; getAllStreamStats() const;
int getActivePlayers() const; int getActivePublishers() const;
int getTotalConnections() const; int getActivePlayers() const;
int getTotalConnections() const;
// Connection limits // Connection limits
void setMaxPublishersPerStream(int max) void setMaxPublishersPerStream(int max) { max_publishers_per_stream = max; }
{ void setMaxPlayersPerStream(int max) { max_players_per_stream = max; }
max_publishers_per_stream = max; void setMaxTotalConnections(int max) { max_total_connections = max; }
} int getMaxPublishersPerStream() const { return max_publishers_per_stream; }
void setMaxPlayersPerStream(int max) int getMaxPlayersPerStream() const { return max_players_per_stream; }
{ int getMaxTotalConnections() const { return max_total_connections; }
max_players_per_stream = max;
}
void setMaxTotalConnections(int max)
{
max_total_connections = max;
}
int getMaxPublishersPerStream() const
{
return max_publishers_per_stream;
}
int getMaxPlayersPerStream() const
{
return max_players_per_stream;
}
int getMaxTotalConnections() const
{
return max_total_connections;
}
// Ping/Pong // Ping/Pong
void enablePingPong(bool enable, int interval_seconds = 30); void enablePingPong(bool enable, int interval_seconds = 30);
bool isPingPongEnabled() const bool isPingPongEnabled() const { return ping_enabled; }
{
return ping_enabled;
}
// Timeout handling // Timeout handling
void setConnectionTimeout(int seconds) void setConnectionTimeout(int seconds) { connection_timeout = seconds; }
{ int getConnectionTimeout() const { return connection_timeout; }
connection_timeout = seconds;
}
int getConnectionTimeout() const
{
return connection_timeout;
}
// Broadcasting // Broadcasting
bool sendAudioToPlayers(const std::string& app, const std::string& stream_key, bool sendAudioToPlayers(const std::string &app, const std::string &stream_key,
const std::vector<uint8_t> &data, uint32_t timestamp); const std::vector<uint8_t> &data, uint32_t timestamp);
bool sendVideoToPlayers(const std::string& app, const std::string& stream_key, bool sendVideoToPlayers(const std::string &app, const std::string &stream_key,
const std::vector<uint8_t> &data, uint32_t timestamp); const std::vector<uint8_t> &data, uint32_t timestamp);
void sendMetadataToPlayers(const std::string& app, void sendMetadataToPlayers(const std::string &app,
const std::string& stream_key, const std::string &stream_key,
const std::vector<uint8_t> &data); const std::vector<uint8_t> &data);
private: private:
int port; int port;
int server_fd; int server_fd;
bool running; bool running;
std::thread accept_thread; std::thread accept_thread;
std::thread ping_thread; std::thread ping_thread;
std::thread timeout_thread; std::thread timeout_thread;
std::vector<std::thread> client_threads; std::vector<std::thread> client_threads;
std::vector<std::shared_ptr<RTMPSession>> sessions; std::vector<std::shared_ptr<RTMPSession>> sessions;
mutable std::mutex sessions_mutex; // FIXED: Added mutable mutable std::mutex sessions_mutex; // FIXED: Added mutable
// Callbacks // Callbacks
OnConnectCallback on_connect; OnConnectCallback on_connect;
OnPublishCallback on_publish; OnPublishCallback on_publish;
OnPlayCallback on_play; OnPlayCallback on_play;
OnAudioDataCallback on_audio_data; OnAudioDataCallback on_audio_data;
OnVideoDataCallback on_video_data; OnVideoDataCallback on_video_data;
OnMetaDataCallback on_metadata; OnMetaDataCallback on_metadata;
OnDisconnectCallback on_disconnect; OnDisconnectCallback on_disconnect;
AuthCallback auth_callback; AuthCallback auth_callback;
// GOP Caches // GOP Caches
bool use_gop_cache = true; bool use_gop_cache = true;
std::map<std::string, std::shared_ptr<GOPCache>> gop_caches; std::map<std::string, std::shared_ptr<GOPCache>> gop_caches;
std::mutex gop_mutex; std::mutex gop_mutex;
// Recorders // Recorders
std::map<std::string, std::shared_ptr<FLVRecorder>> recorders; std::map<std::string, std::shared_ptr<FLVRecorder>> recorders;
mutable std::mutex recorder_mutex; // FIXED: Added mutable mutable std::mutex recorder_mutex; // FIXED: Added mutable
// Statistics // Statistics
std::map<std::string, StreamStatistics> stream_stats; std::map<std::string, StreamStatistics> stream_stats;
mutable std::mutex stats_mutex; mutable std::mutex stats_mutex;
// Connection limits // Connection limits
int max_publishers_per_stream = 1; int max_publishers_per_stream = 1;
int max_players_per_stream = 1000; int max_players_per_stream = 1000;
int max_total_connections = 1000; int max_total_connections = 1000;
// Ping/Pong // Ping/Pong
bool ping_enabled = false; bool ping_enabled = false;
int ping_interval = 30; int ping_interval = 30;
// Timeout // Timeout
int connection_timeout = 60; int connection_timeout = 60;
void acceptClients(); void acceptClients();
void handleClient(std::shared_ptr<RTMPSession> session); void handleClient(std::shared_ptr<RTMPSession> session);
void removeSession(std::shared_ptr<RTMPSession> session); void removeSession(std::shared_ptr<RTMPSession> session);
void processMediaMessages(std::shared_ptr<RTMPSession> session); void processMediaMessages(std::shared_ptr<RTMPSession> session);
void pingClientsRoutine(); void pingClientsRoutine();
void timeoutCheckRoutine(); void timeoutCheckRoutine();
std::string makeStreamKey(const std::string& app, std::string makeStreamKey(const std::string &app,
const std::string& stream) const const std::string &stream) const {
{ return app + "/" + stream;
return app + "/" + stream; }
}
int countPublishers(const std::string& app, int countPublishers(const std::string &app,
const std::string& stream_key) const; const std::string &stream_key) const;
int countPlayers(const std::string& app, const std::string& stream_key) const; int countPlayers(const std::string &app, const std::string &stream_key) const;
bool checkConnectionLimits(const std::string& app, bool checkConnectionLimits(const std::string &app,
const std::string& stream_key, bool is_publisher) const; const std::string &stream_key,
}; bool is_publisher) const;
};
// Utility macros // Utility macros
#define LOG_ERROR(msg) rtmp::Logger::getInstance().error(msg) #define LOG_ERROR(msg) rtmp::Logger::getInstance().error(msg)

View File

@@ -1,294 +1,303 @@
#include "rtmp_capi.h" #include "../include/rtmp_capi.h"
#include "rtmp_server.h" #include "../include/rtmp_server.h"
#include <cstdlib>
#include <vector>
#include <string> #include <string>
#include <vector>
namespace rtmp_capi_internal namespace rtmp_capi_internal {
{ struct RtmpServerImpl {
struct RtmpServerImpl rtmp::RTMPServer *server;
{ RtmpOnConnectCallback on_connect_cb;
rtmp::RTMPServer *server; void *on_connect_userdata;
RtmpOnConnectCallback on_connect_cb; RtmpOnPublishCallback on_publish_cb;
void *on_connect_userdata; void *on_publish_userdata;
RtmpOnPublishCallback on_publish_cb; RtmpOnPlayCallback on_play_cb;
void *on_publish_userdata; void *on_play_userdata;
RtmpOnPlayCallback on_play_cb; RtmpOnAudioDataCallback on_audio_cb;
void *on_play_userdata; void *on_audio_userdata;
RtmpOnAudioDataCallback on_audio_cb; RtmpOnVideoDataCallback on_video_cb;
void *on_audio_userdata; void *on_video_userdata;
RtmpOnVideoDataCallback on_video_cb; RtmpOnDisconnectCallback on_disconnect_cb;
void *on_video_userdata; void *on_disconnect_userdata;
RtmpOnDisconnectCallback on_disconnect_cb; RtmpAuthCallback auth_cb;
void *on_disconnect_userdata; void *auth_userdata;
RtmpAuthCallback auth_cb; RtmpServerImpl()
void *auth_userdata; : server(nullptr), on_connect_cb(nullptr), on_connect_userdata(nullptr),
RtmpServerImpl() : server(nullptr), on_connect_cb(nullptr), on_publish_cb(nullptr), on_publish_userdata(nullptr),
on_connect_userdata(nullptr), on_play_cb(nullptr), on_play_userdata(nullptr), on_audio_cb(nullptr),
on_publish_cb(nullptr), on_publish_userdata(nullptr), on_play_cb(nullptr), on_audio_userdata(nullptr), on_video_cb(nullptr),
on_play_userdata(nullptr), on_video_userdata(nullptr), on_disconnect_cb(nullptr),
on_audio_cb(nullptr), on_audio_userdata(nullptr), on_video_cb(nullptr), on_disconnect_userdata(nullptr), auth_cb(nullptr),
on_video_userdata(nullptr), auth_userdata(nullptr) {}
on_disconnect_cb(nullptr), on_disconnect_userdata(nullptr), auth_cb(nullptr), };
auth_userdata(nullptr) {} } // namespace rtmp_capi_internal
};
}
using Impl = rtmp_capi_internal::RtmpServerImpl; using Impl = rtmp_capi_internal::RtmpServerImpl;
extern "C" { extern "C" {
RtmpServerHandle rtmp_server_create(int port) RtmpServerHandle rtmp_server_create(int port) {
{ Impl *impl = new Impl();
Impl* impl = new Impl(); impl->server = new rtmp::RTMPServer(port);
impl->server = new rtmp::RTMPServer(port); impl->server->setOnConnect(
impl->server->setOnConnect([impl](std::shared_ptr<rtmp::RTMPSession> session) [impl](std::shared_ptr<rtmp::RTMPSession> session) {
{ if (!impl || !impl->on_connect_cb)
if (!impl || !impl->on_connect_cb) return; return;
const auto& info = session->getStreamInfo(); const auto &info = session->getStreamInfo();
impl->on_connect_cb(info.client_ip.c_str(), impl->on_connect_userdata); impl->on_connect_cb(info.client_ip.c_str(), impl->on_connect_userdata);
}); });
impl->server->setOnPublish([impl](std::shared_ptr<rtmp::RTMPSession> session, impl->server->setOnPublish([impl](std::shared_ptr<rtmp::RTMPSession> session,
const std::string & app, const std::string & stream_key) const std::string &app,
{ const std::string &stream_key) {
if (!impl || !impl->on_publish_cb) return; if (!impl || !impl->on_publish_cb)
const auto& info = session->getStreamInfo(); return;
impl->on_publish_cb(info.client_ip.c_str(), app.c_str(), stream_key.c_str(), const auto &info = session->getStreamInfo();
impl->on_publish_userdata); impl->on_publish_cb(info.client_ip.c_str(), app.c_str(), stream_key.c_str(),
}); impl->on_publish_userdata);
impl->server->setOnPlay([impl](std::shared_ptr<rtmp::RTMPSession> session, });
const std::string & app, const std::string & stream_key) impl->server->setOnPlay([impl](std::shared_ptr<rtmp::RTMPSession> session,
{ const std::string &app,
if (!impl || !impl->on_play_cb) return; const std::string &stream_key) {
const auto& info = session->getStreamInfo(); if (!impl || !impl->on_play_cb)
impl->on_play_cb(info.client_ip.c_str(), app.c_str(), stream_key.c_str(), return;
impl->on_play_userdata); const auto &info = session->getStreamInfo();
}); impl->on_play_cb(info.client_ip.c_str(), app.c_str(), stream_key.c_str(),
impl->server->setOnAudioData([impl](std::shared_ptr<rtmp::RTMPSession> session, impl->on_play_userdata);
const std::vector<uint8_t> &data, uint32_t timestamp) });
{ impl->server->setOnAudioData(
if (!impl || !impl->on_audio_cb) return; [impl](std::shared_ptr<rtmp::RTMPSession> session,
const auto& info = session->getStreamInfo(); const std::vector<uint8_t> &data, uint32_t timestamp) {
impl->on_audio_cb(info.app.c_str(), info.stream_key.c_str(), data.data(), if (!impl || !impl->on_audio_cb)
static_cast<uint32_t>(data.size()), timestamp, impl->on_audio_userdata); return;
}); const auto &info = session->getStreamInfo();
impl->server->setOnVideoData([impl](std::shared_ptr<rtmp::RTMPSession> session, impl->on_audio_cb(info.app.c_str(), info.stream_key.c_str(),
const std::vector<uint8_t> &data, uint32_t timestamp) data.data(), static_cast<uint32_t>(data.size()),
{ timestamp, impl->on_audio_userdata);
if (!impl || !impl->on_video_cb) return; });
const auto& info = session->getStreamInfo(); impl->server->setOnVideoData(
impl->on_video_cb(info.app.c_str(), info.stream_key.c_str(), data.data(), [impl](std::shared_ptr<rtmp::RTMPSession> session,
static_cast<uint32_t>(data.size()), timestamp, impl->on_video_userdata); const std::vector<uint8_t> &data, uint32_t timestamp) {
}); if (!impl || !impl->on_video_cb)
impl->server->setOnDisconnect([impl](std::shared_ptr<rtmp::RTMPSession> return;
session) const auto &info = session->getStreamInfo();
{ impl->on_video_cb(info.app.c_str(), info.stream_key.c_str(),
if (!impl || !impl->on_disconnect_cb) return; data.data(), static_cast<uint32_t>(data.size()),
const auto& info = session->getStreamInfo(); timestamp, impl->on_video_userdata);
impl->on_disconnect_cb(info.client_ip.c_str(), info.app.c_str(), });
info.stream_key.c_str(), info.is_publishing, info.is_playing, impl->server->setOnDisconnect(
impl->on_disconnect_userdata); [impl](std::shared_ptr<rtmp::RTMPSession> session) {
}); if (!impl || !impl->on_disconnect_cb)
impl->server->setAuthCallback([impl](const std::string & app, return;
const std::string & stream_key, const std::string & client_ip) -> bool const auto &info = session->getStreamInfo();
{ impl->on_disconnect_cb(info.client_ip.c_str(), info.app.c_str(),
if (!impl || !impl->auth_cb) return true; info.stream_key.c_str(), info.is_publishing,
return impl->auth_cb(app.c_str(), stream_key.c_str(), client_ip.c_str(), impl->auth_userdata); info.is_playing, impl->on_disconnect_userdata);
}); });
return impl; impl->server->setAuthCallback([impl](const std::string &app,
} const std::string &stream_key,
// add all other functions as above const std::string &client_ip) -> bool {
void rtmp_server_destroy(RtmpServerHandle handle) if (!impl || !impl->auth_cb)
{ return true;
if (!handle) return; return impl->auth_cb(app.c_str(), stream_key.c_str(), client_ip.c_str(),
Impl* impl = static_cast<Impl *>(handle); impl->auth_userdata);
delete impl->server; });
delete impl; return impl;
} }
bool rtmp_server_start(RtmpServerHandle handle) // add all other functions as above
{ void rtmp_server_destroy(RtmpServerHandle handle) {
if (!handle) return false; if (!handle)
Impl* impl = static_cast<Impl *>(handle); return;
return impl->server->start(); Impl *impl = static_cast<Impl *>(handle);
} delete impl->server;
void rtmp_server_stop(RtmpServerHandle handle) delete impl;
{ }
if (!handle) return; bool rtmp_server_start(RtmpServerHandle handle) {
Impl* impl = static_cast<Impl *>(handle); if (!handle)
impl->server->stop(); return false;
} Impl *impl = static_cast<Impl *>(handle);
bool rtmp_server_is_running(RtmpServerHandle handle) return impl->server->start();
{ }
if (!handle) return false; void rtmp_server_stop(RtmpServerHandle handle) {
Impl* impl = static_cast<Impl *>(handle); if (!handle)
return impl->server->isRunning(); return;
} Impl *impl = static_cast<Impl *>(handle);
void rtmp_server_set_on_connect(RtmpServerHandle handle, impl->server->stop();
RtmpOnConnectCallback cb, void* user_data) }
{ bool rtmp_server_is_running(RtmpServerHandle handle) {
if (!handle) return; if (!handle)
Impl* impl = static_cast<Impl *>(handle); return false;
impl->on_connect_cb = cb; Impl *impl = static_cast<Impl *>(handle);
impl->on_connect_userdata = user_data; return impl->server->isRunning();
} }
void rtmp_server_set_on_publish(RtmpServerHandle handle, void rtmp_server_set_on_connect(RtmpServerHandle handle,
RtmpOnPublishCallback cb, void* user_data) RtmpOnConnectCallback cb, void *user_data) {
{ if (!handle)
if (!handle) return; return;
Impl* impl = static_cast<Impl *>(handle); Impl *impl = static_cast<Impl *>(handle);
impl->on_publish_cb = cb; impl->on_connect_cb = cb;
impl->on_publish_userdata = user_data; impl->on_connect_userdata = user_data;
} }
void rtmp_server_set_on_play(RtmpServerHandle handle, RtmpOnPlayCallback cb, void rtmp_server_set_on_publish(RtmpServerHandle handle,
void* user_data) RtmpOnPublishCallback cb, void *user_data) {
{ if (!handle)
if (!handle) return; return;
Impl* impl = static_cast<Impl *>(handle); Impl *impl = static_cast<Impl *>(handle);
impl->on_play_cb = cb; impl->on_publish_cb = cb;
impl->on_play_userdata = user_data; impl->on_publish_userdata = user_data;
} }
void rtmp_server_set_on_audio_data(RtmpServerHandle handle, void rtmp_server_set_on_play(RtmpServerHandle handle, RtmpOnPlayCallback cb,
RtmpOnAudioDataCallback cb, void* user_data) void *user_data) {
{ if (!handle)
if (!handle) return; return;
Impl* impl = static_cast<Impl *>(handle); Impl *impl = static_cast<Impl *>(handle);
impl->on_audio_cb = cb; impl->on_play_cb = cb;
impl->on_audio_userdata = user_data; impl->on_play_userdata = user_data;
} }
void rtmp_server_set_on_video_data(RtmpServerHandle handle, void rtmp_server_set_on_audio_data(RtmpServerHandle handle,
RtmpOnVideoDataCallback cb, void* user_data) RtmpOnAudioDataCallback cb,
{ void *user_data) {
if (!handle) return; if (!handle)
Impl* impl = static_cast<Impl *>(handle); return;
impl->on_video_cb = cb; Impl *impl = static_cast<Impl *>(handle);
impl->on_video_userdata = user_data; impl->on_audio_cb = cb;
} impl->on_audio_userdata = user_data;
void rtmp_server_set_on_disconnect(RtmpServerHandle handle, }
RtmpOnDisconnectCallback cb, void* user_data) void rtmp_server_set_on_video_data(RtmpServerHandle handle,
{ RtmpOnVideoDataCallback cb,
if (!handle) return; void *user_data) {
Impl* impl = static_cast<Impl *>(handle); if (!handle)
impl->on_disconnect_cb = cb; return;
impl->on_disconnect_userdata = user_data; Impl *impl = static_cast<Impl *>(handle);
} impl->on_video_cb = cb;
void rtmp_server_set_auth_callback(RtmpServerHandle handle, RtmpAuthCallback cb, impl->on_video_userdata = user_data;
void* user_data) }
{ void rtmp_server_set_on_disconnect(RtmpServerHandle handle,
if (!handle) return; RtmpOnDisconnectCallback cb,
Impl* impl = static_cast<Impl *>(handle); void *user_data) {
impl->auth_cb = cb; if (!handle)
impl->auth_userdata = user_data; return;
} Impl *impl = static_cast<Impl *>(handle);
void rtmp_server_enable_gop_cache(RtmpServerHandle handle, bool enable) impl->on_disconnect_cb = cb;
{ impl->on_disconnect_userdata = user_data;
if (!handle) return; }
static_cast<Impl *>(handle)->server->enableGOPCache(enable); void rtmp_server_set_auth_callback(RtmpServerHandle handle, RtmpAuthCallback cb,
} void *user_data) {
void rtmp_server_set_max_publishers_per_stream(RtmpServerHandle handle, if (!handle)
int max) return;
{ Impl *impl = static_cast<Impl *>(handle);
if (!handle) return; impl->auth_cb = cb;
static_cast<Impl *>(handle)->server->setMaxPublishersPerStream(max); impl->auth_userdata = user_data;
} }
void rtmp_server_set_max_players_per_stream(RtmpServerHandle handle, int max) void rtmp_server_enable_gop_cache(RtmpServerHandle handle, bool enable) {
{ if (!handle)
if (!handle) return; return;
static_cast<Impl *>(handle)->server->setMaxPlayersPerStream(max); static_cast<Impl *>(handle)->server->enableGOPCache(enable);
} }
void rtmp_server_set_max_total_connections(RtmpServerHandle handle, int max) void rtmp_server_set_max_publishers_per_stream(RtmpServerHandle handle,
{ int max) {
if (!handle) return; if (!handle)
static_cast<Impl *>(handle)->server->setMaxTotalConnections(max); return;
} static_cast<Impl *>(handle)->server->setMaxPublishersPerStream(max);
void rtmp_server_set_connection_timeout(RtmpServerHandle handle, int seconds) }
{ void rtmp_server_set_max_players_per_stream(RtmpServerHandle handle, int max) {
if (!handle) return; if (!handle)
static_cast<Impl *>(handle)->server->setConnectionTimeout(seconds); return;
} static_cast<Impl *>(handle)->server->setMaxPlayersPerStream(max);
void rtmp_server_enable_ping_pong(RtmpServerHandle handle, bool enable, }
int interval_seconds) void rtmp_server_set_max_total_connections(RtmpServerHandle handle, int max) {
{ if (!handle)
if (!handle) return; return;
static_cast<Impl *>(handle)->server->enablePingPong(enable, interval_seconds); static_cast<Impl *>(handle)->server->setMaxTotalConnections(max);
} }
int rtmp_server_get_active_publishers(RtmpServerHandle handle) void rtmp_server_set_connection_timeout(RtmpServerHandle handle, int seconds) {
{ if (!handle)
if (!handle) return 0; return;
return static_cast<Impl *>(handle)->server->getActivePublishers(); static_cast<Impl *>(handle)->server->setConnectionTimeout(seconds);
} }
int rtmp_server_get_active_players(RtmpServerHandle handle) void rtmp_server_enable_ping_pong(RtmpServerHandle handle, bool enable,
{ int interval_seconds) {
if (!handle) return 0; if (!handle)
return static_cast<Impl *>(handle)->server->getActivePlayers(); return;
} static_cast<Impl *>(handle)->server->enablePingPong(enable, interval_seconds);
int rtmp_server_get_total_connections(RtmpServerHandle handle) }
{ int rtmp_server_get_active_publishers(RtmpServerHandle handle) {
if (!handle) return 0; if (!handle)
return static_cast<Impl *>(handle)->server->getTotalConnections(); return 0;
} return static_cast<Impl *>(handle)->server->getActivePublishers();
struct RtmpStreamStats rtmp_server_get_stream_stats(RtmpServerHandle handle, }
const char* app, const char* stream_key) int rtmp_server_get_active_players(RtmpServerHandle handle) {
{ if (!handle)
struct RtmpStreamStats stats = {0}; return 0;
if (!handle || !app || !stream_key) return stats; return static_cast<Impl *>(handle)->server->getActivePlayers();
Impl* impl = static_cast<Impl *>(handle); }
auto cstats = impl->server->getStreamStats(app, stream_key); int rtmp_server_get_total_connections(RtmpServerHandle handle) {
stats.bytes_sent = cstats.bytes_sent; if (!handle)
stats.bytes_received = cstats.bytes_received; return 0;
stats.video_frames = cstats.video_frames; return static_cast<Impl *>(handle)->server->getTotalConnections();
stats.audio_frames = cstats.audio_frames; }
stats.dropped_frames = cstats.dropped_frames; struct RtmpStreamStats rtmp_server_get_stream_stats(RtmpServerHandle handle,
stats.bitrate_kbps = cstats.getBitrate(); const char *app,
stats.uptime_seconds = cstats.getUptime(); const char *stream_key) {
return stats; struct RtmpStreamStats stats = {0};
} if (!handle || !app || !stream_key)
bool rtmp_server_start_recording(RtmpServerHandle handle, const char* app, return stats;
const char* stream_key, const char* filename) Impl *impl = static_cast<Impl *>(handle);
{ auto cstats = impl->server->getStreamStats(app, stream_key);
if (!handle || !app || !stream_key || !filename) return false; stats.bytes_sent = cstats.bytes_sent;
return static_cast<Impl *>(handle)->server->startRecording(app, stream_key, stats.bytes_received = cstats.bytes_received;
filename); stats.video_frames = cstats.video_frames;
} stats.audio_frames = cstats.audio_frames;
void rtmp_server_stop_recording(RtmpServerHandle handle, const char* app, stats.dropped_frames = cstats.dropped_frames;
const char* stream_key) stats.bitrate_kbps = cstats.getBitrate();
{ stats.uptime_seconds = cstats.getUptime();
if (!handle || !app || !stream_key) return; return stats;
static_cast<Impl *>(handle)->server->stopRecording(app, stream_key); }
} bool rtmp_server_start_recording(RtmpServerHandle handle, const char *app,
bool rtmp_server_is_recording(RtmpServerHandle handle, const char* app, const char *stream_key, const char *filename) {
const char* stream_key) if (!handle || !app || !stream_key || !filename)
{ return false;
if (!handle || !app || !stream_key) return false; return static_cast<Impl *>(handle)->server->startRecording(app, stream_key,
return static_cast<Impl *>(handle)->server->isRecording(app, stream_key); filename);
} }
bool rtmp_server_broadcast_audio(RtmpServerHandle handle, const char* app, void rtmp_server_stop_recording(RtmpServerHandle handle, const char *app,
const char* stream_key, const uint8_t* data, uint32_t length, const char *stream_key) {
uint32_t timestamp) if (!handle || !app || !stream_key)
{ return;
if (!handle || !app || !stream_key || !data || length == 0) return false; static_cast<Impl *>(handle)->server->stopRecording(app, stream_key);
Impl* impl = static_cast<Impl *>(handle); }
std::vector<uint8_t> vec(data, data + length); bool rtmp_server_is_recording(RtmpServerHandle handle, const char *app,
return impl->server->sendAudioToPlayers(app, stream_key, vec, timestamp); const char *stream_key) {
} if (!handle || !app || !stream_key)
bool rtmp_server_broadcast_video(RtmpServerHandle handle, const char* app, return false;
const char* stream_key, const uint8_t* data, uint32_t length, return static_cast<Impl *>(handle)->server->isRecording(app, stream_key);
uint32_t timestamp) }
{ bool rtmp_server_broadcast_audio(RtmpServerHandle handle, const char *app,
if (!handle || !app || !stream_key || !data || length == 0) return false; const char *stream_key, const uint8_t *data,
Impl* impl = static_cast<Impl *>(handle); uint32_t length, uint32_t timestamp) {
std::vector<uint8_t> vec(data, data + length); if (!handle || !app || !stream_key || !data || length == 0)
return impl->server->sendVideoToPlayers(app, stream_key, vec, timestamp); return false;
} Impl *impl = static_cast<Impl *>(handle);
bool rtmp_server_broadcast_metadata(RtmpServerHandle handle, const char* app, std::vector<uint8_t> vec(data, data + length);
const char* stream_key, const uint8_t* data, uint32_t length) return impl->server->sendAudioToPlayers(app, stream_key, vec, timestamp);
{ }
if (!handle || !app || !stream_key || !data || length == 0) return false; bool rtmp_server_broadcast_video(RtmpServerHandle handle, const char *app,
Impl* impl = static_cast<Impl *>(handle); const char *stream_key, const uint8_t *data,
std::vector<uint8_t> vec(data, data + length); uint32_t length, uint32_t timestamp) {
impl->server->sendMetadataToPlayers(app, stream_key, vec); if (!handle || !app || !stream_key || !data || length == 0)
return true; return false;
} Impl *impl = static_cast<Impl *>(handle);
void rtmp_logger_set_level(RtmpLogLevel level) std::vector<uint8_t> vec(data, data + length);
{ return impl->server->sendVideoToPlayers(app, stream_key, vec, timestamp);
rtmp::Logger::getInstance().setLevel((rtmp::LogLevel)level); }
} bool rtmp_server_broadcast_metadata(RtmpServerHandle handle, const char *app,
const char *stream_key, const uint8_t *data,
uint32_t length) {
if (!handle || !app || !stream_key || !data || length == 0)
return false;
Impl *impl = static_cast<Impl *>(handle);
std::vector<uint8_t> vec(data, data + length);
impl->server->sendMetadataToPlayers(app, stream_key, vec);
return true;
}
void rtmp_logger_set_level(RtmpLogLevel level) {
rtmp::Logger::getInstance().setLevel((rtmp::LogLevel)level);
}
} }

File diff suppressed because it is too large Load Diff