整理代码

This commit is contained in:
蒲爽 2026-09-28 21:33:04 +08:00
commit c7b4e178ec
364 changed files with 59691 additions and 0 deletions

View file

@ -0,0 +1,237 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "MPEGMuxer.h"
#include "../core/VideoEncoder.h"
#include "../core/AudioEncoder.h"
#include "../core/SampleFormat.h"
#include "../core/System.h"
namespace Video
{
namespace MediaServer
{
/*
*******************************************************************************
* TYPEDEFS
*******************************************************************************
*/
struct CodecIDItem
{
Core::CodecID id;
int type;
};
/*
*******************************************************************************
* LOCAL VARIABLES
*******************************************************************************
*/
static const std::array<CodecIDItem, 8> codecMap = {{
{Core::CodecID::H264, PSI_STREAM_H264},
{Core::CodecID::H265, PSI_STREAM_H265},
{Core::CodecID::MJPEG, PSI_STREAM_JPEG_2000},
{Core::CodecID::MP3, PSI_STREAM_MP3},
{Core::CodecID::AAC, PSI_STREAM_AAC},
{Core::CodecID::OPUS, PSI_STREAM_AUDIO_OPUS},
{Core::CodecID::G711A, PSI_STREAM_AUDIO_G711A},
{Core::CodecID::G711U, PSI_STREAM_AUDIO_G711U},
}};
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
MPEGMuxer::Stream::Stream(const std::string& name, int id, Core::PacketQueue::Ptr pQueue)
: _name(name), _id(id), _pQueue(pQueue)
{
}
MPEGMuxer::Stream::~Stream()
{
}
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
static int isMulticastAddr(const char* ip)
{
int r;
struct addrinfo* ai;
r = getaddrinfo(ip, 0, NULL, &ai);
if (0 != r)
return 0;
if (AF_INET == ai->ai_addr->sa_family)
{
const struct sockaddr_in* in = (const struct sockaddr_in*)ai->ai_addr;
assert(sizeof(struct sockaddr_in) == ai->ai_addrlen);
r = (ntohl(in->sin_addr.s_addr) & 0xf0000000) == 0xe0000000 ? 1 : 0;
}
else if (AF_INET6 == ai->ai_addr->sa_family)
{
const struct sockaddr_in6* in6 = (const struct sockaddr_in6*)ai->ai_addr;
assert(sizeof(struct sockaddr_in6) == ai->ai_addrlen);
r = in6->sin6_addr.s6_addr[0] == 0xff ? 1 : 0;
}
else
{
assert(0);
r = 0;
}
freeaddrinfo(ai);
return r;
}
Video::MediaServer::MPEGMuxer::MPEGMuxer(const std::string& url)
: Core::Muxer(Poco::format("MediaServer::MPEGMuxer(%s)", url), url)
{
}
MPEGMuxer::~MPEGMuxer()
{
}
void MPEGMuxer::connect()
{
Poco::URI uri(url());
std::string scheme = uri.getScheme();
std::string host = uri.getHost();
short port = uri.getPort();
_socket = socket_udp_bind_ipv4(nullptr, 50609);
if (socket_invalid == _socket)
throw Poco::InvalidAccessException(Poco::format("%s: Create socket FAIL", name()));
if (isMulticastAddr(host.c_str()))
socket_udp_multicast(_socket, host.c_str(), nullptr, 16);
memset(&_destAddr, 0, sizeof(_destAddr));
socket_addr_from_ipv4(&_destAddr, host.c_str(), port);
static mpeg_muxer_func_t func = {[](void* param, size_t bytes) -> void*
{
MPEGMuxer* pMuxer = (MPEGMuxer*)param;
if (pMuxer->_writeIndex + bytes > sizeof(pMuxer->_buffer))
pMuxer->flush();
return &pMuxer->_buffer[pMuxer->_writeIndex];
},
[](void* param, void* packet) -> void {},
[](void* param, int stream, void* packet, size_t bytes) -> int
{
MPEGMuxer* pMuxer = (MPEGMuxer*)param;
pMuxer->_writeIndex += bytes;
return 0;
}};
_pMuxer = mpeg_muxer_create(0, &func, this);
if (!_pMuxer)
throw Poco::InvalidAccessException(Poco::format("%s: Create MPEG-Muxer FAIL!", name()));
// mpeg_muxer_add_program(_pMuxer, 1, "XXXX", 4); /* 多节目方式 */
previousComponents().foreach (
[&](Core::Component::Ptr pPrevious) -> void
{
auto pVideoEncoder = pPrevious.cast<Core::VideoEncoder>();
if (!pVideoEncoder.isNull())
{
int id = mpeg_muxer_add_stream(_pMuxer, getCodecID(pVideoEncoder->getCodecID()), nullptr, 0);
_streams.push_back(new Stream(Poco::format("%s/Stream(%s)", name(), pVideoEncoder->name()), id,
packetQueues().find(pPrevious)));
return;
}
auto pAudioEncoder = pPrevious.cast<Core::AudioEncoder>();
if (!pAudioEncoder.isNull())
{
int id = mpeg_muxer_add_stream(_pMuxer, getCodecID(pAudioEncoder->getCodecID()), nullptr, 0);
_streams.push_back(new Stream(Poco::format("%s/Stream(%s)", name(), pAudioEncoder->name()), id,
packetQueues().find(pPrevious)));
}
});
}
void MPEGMuxer::mux()
{
std::sort(_streams.begin(), _streams.end(), [](auto a, auto b) { return *a < *b; });
auto& pStream = *_streams.begin();
Core::Packet::Ptr pPacket = pStream->queue()->dequeue(4000);
if (pStream->serialCodeWatcher().isChanged(pPacket->serialCode()))
throw Poco::ApplicationException(Poco::format("%s: Stream changed, restart...", name()));
pStream->setTimeline(pPacket->pts());
int flags = 0;
if (pPacket->isVideo())
flags |= pPacket->isKeyFrame() ? MPEG_FLAG_IDR_FRAME : 0;
mpeg_muxer_input(_pMuxer, pStream->id(), flags, pPacket->pts() * 90000 / 1000000, pPacket->dts() * 90000 / 1000000,
pPacket->data(), pPacket->length());
#if (TRACE_VIDEO_FRAME_RATE)
if (pPacket->isVideo())
{
count++;
uint64_t now = Core::System::timestamp();
int64_t delta = now - timeline;
if (delta >= 1000000)
{
timeline = now;
std::cout << " ==> " << name() << " fps: " << (double)count / (delta / 1000000) << std::endl;
count = 0;
}
}
#endif
}
void MPEGMuxer::disconnect()
{
if (_writeIndex > 0 && _socket != socket_invalid)
flush();
if (_pMuxer)
{
mpeg_muxer_destroy(_pMuxer);
_pMuxer = nullptr;
}
if (_socket != socket_invalid)
{
socket_close(_socket);
_socket = socket_invalid;
}
_streams.clear();
}
void MPEGMuxer::flush()
{
socket_sendto_by_time(_socket, _buffer, _writeIndex, 0, (const struct sockaddr*)&_destAddr, sizeof(_destAddr),
1000);
_writeIndex = 0;
}
const int MPEGMuxer::getCodecID(Core::CodecID id)
{
for (const auto& m : codecMap)
{
if (id == m.id)
return m.type;
}
throw Poco::NotFoundException(
Poco::format("Video::MediaServer: Can't find %s in codec-map", Core::codecIDToString(id)));
}
}
}

