fix wrong path
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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,16 +10,14 @@ 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;
|
||||||
@@ -29,19 +27,23 @@ struct RtmpStreamStats
|
|||||||
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,19 +54,20 @@ 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);
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -1,31 +1,29 @@
|
|||||||
#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,
|
||||||
@@ -41,11 +39,10 @@ namespace rtmp
|
|||||||
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,
|
||||||
@@ -53,11 +50,10 @@ namespace rtmp
|
|||||||
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,
|
||||||
@@ -66,21 +62,14 @@ namespace rtmp
|
|||||||
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;
|
||||||
@@ -88,11 +77,10 @@ namespace rtmp
|
|||||||
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;
|
||||||
@@ -100,18 +88,16 @@ namespace rtmp
|
|||||||
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;
|
||||||
@@ -119,11 +105,10 @@ namespace rtmp
|
|||||||
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;
|
||||||
@@ -133,40 +118,35 @@ namespace rtmp
|
|||||||
|
|
||||||
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 = std::chrono::duration_cast<std::chrono::seconds>(
|
auto duration =
|
||||||
now - start_time).count();
|
std::chrono::duration_cast<std::chrono::seconds>(now - start_time)
|
||||||
if (duration == 0) return 0;
|
.count();
|
||||||
|
if (duration == 0)
|
||||||
|
return 0;
|
||||||
return (bytes_sent * 8.0) / duration / 1000.0; // kbps
|
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>(
|
return std::chrono::duration_cast<std::chrono::seconds>(now - start_time)
|
||||||
now - start_time).count();
|
.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
|
bool hasKeyframe() const { return has_keyframe; }
|
||||||
{
|
|
||||||
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;
|
||||||
@@ -178,28 +158,24 @@ namespace rtmp
|
|||||||
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;
|
||||||
@@ -209,44 +185,28 @@ namespace rtmp
|
|||||||
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);
|
||||||
@@ -258,45 +218,29 @@ namespace rtmp
|
|||||||
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()
|
void updateActivity() { last_activity = std::chrono::steady_clock::now(); }
|
||||||
{
|
|
||||||
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;
|
||||||
@@ -311,22 +255,22 @@ namespace rtmp
|
|||||||
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);
|
||||||
@@ -342,181 +286,119 @@ namespace rtmp
|
|||||||
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;
|
static Logger instance;
|
||||||
return 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>>
|
||||||
|
getAllStreamStats() const;
|
||||||
int getActivePublishers() const;
|
int getActivePublishers() const;
|
||||||
int getActivePlayers() const;
|
int getActivePlayers() const;
|
||||||
int getTotalConnections() 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;
|
||||||
@@ -569,18 +451,18 @@ namespace rtmp
|
|||||||
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)
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
#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;
|
rtmp::RTMPServer *server;
|
||||||
RtmpOnConnectCallback on_connect_cb;
|
RtmpOnConnectCallback on_connect_cb;
|
||||||
void *on_connect_userdata;
|
void *on_connect_userdata;
|
||||||
@@ -23,214 +20,226 @@ namespace rtmp_capi_internal
|
|||||||
void *on_disconnect_userdata;
|
void *on_disconnect_userdata;
|
||||||
RtmpAuthCallback auth_cb;
|
RtmpAuthCallback auth_cb;
|
||||||
void *auth_userdata;
|
void *auth_userdata;
|
||||||
RtmpServerImpl() : server(nullptr), on_connect_cb(nullptr),
|
RtmpServerImpl()
|
||||||
on_connect_userdata(nullptr),
|
: server(nullptr), on_connect_cb(nullptr), on_connect_userdata(nullptr),
|
||||||
on_publish_cb(nullptr), on_publish_userdata(nullptr), on_play_cb(nullptr),
|
on_publish_cb(nullptr), on_publish_userdata(nullptr),
|
||||||
on_play_userdata(nullptr),
|
on_play_cb(nullptr), on_play_userdata(nullptr), on_audio_cb(nullptr),
|
||||||
on_audio_cb(nullptr), on_audio_userdata(nullptr), on_video_cb(nullptr),
|
on_audio_userdata(nullptr), on_video_cb(nullptr),
|
||||||
on_video_userdata(nullptr),
|
on_video_userdata(nullptr), on_disconnect_cb(nullptr),
|
||||||
on_disconnect_cb(nullptr), on_disconnect_userdata(nullptr), auth_cb(nullptr),
|
on_disconnect_userdata(nullptr), auth_cb(nullptr),
|
||||||
auth_userdata(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](std::shared_ptr<rtmp::RTMPSession> session)
|
impl->server->setOnConnect(
|
||||||
{
|
[impl](std::shared_ptr<rtmp::RTMPSession> session) {
|
||||||
if (!impl || !impl->on_connect_cb) return;
|
if (!impl || !impl->on_connect_cb)
|
||||||
const auto& info = session->getStreamInfo();
|
return;
|
||||||
|
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;
|
||||||
|
const auto &info = session->getStreamInfo();
|
||||||
impl->on_publish_cb(info.client_ip.c_str(), app.c_str(), stream_key.c_str(),
|
impl->on_publish_cb(info.client_ip.c_str(), app.c_str(), stream_key.c_str(),
|
||||||
impl->on_publish_userdata);
|
impl->on_publish_userdata);
|
||||||
});
|
});
|
||||||
impl->server->setOnPlay([impl](std::shared_ptr<rtmp::RTMPSession> session,
|
impl->server->setOnPlay([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_play_cb) return;
|
if (!impl || !impl->on_play_cb)
|
||||||
const auto& info = session->getStreamInfo();
|
return;
|
||||||
|
const auto &info = session->getStreamInfo();
|
||||||
impl->on_play_cb(info.client_ip.c_str(), app.c_str(), stream_key.c_str(),
|
impl->on_play_cb(info.client_ip.c_str(), app.c_str(), stream_key.c_str(),
|
||||||
impl->on_play_userdata);
|
impl->on_play_userdata);
|
||||||
});
|
});
|
||||||
impl->server->setOnAudioData([impl](std::shared_ptr<rtmp::RTMPSession> session,
|
impl->server->setOnAudioData(
|
||||||
const std::vector<uint8_t> &data, uint32_t timestamp)
|
[impl](std::shared_ptr<rtmp::RTMPSession> session,
|
||||||
{
|
const std::vector<uint8_t> &data, uint32_t timestamp) {
|
||||||
if (!impl || !impl->on_audio_cb) return;
|
if (!impl || !impl->on_audio_cb)
|
||||||
const auto& info = session->getStreamInfo();
|
return;
|
||||||
impl->on_audio_cb(info.app.c_str(), info.stream_key.c_str(), data.data(),
|
const auto &info = session->getStreamInfo();
|
||||||
static_cast<uint32_t>(data.size()), timestamp, impl->on_audio_userdata);
|
impl->on_audio_cb(info.app.c_str(), info.stream_key.c_str(),
|
||||||
|
data.data(), static_cast<uint32_t>(data.size()),
|
||||||
|
timestamp, impl->on_audio_userdata);
|
||||||
});
|
});
|
||||||
impl->server->setOnVideoData([impl](std::shared_ptr<rtmp::RTMPSession> session,
|
impl->server->setOnVideoData(
|
||||||
const std::vector<uint8_t> &data, uint32_t timestamp)
|
[impl](std::shared_ptr<rtmp::RTMPSession> session,
|
||||||
{
|
const std::vector<uint8_t> &data, uint32_t timestamp) {
|
||||||
if (!impl || !impl->on_video_cb) return;
|
if (!impl || !impl->on_video_cb)
|
||||||
const auto& info = session->getStreamInfo();
|
return;
|
||||||
impl->on_video_cb(info.app.c_str(), info.stream_key.c_str(), data.data(),
|
const auto &info = session->getStreamInfo();
|
||||||
static_cast<uint32_t>(data.size()), timestamp, impl->on_video_userdata);
|
impl->on_video_cb(info.app.c_str(), info.stream_key.c_str(),
|
||||||
|
data.data(), static_cast<uint32_t>(data.size()),
|
||||||
|
timestamp, impl->on_video_userdata);
|
||||||
});
|
});
|
||||||
impl->server->setOnDisconnect([impl](std::shared_ptr<rtmp::RTMPSession>
|
impl->server->setOnDisconnect(
|
||||||
session)
|
[impl](std::shared_ptr<rtmp::RTMPSession> session) {
|
||||||
{
|
if (!impl || !impl->on_disconnect_cb)
|
||||||
if (!impl || !impl->on_disconnect_cb) return;
|
return;
|
||||||
const auto& info = session->getStreamInfo();
|
const auto &info = session->getStreamInfo();
|
||||||
impl->on_disconnect_cb(info.client_ip.c_str(), info.app.c_str(),
|
impl->on_disconnect_cb(info.client_ip.c_str(), info.app.c_str(),
|
||||||
info.stream_key.c_str(), info.is_publishing, info.is_playing,
|
info.stream_key.c_str(), info.is_publishing,
|
||||||
impl->on_disconnect_userdata);
|
info.is_playing, impl->on_disconnect_userdata);
|
||||||
});
|
});
|
||||||
impl->server->setAuthCallback([impl](const std::string & app,
|
impl->server->setAuthCallback([impl](const std::string &app,
|
||||||
const std::string & stream_key, const std::string & client_ip) -> bool
|
const std::string &stream_key,
|
||||||
{
|
const std::string &client_ip) -> bool {
|
||||||
if (!impl || !impl->auth_cb) return true;
|
if (!impl || !impl->auth_cb)
|
||||||
return impl->auth_cb(app.c_str(), stream_key.c_str(), client_ip.c_str(), impl->auth_userdata);
|
return true;
|
||||||
|
return impl->auth_cb(app.c_str(), stream_key.c_str(), client_ip.c_str(),
|
||||||
|
impl->auth_userdata);
|
||||||
});
|
});
|
||||||
return impl;
|
return impl;
|
||||||
}
|
}
|
||||||
// add all other functions as above
|
// add all other functions as above
|
||||||
void rtmp_server_destroy(RtmpServerHandle handle)
|
void rtmp_server_destroy(RtmpServerHandle handle) {
|
||||||
{
|
if (!handle)
|
||||||
if (!handle) return;
|
return;
|
||||||
Impl* impl = static_cast<Impl *>(handle);
|
Impl *impl = static_cast<Impl *>(handle);
|
||||||
delete impl->server;
|
delete impl->server;
|
||||||
delete impl;
|
delete impl;
|
||||||
}
|
}
|
||||||
bool rtmp_server_start(RtmpServerHandle handle)
|
bool rtmp_server_start(RtmpServerHandle handle) {
|
||||||
{
|
if (!handle)
|
||||||
if (!handle) return false;
|
return false;
|
||||||
Impl* impl = static_cast<Impl *>(handle);
|
Impl *impl = static_cast<Impl *>(handle);
|
||||||
return impl->server->start();
|
return impl->server->start();
|
||||||
}
|
}
|
||||||
void rtmp_server_stop(RtmpServerHandle handle)
|
void rtmp_server_stop(RtmpServerHandle handle) {
|
||||||
{
|
if (!handle)
|
||||||
if (!handle) return;
|
return;
|
||||||
Impl* impl = static_cast<Impl *>(handle);
|
Impl *impl = static_cast<Impl *>(handle);
|
||||||
impl->server->stop();
|
impl->server->stop();
|
||||||
}
|
}
|
||||||
bool rtmp_server_is_running(RtmpServerHandle handle)
|
bool rtmp_server_is_running(RtmpServerHandle handle) {
|
||||||
{
|
if (!handle)
|
||||||
if (!handle) return false;
|
return false;
|
||||||
Impl* impl = static_cast<Impl *>(handle);
|
Impl *impl = static_cast<Impl *>(handle);
|
||||||
return impl->server->isRunning();
|
return impl->server->isRunning();
|
||||||
}
|
}
|
||||||
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) {
|
||||||
{
|
if (!handle)
|
||||||
if (!handle) return;
|
return;
|
||||||
Impl* impl = static_cast<Impl *>(handle);
|
Impl *impl = static_cast<Impl *>(handle);
|
||||||
impl->on_connect_cb = cb;
|
impl->on_connect_cb = cb;
|
||||||
impl->on_connect_userdata = user_data;
|
impl->on_connect_userdata = 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) {
|
||||||
{
|
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_publish_cb = cb;
|
||||||
impl->on_publish_userdata = user_data;
|
impl->on_publish_userdata = 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) {
|
||||||
{
|
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_play_cb = cb;
|
||||||
impl->on_play_userdata = user_data;
|
impl->on_play_userdata = 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) {
|
||||||
if (!handle) return;
|
if (!handle)
|
||||||
Impl* impl = static_cast<Impl *>(handle);
|
return;
|
||||||
|
Impl *impl = static_cast<Impl *>(handle);
|
||||||
impl->on_audio_cb = cb;
|
impl->on_audio_cb = cb;
|
||||||
impl->on_audio_userdata = user_data;
|
impl->on_audio_userdata = 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) {
|
||||||
if (!handle) return;
|
if (!handle)
|
||||||
Impl* impl = static_cast<Impl *>(handle);
|
return;
|
||||||
|
Impl *impl = static_cast<Impl *>(handle);
|
||||||
impl->on_video_cb = cb;
|
impl->on_video_cb = cb;
|
||||||
impl->on_video_userdata = user_data;
|
impl->on_video_userdata = 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) {
|
||||||
if (!handle) return;
|
if (!handle)
|
||||||
Impl* impl = static_cast<Impl *>(handle);
|
return;
|
||||||
|
Impl *impl = static_cast<Impl *>(handle);
|
||||||
impl->on_disconnect_cb = cb;
|
impl->on_disconnect_cb = cb;
|
||||||
impl->on_disconnect_userdata = user_data;
|
impl->on_disconnect_userdata = 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) {
|
||||||
{
|
if (!handle)
|
||||||
if (!handle) return;
|
return;
|
||||||
Impl* impl = static_cast<Impl *>(handle);
|
Impl *impl = static_cast<Impl *>(handle);
|
||||||
impl->auth_cb = cb;
|
impl->auth_cb = cb;
|
||||||
impl->auth_userdata = user_data;
|
impl->auth_userdata = user_data;
|
||||||
}
|
}
|
||||||
void rtmp_server_enable_gop_cache(RtmpServerHandle handle, bool enable)
|
void rtmp_server_enable_gop_cache(RtmpServerHandle handle, bool enable) {
|
||||||
{
|
if (!handle)
|
||||||
if (!handle) return;
|
return;
|
||||||
static_cast<Impl *>(handle)->server->enableGOPCache(enable);
|
static_cast<Impl *>(handle)->server->enableGOPCache(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) {
|
||||||
{
|
if (!handle)
|
||||||
if (!handle) return;
|
return;
|
||||||
static_cast<Impl *>(handle)->server->setMaxPublishersPerStream(max);
|
static_cast<Impl *>(handle)->server->setMaxPublishersPerStream(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) {
|
||||||
{
|
if (!handle)
|
||||||
if (!handle) return;
|
return;
|
||||||
static_cast<Impl *>(handle)->server->setMaxPlayersPerStream(max);
|
static_cast<Impl *>(handle)->server->setMaxPlayersPerStream(max);
|
||||||
}
|
}
|
||||||
void rtmp_server_set_max_total_connections(RtmpServerHandle handle, int max)
|
void rtmp_server_set_max_total_connections(RtmpServerHandle handle, int max) {
|
||||||
{
|
if (!handle)
|
||||||
if (!handle) return;
|
return;
|
||||||
static_cast<Impl *>(handle)->server->setMaxTotalConnections(max);
|
static_cast<Impl *>(handle)->server->setMaxTotalConnections(max);
|
||||||
}
|
}
|
||||||
void rtmp_server_set_connection_timeout(RtmpServerHandle handle, int seconds)
|
void rtmp_server_set_connection_timeout(RtmpServerHandle handle, int seconds) {
|
||||||
{
|
if (!handle)
|
||||||
if (!handle) return;
|
return;
|
||||||
static_cast<Impl *>(handle)->server->setConnectionTimeout(seconds);
|
static_cast<Impl *>(handle)->server->setConnectionTimeout(seconds);
|
||||||
}
|
}
|
||||||
void rtmp_server_enable_ping_pong(RtmpServerHandle handle, bool enable,
|
void rtmp_server_enable_ping_pong(RtmpServerHandle handle, bool enable,
|
||||||
int interval_seconds)
|
int interval_seconds) {
|
||||||
{
|
if (!handle)
|
||||||
if (!handle) return;
|
return;
|
||||||
static_cast<Impl *>(handle)->server->enablePingPong(enable, interval_seconds);
|
static_cast<Impl *>(handle)->server->enablePingPong(enable, interval_seconds);
|
||||||
}
|
}
|
||||||
int rtmp_server_get_active_publishers(RtmpServerHandle handle)
|
int rtmp_server_get_active_publishers(RtmpServerHandle handle) {
|
||||||
{
|
if (!handle)
|
||||||
if (!handle) return 0;
|
return 0;
|
||||||
return static_cast<Impl *>(handle)->server->getActivePublishers();
|
return static_cast<Impl *>(handle)->server->getActivePublishers();
|
||||||
}
|
}
|
||||||
int rtmp_server_get_active_players(RtmpServerHandle handle)
|
int rtmp_server_get_active_players(RtmpServerHandle handle) {
|
||||||
{
|
if (!handle)
|
||||||
if (!handle) return 0;
|
return 0;
|
||||||
return static_cast<Impl *>(handle)->server->getActivePlayers();
|
return static_cast<Impl *>(handle)->server->getActivePlayers();
|
||||||
}
|
}
|
||||||
int rtmp_server_get_total_connections(RtmpServerHandle handle)
|
int rtmp_server_get_total_connections(RtmpServerHandle handle) {
|
||||||
{
|
if (!handle)
|
||||||
if (!handle) return 0;
|
return 0;
|
||||||
return static_cast<Impl *>(handle)->server->getTotalConnections();
|
return static_cast<Impl *>(handle)->server->getTotalConnections();
|
||||||
}
|
}
|
||||||
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) {
|
||||||
struct RtmpStreamStats stats = {0};
|
struct RtmpStreamStats stats = {0};
|
||||||
if (!handle || !app || !stream_key) return stats;
|
if (!handle || !app || !stream_key)
|
||||||
Impl* impl = static_cast<Impl *>(handle);
|
return stats;
|
||||||
|
Impl *impl = static_cast<Impl *>(handle);
|
||||||
auto cstats = impl->server->getStreamStats(app, stream_key);
|
auto cstats = impl->server->getStreamStats(app, stream_key);
|
||||||
stats.bytes_sent = cstats.bytes_sent;
|
stats.bytes_sent = cstats.bytes_sent;
|
||||||
stats.bytes_received = cstats.bytes_received;
|
stats.bytes_received = cstats.bytes_received;
|
||||||
@@ -240,55 +249,55 @@ extern "C" {
|
|||||||
stats.bitrate_kbps = cstats.getBitrate();
|
stats.bitrate_kbps = cstats.getBitrate();
|
||||||
stats.uptime_seconds = cstats.getUptime();
|
stats.uptime_seconds = cstats.getUptime();
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
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) {
|
||||||
{
|
if (!handle || !app || !stream_key || !filename)
|
||||||
if (!handle || !app || !stream_key || !filename) return false;
|
return false;
|
||||||
return static_cast<Impl *>(handle)->server->startRecording(app, stream_key,
|
return static_cast<Impl *>(handle)->server->startRecording(app, stream_key,
|
||||||
filename);
|
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) {
|
||||||
{
|
if (!handle || !app || !stream_key)
|
||||||
if (!handle || !app || !stream_key) return;
|
return;
|
||||||
static_cast<Impl *>(handle)->server->stopRecording(app, stream_key);
|
static_cast<Impl *>(handle)->server->stopRecording(app, 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) {
|
||||||
{
|
if (!handle || !app || !stream_key)
|
||||||
if (!handle || !app || !stream_key) return false;
|
return false;
|
||||||
return static_cast<Impl *>(handle)->server->isRecording(app, stream_key);
|
return static_cast<Impl *>(handle)->server->isRecording(app, stream_key);
|
||||||
}
|
}
|
||||||
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) {
|
||||||
{
|
if (!handle || !app || !stream_key || !data || length == 0)
|
||||||
if (!handle || !app || !stream_key || !data || length == 0) return false;
|
return false;
|
||||||
Impl* impl = static_cast<Impl *>(handle);
|
Impl *impl = static_cast<Impl *>(handle);
|
||||||
std::vector<uint8_t> vec(data, data + length);
|
std::vector<uint8_t> vec(data, data + length);
|
||||||
return impl->server->sendAudioToPlayers(app, stream_key, vec, timestamp);
|
return impl->server->sendAudioToPlayers(app, stream_key, vec, 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) {
|
||||||
{
|
if (!handle || !app || !stream_key || !data || length == 0)
|
||||||
if (!handle || !app || !stream_key || !data || length == 0) return false;
|
return false;
|
||||||
Impl* impl = static_cast<Impl *>(handle);
|
Impl *impl = static_cast<Impl *>(handle);
|
||||||
std::vector<uint8_t> vec(data, data + length);
|
std::vector<uint8_t> vec(data, data + length);
|
||||||
return impl->server->sendVideoToPlayers(app, stream_key, vec, timestamp);
|
return impl->server->sendVideoToPlayers(app, stream_key, vec, timestamp);
|
||||||
}
|
}
|
||||||
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) {
|
||||||
if (!handle || !app || !stream_key || !data || length == 0) return false;
|
if (!handle || !app || !stream_key || !data || length == 0)
|
||||||
Impl* impl = static_cast<Impl *>(handle);
|
return false;
|
||||||
|
Impl *impl = static_cast<Impl *>(handle);
|
||||||
std::vector<uint8_t> vec(data, data + length);
|
std::vector<uint8_t> vec(data, data + length);
|
||||||
impl->server->sendMetadataToPlayers(app, stream_key, vec);
|
impl->server->sendMetadataToPlayers(app, stream_key, vec);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void rtmp_logger_set_level(RtmpLogLevel level)
|
void rtmp_logger_set_level(RtmpLogLevel level) {
|
||||||
{
|
rtmp::Logger::getInstance().setLevel((rtmp::LogLevel)level);
|
||||||
rtmp::Logger::getInstance().setLevel((rtmp::LogLevel)level);
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
1272
src/rtmp_server.cpp
1272
src/rtmp_server.cpp
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user