View file

@ -0,0 +1,98 @@
#ifndef F9CED6CA_74FC_49C3_8D58_95A62F2EA1EC
#define F9CED6CA_74FC_49C3_8D58_95A62F2EA1EC
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "../core/Muxer.h"
#include "util/ClassAssistant.h"
#include <sdk/include/sockutil.h>
#include <media-server/libmpeg/mpeg-muxer.h>
#include <media-server/libmpeg/mpeg-proto.h>
/*
*******************************************************************************
* MACROS
*******************************************************************************
*/
#define TRACE_VIDEO_FRAME_RATE (0)
namespace Video
{
namespace MediaServer
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class MPEGMuxer: public Core::Muxer
{
private:
class Stream
{
public:
using Ptr = Poco::SharedPtr<Stream>;
public:
Stream(const std::string& name, int id, Core::PacketQueue::Ptr pQueue);
~Stream();
public:
bool operator<(const Stream& other);
Core::PacketQueue::Ptr queue();
Core::SerialCodeWatcher& serialCodeWatcher();
PROPERTY_DECLARE(Timeline, uint64_t, 0, public, public);
READONLY_PROPERTY_DECLARE(name, std::string)
READONLY_PROPERTY_DECLARE(id, int)
private:
Core::PacketQueue::Ptr _pQueue;
Core::SerialCodeWatcher _serialCodeWatcher;
};
public:
MPEGMuxer(const std::string& url);
~MPEGMuxer();
protected:
void connect() override;
void mux() override;
void disconnect() override;
void flush();
const int getCodecID(Core::CodecID id);
private:
socket_t _socket;
struct sockaddr_in _destAddr;
mpeg_muxer_t* _pMuxer;
std::vector<Stream::Ptr> _streams;
uint8_t _buffer[1316];
int _writeIndex;
#if (TRACE_VIDEO_FRAME_RATE)
int count = 0;
uint64_t timeline = 0;
#endif
};
inline bool MPEGMuxer::Stream::operator<(const Stream& other)
{
return _Timeline < other._Timeline;
}
inline Core::PacketQueue::Ptr MPEGMuxer::Stream::queue()
{
return _pQueue;
}
inline Core::SerialCodeWatcher& MPEGMuxer::Stream::serialCodeWatcher()
{
return _serialCodeWatcher;
}
}
}
#endif

View file

@ -0,0 +1,554 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "RTMPDemuxHandler.h"
#include "Logger.h"
#include "../core/System.h"
#include "util/Math.h"
#include <list>
namespace Video
{
namespace MediaServer
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
RTMPDemuxHandler::RTMPDemuxHandler(const std::string& uri)
: Core::DemuxHandler(Poco::format("MediaServer::DemuxHandler(%s)", uri), uri)
{
}
RTMPDemuxHandler::~RTMPDemuxHandler()
{
}
void RTMPDemuxHandler::run(Core::VideoEncoder::Ptr pVideoEncoder, Core::AudioEncoder::Ptr pAudioEncoder,
const Post& post, std::atomic<bool>& isCancelled)
{
_pVideoEncoder = pVideoEncoder;
_pAudioEncoder = pAudioEncoder;
_post = post;
Poco::URI uriParser(uri());
std::string scheme = uriParser.getScheme();
std::string host = uriParser.getHost();
short port = uriParser.getPort();
port = port <= 0 ? 1935 : port;
std::string path = uriParser.getPath();
if (!path.empty() && path[0] == '/')
path = path.substr(1);
size_t pos = path.find_first_of('/');
if (pos == std::string::npos)
{
logger().error("%s: Invalid URL: %s", name(), uri());
Poco::Thread::sleep(2000);
return;
}
std::string turl = scheme + "://" + host + ":" + std::to_string(port) + "/" + path.substr(0, pos);
std::string appname = path.substr(0, pos);
std::string playpath = path.substr(pos + 1);
struct rtmp_client_handler_t handler;
handler.send = RTMPDemuxHandler::clientSend;
handler.onaudio = RTMPDemuxHandler::clientOnAudio;
handler.onvideo = RTMPDemuxHandler::clientOnVideo;
handler.onscript = RTMPDemuxHandler::clientOnScript;
const size_t buffsz = 2 * 1024 * 1024;
Poco::SharedPtr<uint8_t> pBuffer = new uint8_t[buffsz];
int rc;
while (!isCancelled)
{
_audioInfoParsed = false;
_videoInfoParsed = false;
while (!isCancelled)
{
_socket = socket_connect_host(host.c_str(), port, 2000);
if (_socket != socket_invalid)
{
socket_setnonblock(_socket, 0);
break;
}
Poco::Thread::sleep(2000);
continue;
}
_pCient = rtmp_client_create(appname.c_str(), playpath.c_str(), turl.c_str(), this, &handler);
if (!_pCient)
{
logger().error("%s: Create RTMP-Client FAIL!(URL: %s)", name(), uri());
Poco::Thread::sleep(2000);
goto rtmp_create_fail;
}
_pDemuxer = flv_demuxer_create(RTMPDemuxHandler::flvDemuxerHandler, this);
if (!_pDemuxer)
{
logger().error("%s: Create FLV Debuxer FAIL!(URL: %s)", name(), uri());
Poco::Thread::sleep(2000);
goto demuxer_fail;
}
rc = rtmp_client_start(_pCient, 1);
if (rc != 0)
{
logger().error("%s: Start RTMP-Client FAIL!(URL: %s)", name(), uri());
Poco::Thread::sleep(2000);
goto rmtp_start_fail;
}
while (!isCancelled)
{
rc = socket_recv(_socket, pBuffer.get(), buffsz, 0);
if (rc <= 0)
{
logger().error("%s: Socket recv FAIL: %d(URL: %s)", name(), rc, uri());
break;
}
rc = rtmp_client_input(_pCient, pBuffer.get(), rc);
if (rc != 0)
{
logger().error("%s: RTMP client input FAIL: %d(URL: %s)", name(), rc, uri());
break;
}
}
rtmp_client_stop(_pCient);
rmtp_start_fail:
flv_demuxer_destroy(_pDemuxer);
demuxer_fail:
rtmp_client_destroy(_pCient);
rtmp_create_fail:
socket_close(_socket);
_socket = socket_invalid;
Poco::Thread::sleep(2000);
}
}
int RTMPDemuxHandler::parseFLVMetaData(const uint8_t* data, size_t bytes)
{
double audiocodecid = 0;
double audiodatarate = 0;
double audiodelay = 0;
double audiosamplerate = 0;
double audiosamplesize = 0;
int stereo = 0;
double videocodecid = 0;
double videodatarate = 0;
double framerate = 0;
double height = 0;
double width = 0;
struct amf_object_item_t prop[16];
struct amf_object_item_t items[1];
char buffer[64];
#define AMF_OBJECT_ITEM_VALUE(v, amf_type, amf_name, amf_value, amf_size) \
{ \
v.type = amf_type; \
v.name = amf_name; \
v.value = amf_value; \
v.size = amf_size; \
}
int i = 0;
AMF_OBJECT_ITEM_VALUE(prop[i], AMF_NUMBER, "audiocodecid", &audiocodecid, sizeof(audiocodecid));
i++;
AMF_OBJECT_ITEM_VALUE(prop[i], AMF_NUMBER, "audiodatarate", &audiodatarate, sizeof(audiodatarate));
i++;
AMF_OBJECT_ITEM_VALUE(prop[i], AMF_NUMBER, "audiodelay", &audiodelay, sizeof(audiodelay));
i++;
AMF_OBJECT_ITEM_VALUE(prop[i], AMF_NUMBER, "audiosamplerate", &audiosamplerate, sizeof(audiosamplerate));
i++;
AMF_OBJECT_ITEM_VALUE(prop[i], AMF_NUMBER, "audiosamplesize", &audiosamplesize, sizeof(audiosamplesize));
i++;
AMF_OBJECT_ITEM_VALUE(prop[i], AMF_BOOLEAN, "stereo", &stereo, sizeof(stereo));
i++;
AMF_OBJECT_ITEM_VALUE(prop[i], AMF_NUMBER, "videocodecid", &videocodecid, sizeof(videocodecid));
i++;
AMF_OBJECT_ITEM_VALUE(prop[i], AMF_NUMBER, "videodatarate", &videodatarate, sizeof(videodatarate));
i++;
AMF_OBJECT_ITEM_VALUE(prop[i], AMF_NUMBER, "framerate", &framerate, sizeof(framerate));
i++;
AMF_OBJECT_ITEM_VALUE(prop[i], AMF_NUMBER, "height", &height, sizeof(height));
i++;
AMF_OBJECT_ITEM_VALUE(prop[i], AMF_NUMBER, "width", &width, sizeof(width));
i++;
AMF_OBJECT_ITEM_VALUE(items[0], AMF_OBJECT, "onMetaData", prop, i);
const uint8_t* end = data + bytes;
if (AMF_STRING != data[0] || NULL == (data = AMFReadString(data + 1, end, 0, buffer, sizeof(buffer) - 1)))
return -1;
if (0 == strcmp(buffer, "@setDataFrame"))
{
if (AMF_STRING != data[0] || NULL == (data = AMFReadString(data + 1, end, 0, buffer, sizeof(buffer) - 1)))
return -1;
}
if (0 != strcmp(buffer, "onMetaData"))
return 0;
if (amf_read_items(data, end, items, sizeof(items) / sizeof(items[0])))
{
if (framerate > 0)
_pVideoEncoder->setFrameRate(framerate > 60 ? 60 : framerate);
if (videodatarate > 0)
_pVideoEncoder->setBitrate((int64_t)videodatarate);
if (audiodatarate > 0)
_pAudioEncoder->setBitrate((int64_t)audiodatarate);
}
return 0;
}
inline const uint8_t* RTMPDemuxHandler::annexBStartcode(const uint8_t* data, size_t bytes)
{
size_t i;
for (i = 2; i + 1 < bytes; i++)
{
if (0x01 == data[i] && 0x00 == data[i - 1] && 0x00 == data[i - 2])
return data + i + 1;
}
return nullptr;
}
bool RTMPDemuxHandler::isH264BFrame(const void* h264, size_t bytes)
{
ptrdiff_t n;
const uint8_t *p, *next, *end;
end = (const uint8_t*)h264 + bytes;
p = annexBStartcode((const uint8_t*)h264, bytes);
while (p)
{
next = annexBStartcode(p, end - p);
if (next)
n = next - p - 3;
else
n = end - p;
while (n > 0 && 0 == p[n - 1])
n--;
assert(n > 0);
if (n > 0)
{
if ((p[0] & 0x1f) == 1)
{
bitstream_t stream;
bitstream_init(&stream, (const uint8_t*)(p + 1), n - 1);
bitstream_read_ue(&stream);
int32_t sliceType = bitstream_read_ue(&stream);
if (sliceType == 1)
return true;
}
}
p = next;
}
return false;
}
int RTMPDemuxHandler::processH264Data(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags)
{
if (!_videoInfoParsed)
mpeg4_h264_annexb_nalu(data, bytes, RTMPDemuxHandler::h264NALhandler, this);
enqueueVideoPacket(data, bytes, pts, dts, flags);
return 0;
}
int RTMPDemuxHandler::processH265Data(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags)
{
if (!_videoInfoParsed)
mpeg4_h264_annexb_nalu(data, bytes, RTMPDemuxHandler::h265NALhandler, this);
enqueueVideoPacket(data, bytes, pts, dts, flags);
return 0;
}
int RTMPDemuxHandler::processASCData(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags)
{
struct mpeg4_aac_t aac;
if (mpeg4_aac_audio_specific_config_load((const uint8_t*)data, bytes, &aac) >= 0)
{
_pAudioEncoder->setCodecID(Core::CodecID::AAC);
_pAudioEncoder->setFrameSize(1024);
_pAudioEncoder->setSampleFormat(Core::SampleFormat::FLTP);
_pAudioEncoder->setSampleRate((int)aac.sampling_frequency);
_pAudioEncoder->setChannelLayout(aac.channels == 2 ? Core::ChannelLayout::STEREO : Core::ChannelLayout::MONO);
_pAudioEncoder->commit();
logger().information("%s: Audio found: AAC %dHz %s", name(), (int)aac.sampling_frequency,
Core::channelLayoutToString(_pAudioEncoder->getChannelLayout()));
;
}
return 0;
}
int RTMPDemuxHandler::processAACData(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags)
{
enqueueAudioPacket(data, bytes, pts, dts, flags);
return 0;
}
int RTMPDemuxHandler::processOpusHeadData(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags)
{
struct opus_head_t opus;
if (opus_head_load((const uint8_t*)data, bytes, &opus) >= 0)
{
_pAudioEncoder->setCodecID(Core::CodecID::OPUS);
_pAudioEncoder->setFrameSize(960);
_pAudioEncoder->setSampleFormat(Core::SampleFormat::S16);
_pAudioEncoder->setSampleRate((int)opus.input_sample_rate);
_pAudioEncoder->setChannelLayout(opus_head_channels(&opus) == 2 ? Core::ChannelLayout::STEREO
: Core::ChannelLayout::MONO);
_pAudioEncoder->commit();
logger().information("%s: Audio found: OPUS %dHz %s", name(), (int)opus.input_sample_rate,
Core::channelLayoutToString(_pAudioEncoder->getChannelLayout()));
}
return 0;
}
int RTMPDemuxHandler::processOpusData(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags)
{
enqueueAudioPacket(data, bytes, pts, dts, flags);
return 0;
}
int RTMPDemuxHandler::processG711AData(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags)
{
if (!_audioInfoParsed)
{
_pAudioEncoder->setCodecID(Core::CodecID::G711A);
_pAudioEncoder->setFrameSize(1024);
_pAudioEncoder->setSampleFormat(Core::SampleFormat::S16P);
_pAudioEncoder->setSampleRate((int)8000);
_pAudioEncoder->setChannelLayout(Core::ChannelLayout::MONO);
_pAudioEncoder->commit();
logger().information("%s: Audio found: G711A %dHz %s", name(), 8000, std::string("MONO"));
_audioInfoParsed = true;
}
enqueueAudioPacket(data, bytes, pts, dts, flags);
return 0;
}
int RTMPDemuxHandler::processG711UData(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags)
{
if (!_audioInfoParsed)
{
_pAudioEncoder->setCodecID(Core::CodecID::G711U);
_pAudioEncoder->setFrameSize(1024);
_pAudioEncoder->setSampleFormat(Core::SampleFormat::S16P);
_pAudioEncoder->setSampleRate((int)8000);
_pAudioEncoder->setChannelLayout(Core::ChannelLayout::MONO);
_pAudioEncoder->commit();
logger().information("%s: Audio found: G711U %dHz %s", name(), 8000, std::string("MONO"));
_audioInfoParsed = true;
}
enqueueAudioPacket(data, bytes, pts, dts, flags);
return 0;
}
void RTMPDemuxHandler::enqueueVideoPacket(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags)
{
uint8_t* pBuffer = new uint8_t[bytes];
memcpy(pBuffer, data, bytes);
uint64_t localPTS = pts;
uint64_t localDTS = dts;
adjustTimestamp(localPTS, localDTS);
updateTimeline(localPTS);
_post(new Core::Packet(_pVideoEncoder->code(),
Core::Packet::FLAG_IS_VIDEO | (flags ? Core::Packet::FLAG_IS_KEY : 0), pBuffer, bytes,
localPTS, localDTS),
Core::MediaType::VIDEO);
}
void RTMPDemuxHandler::enqueueAudioPacket(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags)
{
uint8_t* pBuffer = new uint8_t[bytes];
memcpy(pBuffer, data, bytes);
uint64_t localPTS = pts;
uint64_t localDTS = dts;
adjustTimestamp(localPTS, localDTS);
updateTimeline(localPTS);
_post(new Core::Packet(_pAudioEncoder->code(), flags, pBuffer, bytes, localPTS, localDTS), Core::MediaType::AUDIO);
}
int RTMPDemuxHandler::clientSend(void* param, const void* header, size_t len, const void* data, size_t bytes)
{
socket_bufvec_t vec[2];
socket_setbufvec(vec, 0, (void*)header, len);
socket_setbufvec(vec, 1, (void*)data, bytes);
return socket_send_v_all_by_time(((RTMPDemuxHandler*)param)->_socket, vec, bytes ? 2 : 1, 0, 2000);
}
int RTMPDemuxHandler::clientOnAudio(void* param, const void* data, size_t bytes, uint32_t timestamp)
{
return flv_demuxer_input(((RTMPDemuxHandler*)param)->_pDemuxer, FLV_TYPE_AUDIO, data, bytes, timestamp);
}
int RTMPDemuxHandler::clientOnVideo(void* param, const void* data, size_t bytes, uint32_t timestamp)
{
return flv_demuxer_input(((RTMPDemuxHandler*)param)->_pDemuxer, FLV_TYPE_VIDEO, data, bytes, timestamp);
}
int RTMPDemuxHandler::clientOnScript(void* param, const void* data, size_t bytes, uint32_t timestamp)
{
return flv_demuxer_input(((RTMPDemuxHandler*)param)->_pDemuxer, FLV_TYPE_SCRIPT, data, bytes, timestamp);
}
int RTMPDemuxHandler::flvDemuxerHandler(void* param, int codec, const void* data, size_t bytes, uint32_t pts,
uint32_t dts, int flags)
{
int rc;
RTMPDemuxHandler* pHandler = (RTMPDemuxHandler*)param;
switch (codec)
{
case FLV_SCRIPT_METADATA:
rc = pHandler->parseFLVMetaData((const uint8_t*)data, bytes);
break;
case FLV_VIDEO_AVCC:
break;
case FLV_VIDEO_H264:
rc = pHandler->processH264Data(data, bytes, pts, dts, flags);
break;
case FLV_VIDEO_HVCC:
break;
case FLV_VIDEO_H265:
rc = pHandler->processH265Data(data, bytes, pts, dts, flags);
break;
case FLV_AUDIO_ASC:
rc = pHandler->processASCData(data, bytes, pts, dts, flags);
break;
case FLV_AUDIO_AAC:
rc = pHandler->processAACData(data, bytes, pts, dts, flags);
break;
case FLV_AUDIO_OPUS_HEAD:
rc = pHandler->processOpusHeadData(data, bytes, pts, dts, flags);
break;
case FLV_AUDIO_OPUS:
rc = pHandler->processOpusData(data, bytes, pts, dts, flags);
break;
case FLV_AUDIO_G711A:
rc = pHandler->processG711AData(data, bytes, pts, dts, flags);
break;
case FLV_AUDIO_G711U:
rc = pHandler->processG711UData(data, bytes, pts, dts, flags);
break;
default:
logger().warning("%s: UNIMPLEMENTED FLV-CODEC type: %d(%#x)", pHandler->name(), codec, (uint32_t)codec);
break;
}
(void)rc;
return 0;
}
void RTMPDemuxHandler::h264NALhandler(void* param, const uint8_t* nalu, size_t bytes)
{
RTMPDemuxHandler* pHandler = (RTMPDemuxHandler*)param;
struct h264_sps_t sps;
if (h264_sps_parse(nalu, bytes, &sps) == 0)
{
int x, y, width, height;
h264_display_rect(&sps, &x, &y, &width, &height);
auto pEncoder = pHandler->_pVideoEncoder;
pEncoder->setCodecID(Core::CodecID::H264);
pEncoder->setWidth(width);
pEncoder->setHeight(height);
pEncoder->commit();
pHandler->_videoInfoParsed = true;
logger().information("%s: Video found: H.264 %dx%d", pHandler->name(), width, height);
}
}
void RTMPDemuxHandler::h265NALhandler(void* param, const uint8_t* nalu, size_t bytes)
{
RTMPDemuxHandler* pHandler = (RTMPDemuxHandler*)param;
struct h265_sps_t sps;
if (h265_sps_parse(nalu, bytes, &sps) == 0)
{
int x, y, width, height;
h265_display_rect(&sps, &x, &y, &width, &height);
auto pEncoder = pHandler->_pVideoEncoder;
pEncoder->setCodecID(Core::CodecID::H265);
pEncoder->setWidth(width);
pEncoder->setHeight(height);
pEncoder->commit();
pHandler->_videoInfoParsed = true;
logger().information("%s: Video found: H.265 %dx%d", pHandler->name(), width, height);
}
}
void RTMPDemuxHandler::adjustTimestamp(uint64_t& pts, uint64_t& dts)
{
pts *= 1000ull;
dts *= 1000ull;
auto drift = getTimeDrift(pts);
pts += drift;
dts += drift;
}
void RTMPDemuxHandler::updateTimeline(uint64_t ts)
{
if (getTimeline() < ts)
setTimeline(ts);
}
void RTMPDemuxHandler::seek(uint64_t ts)
{
}
}
}

View file

@ -0,0 +1,97 @@
#ifndef E3F40CBC_D6F1_4B8F_B90E_6765659CF90F
#define E3F40CBC_D6F1_4B8F_B90E_6765659CF90F
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "../core/Demuxer.h"
extern "C"
{
#include <media-server/librtmp/rtmp-client.h>
#include <media-server/libflv/flv-demuxer.h>
#include <media-server/libflv/flv-proto.h>
#include <media-server/libflv/opus-head.h>
#include <media-server/libflv/mpeg4-aac.h>
#include <media-server/libflv/amf0.h>
#include <media-server/libflv/mpeg4-avc.h>
#include <media-server/libflv/mpeg4-hevc.h>
#include <avcodec/h264/bitstream.h>
#include <avcodec/h264/h264-nal.h>
#include <avcodec/h264/h264-sps.h>
#include <avcodec/h264/h264-pps.h>
#include <avcodec/h264/h264-util.h>
#include <avcodec/h264/h264-internal.h>
#include <avcodec/h265/h265-parser.h>
#include <sdk/include/sockutil.h>
}
namespace Video
{
namespace MediaServer
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class RTMPDemuxHandler: public Core::DemuxHandler
{
public:
RTMPDemuxHandler(const std::string& url);
~RTMPDemuxHandler();
public:
void run(Core::VideoEncoder::Ptr pVideoEncoder, Core::AudioEncoder::Ptr pAudioEncoder, const Post& post,
std::atomic<bool>& isCancelled) override;
protected:
int parseFLVMetaData(const uint8_t* pData, size_t length);
int processH264Data(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags);
int processH265Data(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags);
int processASCData(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags);
int processAACData(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags);
int processOpusHeadData(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags);
int processOpusData(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags);
int processG711AData(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags);
int processG711UData(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags);
void enqueueVideoPacket(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags);
void enqueueAudioPacket(const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags);
inline const uint8_t* annexBStartcode(const uint8_t* data, size_t bytes);
bool isH264BFrame(const void* h264, size_t bytes);
void adjustTimestamp(uint64_t& pts, uint64_t& dts);
void updateTimeline(uint64_t ts);
void seek(uint64_t) override;
private:
socket_t _socket;
rtmp_client_t* _pCient;
flv_demuxer_t* _pDemuxer;
bool _audioInfoParsed;
bool _videoInfoParsed;
uint32_t _iFrameLength;
Core::VideoEncoder::Ptr _pVideoEncoder;
Core::AudioEncoder::Ptr _pAudioEncoder;
Post _post;
union
{
struct mpeg4_avc_t avc;
struct mpeg4_hevc_t hevc;
} _v;
protected:
static int clientSend(void* param, const void* header, size_t len, const void* data, size_t bytes);
static int clientOnAudio(void* param, const void* data, size_t bytes, uint32_t timestamp);
static int clientOnVideo(void* param, const void* data, size_t bytes, uint32_t timestamp);
static int clientOnScript(void* param, const void* data, size_t bytes, uint32_t timestamp);
static int flvDemuxerHandler(void* param, int codec, const void* data, size_t bytes, uint32_t pts, uint32_t dts,
int flags);
static void h264NALhandler(void* param, const uint8_t* nalu, size_t bytes);
static void h265NALhandler(void* param, const uint8_t* nalu, size_t bytes);
};
}
}
#endif

View file

@ -0,0 +1,369 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "RTMPMuxer.h"
#include "../core/VideoEncoder.h"
#include "../core/AudioEncoder.h"
#include "../core/SampleFormat.h"
#include "../core/System.h"
#include <media-server/libflv/flv-proto.h>
namespace Video
{
namespace MediaServer
{
/*
*******************************************************************************
* TYPEDEFS
*******************************************************************************
*/
struct CodecIDItem
{
Core::CodecID id;
int type;
};
/*
*******************************************************************************
* LOCAL VARIABLES
*******************************************************************************
*/
static const std::array<CodecIDItem, 7> codecMap = {{
{Core::CodecID::H264, FLV_VIDEO_H264},
{Core::CodecID::H265, FLV_VIDEO_H265},
{Core::CodecID::MP3, FLV_AUDIO_MP3},
{Core::CodecID::AAC, FLV_AUDIO_AAC},
{Core::CodecID::OPUS, FLV_AUDIO_OPUS},
{Core::CodecID::G711A, FLV_AUDIO_G711A},
{Core::CodecID::G711U, FLV_AUDIO_G711U},
}};
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
RTMPMuxer::Stream::Stream(const std::string& name, Core::CodecID codecID, Core::PacketQueue::Ptr pQueue)
: _name(name), _codecID(codecID), _pQueue(pQueue)
{
switch (codecID)
{
case Core::CodecID::H264:
_pMuxerMethod = flv_muxer_avc;
break;
case Core::CodecID::H265:
_pMuxerMethod = flv_muxer_hevc;
break;
case Core::CodecID::MP3:
_pMuxerMethod = flv_muxer_mp3;
break;
case Core::CodecID::AAC:
_pMuxerMethod = flv_muxer_aac;
break;
case Core::CodecID::OPUS:
_pMuxerMethod = flv_muxer_opus;
break;
case Core::CodecID::G711A:
_pMuxerMethod = flv_muxer_g711a;
break;
case Core::CodecID::G711U:
_pMuxerMethod = flv_muxer_g711u;
break;
default:
_pMuxerMethod = nullptr;
throw Poco::NotImplementedException(
Poco::format("%s: Unsupported codec: %s", name, Core::codecIDToString(codecID)));
}
}
RTMPMuxer::Stream::~Stream()
{
}
inline int Video::MediaServer::RTMPMuxer::Stream::muxer(flv_muxer_t* muxer, const void* data, size_t bytes,
uint32_t pts, uint32_t dts)
{
if (!_pMuxerMethod)
throw Poco::BugcheckException(Poco::format("%s: muxer method is NULL!", name()));
return _pMuxerMethod(muxer, data, bytes, pts, dts);
}
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
Video::MediaServer::RTMPMuxer::RTMPMuxer(const std::string& url)
: Core::Muxer(Poco::format("MediaServer::RTMPMuxer(%s)", url), url)
{
}
RTMPMuxer::~RTMPMuxer()
{
}
void RTMPMuxer::connect()
{
Poco::URI uri(url());
std::string scheme = uri.getScheme();
std::string host = uri.getHost();
short port = uri.getPort();
if (port <= 0)
port = 1935;
std::string path = uri.getPath();
if (!path.empty() && path[0] == '/')
path = path.substr(1);
size_t pos = path.find_first_of('/');
if (pos == std::string::npos)
throw Poco::InvalidArgumentException(Poco::format("%s: Invalid URL: %s", name(), url()));
_socket = socket_connect_host(host.c_str(), port, 2000);
if (socket_invalid == _socket)
{
throw Poco::InvalidAccessException(Poco::format("%s: Connect FAIL", name()));
}
socket_setnonblock(_socket, 0);
std::string turl = scheme + "://" + host + ":" + std::to_string(port) + "/" + path.substr(0, pos);
std::string appname = path.substr(0, pos);
std::string playpath = path.substr(pos + 1);
struct rtmp_client_handler_t handler = {};
handler.send = RTMPMuxer::clientSend;
_pClient = rtmp_client_create(appname.c_str(), playpath.c_str(), turl.c_str(), this, &handler);
if (!_pClient)
throw Poco::InvalidAccessException(Poco::format("%s: Create RTMP-Client FAIL!", name()));
_pMuxer = flv_muxer_create(RTMPMuxer::muxerHandler, this);
if (!_pMuxer)
throw Poco::InvalidAccessException(Poco::format("%s: Create RTMP-Muxer FAIL!", name()));
int rc = rtmp_client_start(_pClient, 0);
if (rc != 0)
throw Poco::InvalidAccessException(Poco::format("%s: Start RTMP-Client FAIL: %d", name(), rc));
int limit = 50;
uint8_t buffer[4096];
while (--limit > 0 && 4 != rtmp_client_getstate(_pClient) &&
(rc = socket_recv(_socket, buffer, sizeof(buffer), 0)) > 0)
{
if (rtmp_client_input(_pClient, buffer, rc) != 0)
throw Poco::InvalidAccessException(Poco::format("%s: RTMP-Client input handle FAIL!", name()));
}
struct flv_metadata_t metadata = {};
uint8_t opusHead[19] = {0};
bool hasOpus = false;
previousComponents().foreach (
[&](Core::Component::Ptr pPrevious) -> void
{
auto pVideoEncoder = pPrevious.cast<Core::VideoEncoder>();
if (!pVideoEncoder.isNull())
{
_streams.push_back(new Stream(Poco::format("%s/Stream(%s)", name(), pVideoEncoder->name()),
pVideoEncoder->getCodecID(), packetQueues().find(pPrevious)));
metadata.videocodecid = getCodecID(pVideoEncoder->getCodecID());
metadata.videodatarate = pVideoEncoder->getBitrate() / 1000;
metadata.framerate = pVideoEncoder->getFrameRate();
metadata.width = pVideoEncoder->getWidth();
metadata.height = pVideoEncoder->getHeight();
return;
}
auto pAudioEncoder = pPrevious.cast<Core::AudioEncoder>();
if (!pAudioEncoder.isNull())
{
_streams.push_back(new Stream(Poco::format("%s/Stream(%s)", name(), pAudioEncoder->name()),
pAudioEncoder->getCodecID(), packetQueues().find(pPrevious)));
metadata.audiocodecid = getCodecID(pAudioEncoder->getCodecID());
metadata.audiocodecid >>= 4;
metadata.audiosamplerate = pAudioEncoder->getSampleRate();
metadata.audiodatarate = pAudioEncoder->getBitrate() / 1000;
metadata.audiosamplesize = Core::sampleFormatBitWidth(pAudioEncoder->getSampleFormat());
metadata.stereo = pAudioEncoder->getChannelLayout() == Core::ChannelLayout::STEREO ? true : false;
if (pAudioEncoder->getCodecID() == Core::CodecID::OPUS)
{
memcpy(opusHead, "OpusHead", 8);
opusHead[8] = 1;
opusHead[9] = (uint8_t)(metadata.stereo ? 2 : 1);
uint32_t sr = (uint32_t)metadata.audiosamplerate;
opusHead[12] = (uint8_t)(sr & 0xFF);
opusHead[13] = (uint8_t)((sr >> 8) & 0xFF);
opusHead[14] = (uint8_t)((sr >> 16) & 0xFF);
opusHead[15] = (uint8_t)((sr >> 24) & 0xFF);
hasOpus = true;
}
}
});
flv_muxer_metadata(_pMuxer, &metadata);
if (hasOpus)
{
for (auto& pStream : _streams)
{
if (pStream->codecID() == Core::CodecID::OPUS)
{
pStream->muxer(_pMuxer, opusHead, 19, 0, 0);
break;
}
}
}
}
void RTMPMuxer::mux()
{
drainInput();
std::sort(_streams.begin(), _streams.end(), [](auto a, auto b) { return *a < *b; });
auto& pStream = *_streams.begin();
Core::Packet::Ptr pPacket = pStream->queue()->dequeue(4000);
if (pStream->serialCodeWatcher().isChanged(pPacket->serialCode()))
throw Poco::ApplicationException(Poco::format("%s: Stream changed, restart...", name()));
pStream->setTimeline(pPacket->pts());
int rc = pStream->muxer(_pMuxer, pPacket->data(), pPacket->length(), pPacket->pts() / 1000, pPacket->dts() / 1000);
if (rc < 0)
throw Poco::ProtocolException(Poco::format("%s: mux %s FAIL: %d (len:%u, pts:%Lu, dts:%Lu)", name(),
(pPacket->isVideo() ? std::string("video") : std::string("audio")),
rc, pPacket->length(), pPacket->pts(), pPacket->dts()));
#if (TRACE_VIDEO_FRAME_RATE)
if (pPacket->isVideo())
{
count++;
uint64_t now = Core::System::timestamp();
int64_t delta = now - timeline;
if (delta >= 1000000)
{
timeline = now;
std::cout << " ==> " << name() << " fps: " << (double)count / (delta / 1000000) << std::endl;
count = 0;
}
}
#endif
}
void RTMPMuxer::disconnect()
{
if (_pClient)
{
rtmp_client_stop(_pClient);
rtmp_client_destroy(_pClient);
_pClient = nullptr;
}
Poco::Thread::sleep(100);
if (_pMuxer)
{
flv_muxer_destroy(_pMuxer);
_pMuxer = nullptr;
}
if (_socket != socket_invalid)
{
socket_close(_socket);
_socket = socket_invalid;
}
_streams.clear();
}
void RTMPMuxer::drainInput()
{
uint8_t buffer[2048];
while (true)
{
int rc = socket_recv_by_time(_socket, buffer, sizeof(buffer), 0, 0);
if (SOCKET_TIMEDOUT == rc)
break;
if (0 == rc)
throw Poco::IOException(Poco::format("%s: Connection closed by peer", name()));
if (rc < 0)
throw Poco::IOException(Poco::format("%s: socket_recv FAIL: %d", name(), rc));
if (rtmp_client_input(_pClient, buffer, (size_t)rc) != 0)
throw Poco::ProtocolException(Poco::format("%s: RTMP-Client input handle FAIL!", name()));
}
}
int RTMPMuxer::clientSend(void* param, const void* header, size_t len, const void* data, size_t bytes)
{
RTMPMuxer* pPusher = (RTMPMuxer*)param;
socket_bufvec_t vec[2];
socket_setbufvec(vec, 0, (void*)header, len);
socket_setbufvec(vec, 1, (void*)data, bytes);
#if defined(CORRUPT_RTMP_CHUNK_DATA)
rtmp_corrupt_data(header, len);
rtmp_corrupt_data(data, bytes);
#endif
return socket_send_v_all_by_time(pPusher->_socket, vec, bytes > 0 ? 2 : 1, 0, 5000);
}
int RTMPMuxer::muxerHandler(void* param, int type, const void* data, size_t bytes, uint32_t timestamp)
{
RTMPMuxer* pPusher = (RTMPMuxer*)param;
int rc;
switch (type)
{
case FLV_TYPE_SCRIPT:
rc = rtmp_client_push_script(pPusher->_pClient, data, bytes, timestamp);
break;
case FLV_TYPE_VIDEO:
rc = rtmp_client_push_video(pPusher->_pClient, data, bytes, timestamp);
break;
case FLV_TYPE_AUDIO:
rc = rtmp_client_push_audio(pPusher->_pClient, data, bytes, timestamp);
break;
default:
rc = -EINVAL;
break;
}
return rc;
}
const int RTMPMuxer::getCodecID(Core::CodecID id)
{
for (const auto& m : codecMap)
{
if (id == m.id)
return m.type;
}
throw Poco::NotFoundException(
Poco::format("Video::MediaServer: Can't find %s in codec-map", Core::codecIDToString(id)));
}
}
}

View file

@ -0,0 +1,115 @@
#ifndef F2B5C47C_73F6_4CFD_A4D6_9C8A1CFB55F5
#define F2B5C47C_73F6_4CFD_A4D6_9C8A1CFB55F5
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "../core/Muxer.h"
#include "util/ClassAssistant.h"
#include <sdk/include/sockutil.h>
#include <media-server/librtmp/rtmp-client.h>
#include <media-server/libflv/flv-muxer.h>
/*
*******************************************************************************
* MACROS
*******************************************************************************
*/
#define TRACE_VIDEO_FRAME_RATE (0)
namespace Video
{
namespace MediaServer
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class RTMPMuxer: public Core::Muxer
{
private:
class Stream
{
public:
using Ptr = Poco::SharedPtr<Stream>;
public:
Stream(const std::string& name, Core::CodecID codecID, Core::PacketQueue::Ptr pQueue);
~Stream();
public:
bool operator<(const Stream& other);
const std::string name() const;
Core::PacketQueue::Ptr queue();
Core::SerialCodeWatcher& serialCodeWatcher();
Core::CodecID codecID() const;
int muxer(flv_muxer_t* muxer, const void* data, size_t bytes, uint32_t pts, uint32_t dts);
PROPERTY_DECLARE(Timeline, uint64_t, 0, public, public);
private:
const std::string _name;
Core::CodecID _codecID;
Core::PacketQueue::Ptr _pQueue;
Core::SerialCodeWatcher _serialCodeWatcher;
int (*_pMuxerMethod)(flv_muxer_t*, const void*, size_t, uint32_t, uint32_t);
};
public:
RTMPMuxer(const std::string& url);
~RTMPMuxer();
protected:
void connect() override;
void mux() override;
void disconnect() override;
protected:
void drainInput();
const int getCodecID(Core::CodecID id);
static int clientSend(void* param, const void* header, size_t len, const void* data, size_t bytes);
static int muxerHandler(void* param, int type, const void* data, size_t bytes, uint32_t timestamp);
private:
socket_t _socket;
rtmp_client_t* _pClient;
flv_muxer_t* _pMuxer;
std::vector<Stream::Ptr> _streams;
#if (TRACE_VIDEO_FRAME_RATE)
int count = 0;
uint64_t timeline = 0;
#endif
};
inline bool RTMPMuxer::Stream::operator<(const Stream& other)
{
return _Timeline < other._Timeline;
}
inline const std::string RTMPMuxer::Stream::name() const
{
return _name;
}
inline Core::PacketQueue::Ptr RTMPMuxer::Stream::queue()
{
return _pQueue;
}
inline Core::SerialCodeWatcher& RTMPMuxer::Stream::serialCodeWatcher()
{
return _serialCodeWatcher;
}
inline Core::CodecID RTMPMuxer::Stream::codecID() const
{
return _codecID;
}
}
}
#endif