mirror of
https://github.com/mii443/libdatachannel.git
synced 2025-08-23 15:48:03 +00:00
Add h264 and opus streaming support to libdatachannel
This commit is contained in:
@ -64,6 +64,14 @@ set(LIBDATACHANNEL_SOURCES
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/track.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/track.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/processor.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/processor.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/capi.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/capi.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/rtppacketizationconfig.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpsenderreportable.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/rtppacketizer.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/opusrtppacketizer.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/opuspacketizationhandler.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/h264rtppacketizer.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/nalunit.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/h264packetizationhandler.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(LIBDATACHANNEL_WEBSOCKET_SOURCES
|
set(LIBDATACHANNEL_WEBSOCKET_SOURCES
|
||||||
@ -95,6 +103,14 @@ set(LIBDATACHANNEL_HEADERS
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtp.hpp
|
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtp.hpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/track.hpp
|
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/track.hpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/websocket.hpp
|
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/websocket.hpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtppacketizationconfig.hpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpsenderreportable.hpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtppacketizer.hpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/opusrtppacketizer.hpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/opuspacketizationhandler.hpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h264rtppacketizer.hpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/nalunit.hpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h264packetizationhandler.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(TESTS_SOURCES
|
set(TESTS_SOURCES
|
||||||
|
72
include/rtc/h264packetizationhandler.hpp
Normal file
72
include/rtc/h264packetizationhandler.hpp
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* libdatachannel client example
|
||||||
|
* Copyright (c) 2020 Filip Klembara (in2core)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef H264PacketizationHandler_hpp
|
||||||
|
#define H264PacketizationHandler_hpp
|
||||||
|
|
||||||
|
#include "rtcp.hpp"
|
||||||
|
#include "h264rtppacketizer.hpp"
|
||||||
|
#include "rtcpsenderreportable.hpp"
|
||||||
|
#include "nalunit.hpp"
|
||||||
|
|
||||||
|
#if RTC_ENABLE_MEDIA
|
||||||
|
|
||||||
|
namespace rtc {
|
||||||
|
|
||||||
|
/// Handler for H264 packetization
|
||||||
|
class H264PacketizationHandler: public RtcpHandler, public RTCPSenderReportable {
|
||||||
|
/// RTP packetizer for H264
|
||||||
|
const std::shared_ptr<H264RTPPacketizer> packetizer;
|
||||||
|
|
||||||
|
const uint16_t maximumFragmentSize;
|
||||||
|
|
||||||
|
std::shared_ptr<NalUnits> splitMessage(message_ptr message);
|
||||||
|
public:
|
||||||
|
/// Nalunit separator
|
||||||
|
enum class Separator {
|
||||||
|
LongStartSequence, // 0x00, 0x00, 0x00, 0x01
|
||||||
|
ShortStartSequence, // 0x00, 0x00, 0x01
|
||||||
|
StartSequence, // LongStartSequence or ShortStartSequence
|
||||||
|
Length // first 4 bytes is nal unit length
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Construct handler for H264 packetization.
|
||||||
|
/// @param separator Nal units separator
|
||||||
|
/// @param packetizer RTP packetizer for h264
|
||||||
|
H264PacketizationHandler(Separator separator, std::shared_ptr<H264RTPPacketizer> packetizer, uint16_t maximumFragmentSize = NalUnits::defaultMaximumFragmentSize);
|
||||||
|
|
||||||
|
/// Returns message unchanged
|
||||||
|
/// @param ptr message
|
||||||
|
message_ptr incoming(message_ptr ptr) override;
|
||||||
|
|
||||||
|
/// Returns packetized message if message type is binary
|
||||||
|
/// @note NAL units in `ptr` message must be separated by `separator` given in constructor
|
||||||
|
/// @note If message generates multiple rtp packets, all but last are send using `outgoingCallback`. It is your responsibility to send last packet.
|
||||||
|
/// @param ptr message containing all NAL units for current timestamp (one sample)
|
||||||
|
/// @return last packet
|
||||||
|
message_ptr outgoing(message_ptr ptr) override;
|
||||||
|
private:
|
||||||
|
/// Separator
|
||||||
|
const Separator separator;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#endif /* RTC_ENABLE_MEDIA */
|
||||||
|
|
||||||
|
#endif /* H264PacketizationHandler_hpp */
|
45
include/rtc/h264rtppacketizer.hpp
Normal file
45
include/rtc/h264rtppacketizer.hpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* libdatachannel streamer example
|
||||||
|
* Copyright (c) 2020 Filip Klembara (in2core)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef H264RTPPacketizer_hpp
|
||||||
|
#define H264RTPPacketizer_hpp
|
||||||
|
|
||||||
|
#include "rtppacketizer.hpp"
|
||||||
|
|
||||||
|
#if RTC_ENABLE_MEDIA
|
||||||
|
|
||||||
|
namespace rtc {
|
||||||
|
|
||||||
|
/// RTP packetization of h264 payload
|
||||||
|
class H264RTPPacketizer: public rtc::RTPPacketizer {
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Default clock rate for H264 in RTP
|
||||||
|
static const auto defaultClockRate = 90 * 1000;
|
||||||
|
|
||||||
|
/// Constructs h264 payload packetizer with given RTP configuration.
|
||||||
|
/// @note RTP configuration is used in packetization process which may change some configuration properties such as sequence number.
|
||||||
|
/// @param rtpConfig RTP configuration
|
||||||
|
H264RTPPacketizer(std::shared_ptr<rtc::RTPPacketizationConfig> rtpConfig);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#endif /* RTC_ENABLE_MEDIA */
|
||||||
|
|
||||||
|
#endif /* H264RTPPacketizer_hpp */
|
155
include/rtc/nalunit.hpp
Normal file
155
include/rtc/nalunit.hpp
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* libdatachannel streamer example
|
||||||
|
* Copyright (c) 2020 Filip Klembara (in2core)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NalUnit_hpp
|
||||||
|
#define NalUnit_hpp
|
||||||
|
|
||||||
|
#include "include.hpp"
|
||||||
|
|
||||||
|
#if RTC_ENABLE_MEDIA
|
||||||
|
|
||||||
|
namespace rtc {
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
|
/// Nalu header
|
||||||
|
struct NalUnitHeader {
|
||||||
|
bool forbiddenBit() { return _first >> 7; }
|
||||||
|
uint8_t nri() { return _first >> 5 & 0x03; }
|
||||||
|
uint8_t unitType() { return _first & 0x1F; }
|
||||||
|
|
||||||
|
void setForbiddenBit(bool isSet) { _first = (_first & 0x7F) | (isSet << 7); }
|
||||||
|
void setNRI(uint8_t nri) { _first = (_first & 0x9F) | ((nri & 0x03) << 5); }
|
||||||
|
void setUnitType(uint8_t type) { _first = (_first &0xE0) | (type & 0x1F); }
|
||||||
|
private:
|
||||||
|
uint8_t _first = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Nalu fragment header
|
||||||
|
struct NalUnitFragmentHeader {
|
||||||
|
bool isStart() { return _first >> 7; }
|
||||||
|
bool reservedBit6() { return (_first >> 6) & 0x01; }
|
||||||
|
bool isEnd() { return (_first >> 5) & 0x01; }
|
||||||
|
uint8_t unitType() { return _first & 0x1F; }
|
||||||
|
|
||||||
|
void setStart(bool isSet) { _first = (_first & 0x7F) | (isSet << 7); }
|
||||||
|
void setEnd(bool isSet) { _first = (_first & 0xDF) | (isSet << 6); }
|
||||||
|
void setReservedBit6(bool isSet) { _first = (_first & 0xBF) | (isSet << 5); }
|
||||||
|
void setUnitType(uint8_t type) { _first = (_first &0xE0) | (type & 0x1F); }
|
||||||
|
private:
|
||||||
|
uint8_t _first = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
/// Nal unit
|
||||||
|
struct NalUnit: rtc::binary {
|
||||||
|
NalUnit(const NalUnit &unit) = default;
|
||||||
|
NalUnit(size_t size, bool includingHeader = true): rtc::binary(size + (includingHeader ? 0 : 1)) { }
|
||||||
|
|
||||||
|
template <typename Iterator>
|
||||||
|
NalUnit(Iterator begin_, Iterator end_): rtc::binary(begin_, end_) { }
|
||||||
|
|
||||||
|
NalUnit(rtc::binary &&data) : rtc::binary(std::move(data)) { }
|
||||||
|
|
||||||
|
bool forbiddenBit() { return header()->forbiddenBit(); }
|
||||||
|
uint8_t nri() { return header()->nri(); }
|
||||||
|
uint8_t unitType() { return header()->unitType(); }
|
||||||
|
rtc::binary payload() {
|
||||||
|
assert(size() >= 1);
|
||||||
|
return {begin() + 1, end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
void setForbiddenBit(bool isSet) { header()->setForbiddenBit(isSet); }
|
||||||
|
void setNRI(uint8_t nri) { header()->setNRI(nri); }
|
||||||
|
void setUnitType(uint8_t type) { header()->setUnitType(type); }
|
||||||
|
void setPayload(rtc::binary payload) {
|
||||||
|
assert(size() >= 1);
|
||||||
|
erase(begin() + 1, end());
|
||||||
|
insert(end(), payload.begin(), payload.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
NalUnitHeader * header() {
|
||||||
|
assert(size() >= 1);
|
||||||
|
return (NalUnitHeader *) data();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Nal unit fragment A
|
||||||
|
struct NalUnitFragmentA: NalUnit {
|
||||||
|
enum class FragmentType {
|
||||||
|
Start,
|
||||||
|
Middle,
|
||||||
|
End
|
||||||
|
};
|
||||||
|
|
||||||
|
NalUnitFragmentA(FragmentType type, bool forbiddenBit, uint8_t nri, uint8_t unitType, rtc::binary data);
|
||||||
|
|
||||||
|
static std::vector<NalUnitFragmentA> fragmentsFrom(NalUnit nalu, uint16_t maximumFragmentSize);
|
||||||
|
|
||||||
|
uint8_t unitType() { return fragmentHeader()->unitType(); }
|
||||||
|
|
||||||
|
rtc::binary payload() {
|
||||||
|
assert(size() >= 2);
|
||||||
|
return {begin() + 2, end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
FragmentType type() {
|
||||||
|
if(fragmentHeader()->isStart()) {
|
||||||
|
return FragmentType::Start;
|
||||||
|
} else if(fragmentHeader()->isEnd()) {
|
||||||
|
return FragmentType::End;
|
||||||
|
} else {
|
||||||
|
return FragmentType::Middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUnitType(uint8_t type) { fragmentHeader()->setUnitType(type); }
|
||||||
|
|
||||||
|
void setPayload(rtc::binary payload) {
|
||||||
|
assert(size() >= 2);
|
||||||
|
erase(begin() + 2, end());
|
||||||
|
insert(end(), payload.begin(), payload.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFragmentType(FragmentType type);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
NalUnitHeader * fragmentIndicator() {
|
||||||
|
return (NalUnitHeader *) data();
|
||||||
|
}
|
||||||
|
|
||||||
|
NalUnitFragmentHeader * fragmentHeader() {
|
||||||
|
return (NalUnitFragmentHeader *) fragmentIndicator() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t nal_type_fu_A = 28;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NalUnits: public std::vector<NalUnit> {
|
||||||
|
public:
|
||||||
|
static const uint16_t defaultMaximumFragmentSize = 1100;
|
||||||
|
std::vector<rtc::binary> generateFragments(uint16_t maximumFragmentSize = NalUnits::defaultMaximumFragmentSize);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#endif /* RTC_ENABLE_MEDIA */
|
||||||
|
|
||||||
|
#endif /* NalUnit_hpp */
|
52
include/rtc/opuspacketizationhandler.hpp
Normal file
52
include/rtc/opuspacketizationhandler.hpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* libdatachannel client example
|
||||||
|
* Copyright (c) 2020 Filip Klembara (in2core)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OpusPacketizationHandler_hpp
|
||||||
|
#define OpusPacketizationHandler_hpp
|
||||||
|
|
||||||
|
#include "rtcpsenderreportable.hpp"
|
||||||
|
#include "opusrtppacketizer.hpp"
|
||||||
|
#include "rtcp.hpp"
|
||||||
|
|
||||||
|
#if RTC_ENABLE_MEDIA
|
||||||
|
|
||||||
|
namespace rtc {
|
||||||
|
|
||||||
|
/// Handler for opus packetization
|
||||||
|
class OpusPacketizationHandler: public RtcpHandler, public RTCPSenderReportable {
|
||||||
|
/// RTP packetizer for opus
|
||||||
|
const std::shared_ptr<OpusRTPPacketizer> packetizer;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Construct handler for opus packetization.
|
||||||
|
/// @param packetizer RTP packetizer for opus
|
||||||
|
OpusPacketizationHandler(std::shared_ptr<OpusRTPPacketizer> packetizer);
|
||||||
|
|
||||||
|
/// Returns message unchanged
|
||||||
|
/// @param ptr message
|
||||||
|
message_ptr incoming(message_ptr ptr) override;
|
||||||
|
/// Returns packetized message if message type is binary
|
||||||
|
/// @param ptr message
|
||||||
|
message_ptr outgoing(message_ptr ptr) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#endif /* RTC_ENABLE_MEDIA */
|
||||||
|
|
||||||
|
#endif /* OpusPacketizationHandler_hpp */
|
51
include/rtc/opusrtppacketizer.hpp
Normal file
51
include/rtc/opusrtppacketizer.hpp
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* libdatachannel streamer example
|
||||||
|
* Copyright (c) 2020 Filip Klembara (in2core)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OpusRTPPacketizer_hpp
|
||||||
|
#define OpusRTPPacketizer_hpp
|
||||||
|
|
||||||
|
#include "rtppacketizer.hpp"
|
||||||
|
|
||||||
|
#if RTC_ENABLE_MEDIA
|
||||||
|
|
||||||
|
namespace rtc {
|
||||||
|
|
||||||
|
/// RTP packetizer for opus
|
||||||
|
class OpusRTPPacketizer: public rtc::RTPPacketizer {
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// default clock rate used in opus RTP communication
|
||||||
|
static const uint32_t defaultClockRate = 48 * 1000;
|
||||||
|
|
||||||
|
/// Constructs opus packetizer with given RTP configuration.
|
||||||
|
/// @note RTP configuration is used in packetization process which may change some configuration properties such as sequence number.
|
||||||
|
/// @param rtpConfig RTP configuration
|
||||||
|
OpusRTPPacketizer(std::shared_ptr<rtc::RTPPacketizationConfig> rtpConfig);
|
||||||
|
|
||||||
|
/// Creates RTP packet for given payload based on `rtpConfig`.
|
||||||
|
/// @note This function increase sequence number after packetization.
|
||||||
|
/// @param payload RTP payload
|
||||||
|
/// @param setMark This needs to be `false` for all RTP packets with opus payload
|
||||||
|
rtc::message_ptr packetize(rtc::binary payload, bool setMark) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#endif /* RTC_ENABLE_MEDIA */
|
||||||
|
|
||||||
|
#endif /* OpusRTPPacketizer_hpp */
|
@ -25,5 +25,13 @@
|
|||||||
#include "peerconnection.hpp"
|
#include "peerconnection.hpp"
|
||||||
#include "websocket.hpp"
|
#include "websocket.hpp"
|
||||||
|
|
||||||
|
#if RTC_ENABLE_MEDIA
|
||||||
|
|
||||||
|
// opus/h264 streaming
|
||||||
|
#include "opuspacketizationhandler.hpp"
|
||||||
|
#include "h264packetizationhandler.hpp"
|
||||||
|
|
||||||
|
#endif /* RTC_ENABLE_MEDIA */
|
||||||
|
|
||||||
// C API
|
// C API
|
||||||
#include "rtc.h"
|
#include "rtc.h"
|
||||||
|
87
include/rtc/rtcpsenderreportable.hpp
Normal file
87
include/rtc/rtcpsenderreportable.hpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* libdatachannel streamer example
|
||||||
|
* Copyright (c) 2020 Filip Klembara (in2core)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RTCPSenderReporter_hpp
|
||||||
|
#define RTCPSenderReporter_hpp
|
||||||
|
|
||||||
|
#include "message.hpp"
|
||||||
|
#include "rtppacketizationconfig.hpp"
|
||||||
|
|
||||||
|
#if RTC_ENABLE_MEDIA
|
||||||
|
|
||||||
|
namespace rtc {
|
||||||
|
|
||||||
|
/// Class for sending RTCP SR
|
||||||
|
class RTCPSenderReportable {
|
||||||
|
bool needsToReport = false;
|
||||||
|
|
||||||
|
uint32_t packetCount = 0;
|
||||||
|
uint32_t payloadOctets = 0;
|
||||||
|
double timeOffset = 0;
|
||||||
|
|
||||||
|
uint32_t _previousReportedTimestamp = 0;
|
||||||
|
|
||||||
|
void addToReport(RTP * rtp, uint32_t rtpSize);
|
||||||
|
message_ptr getSenderReport(uint32_t timestamp);
|
||||||
|
protected:
|
||||||
|
/// Outgoing callback for sender reports
|
||||||
|
synchronized_callback<message_ptr> senderReportOutgoingCallback;
|
||||||
|
public:
|
||||||
|
static uint64_t secondsToNTP(double seconds);
|
||||||
|
|
||||||
|
/// Timestamp of previous sender report
|
||||||
|
const uint32_t & previousReportedTimestamp = _previousReportedTimestamp;
|
||||||
|
|
||||||
|
/// RTP configuration
|
||||||
|
const std::shared_ptr<RTPPacketizationConfig> rtpConfig;
|
||||||
|
|
||||||
|
RTCPSenderReportable(std::shared_ptr<RTPPacketizationConfig> rtpConfig);
|
||||||
|
|
||||||
|
/// Set `needsToReport` flag. Sender report will be sent before next RTP packet with same timestamp.
|
||||||
|
void setNeedsToReport();
|
||||||
|
|
||||||
|
/// Set offset to compute NTS for RTCP SR packets. Offset represents relation between real start time and timestamp of the stream in RTP packets
|
||||||
|
/// @note `time_offset = rtpConfig->startTime_s - rtpConfig->timestampToSeconds(rtpConfig->timestamp)`
|
||||||
|
void startRecording();
|
||||||
|
|
||||||
|
/// Send RTCP SR with given timestamp
|
||||||
|
/// @param timestamp timestamp of the RTCP SR
|
||||||
|
void sendReport(uint32_t timestamp);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// Calls given block with function for statistics. Sends RTCP SR packet with current timestamp before `block` call if `needs_to_report` flag is true.
|
||||||
|
/// @param block Block of code to run. This block has function for rtp stats recording.
|
||||||
|
template <typename T>
|
||||||
|
T withStatsRecording(std::function<T (std::function<void (message_ptr)>)> block) {
|
||||||
|
if (needsToReport) {
|
||||||
|
sendReport(rtpConfig->timestamp);
|
||||||
|
needsToReport = false;
|
||||||
|
}
|
||||||
|
auto result = block([this](message_ptr _rtp) {
|
||||||
|
auto rtp = reinterpret_cast<RTP *>(_rtp->data());
|
||||||
|
this->addToReport(rtp, _rtp->size());
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#endif /* RTC_ENABLE_MEDIA */
|
||||||
|
|
||||||
|
#endif /* RTCPSenderReporter_hpp */
|
91
include/rtc/rtppacketizationconfig.hpp
Normal file
91
include/rtc/rtppacketizationconfig.hpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* libdatachannel streamer example
|
||||||
|
* Copyright (c) 2020 Filip Klembara (in2core)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RTPPacketizationConfig_hpp
|
||||||
|
#define RTPPacketizationConfig_hpp
|
||||||
|
|
||||||
|
#include "rtp.hpp"
|
||||||
|
|
||||||
|
#if RTC_ENABLE_MEDIA
|
||||||
|
|
||||||
|
namespace rtc {
|
||||||
|
|
||||||
|
/// RTP configuration used in packetization process
|
||||||
|
struct RTPPacketizationConfig {
|
||||||
|
private:
|
||||||
|
uint32_t _startTimestamp = 0;
|
||||||
|
double _startTime_s = 0;
|
||||||
|
RTPPacketizationConfig(const RTPPacketizationConfig&) = delete;
|
||||||
|
public:
|
||||||
|
const SSRC ssrc;
|
||||||
|
const std::string cname;
|
||||||
|
const uint8_t payloadType;
|
||||||
|
const uint32_t clockRate;
|
||||||
|
const double & startTime_s = _startTime_s;
|
||||||
|
const uint32_t & startTimestamp = _startTimestamp;
|
||||||
|
|
||||||
|
/// current sequence number
|
||||||
|
uint16_t sequenceNumber;
|
||||||
|
/// current timestamp
|
||||||
|
uint32_t timestamp;
|
||||||
|
|
||||||
|
enum class EpochStart: unsigned long long {
|
||||||
|
T1970 = 2208988800, // number of seconds between 1970 and 1900
|
||||||
|
T1900 = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Creates relation between time and timestamp mapping given start time and start timestamp
|
||||||
|
/// @param startTime_s Start time of the stream
|
||||||
|
/// @param epochStart Type of used epoch
|
||||||
|
/// @param startTimestamp Corresponding timestamp for given start time (current timestamp will be used if value is nullopt)
|
||||||
|
void setStartTime(double startTime_s, EpochStart epochStart, std::optional<uint32_t> startTimestamp = std::nullopt);
|
||||||
|
|
||||||
|
|
||||||
|
/// Construct RTP configuration used in packetization process
|
||||||
|
/// @param ssrc SSRC of source
|
||||||
|
/// @param cname CNAME of source
|
||||||
|
/// @param payloadType Payload type of source
|
||||||
|
/// @param clockRate Clock rate of source used in timestamps
|
||||||
|
/// @param sequenceNumber Initial sequence number of RTP packets (random number is choosed if nullopt)
|
||||||
|
/// @param timestamp Initial timastamp of RTP packets (random number is choosed if nullopt)
|
||||||
|
RTPPacketizationConfig(SSRC ssrc, std::string cname, uint8_t payloadType, uint32_t clockRate, std::optional<uint16_t> sequenceNumber = std::nullopt, std::optional<uint32_t> timestamp = std::nullopt);
|
||||||
|
|
||||||
|
/// Convert timestamp to seconds
|
||||||
|
/// @param timestamp Timestamp
|
||||||
|
/// @param clockRate Clock rate for timestamp calculation
|
||||||
|
static double getSecondsFromTimestamp(uint32_t timestamp, uint32_t clockRate);
|
||||||
|
|
||||||
|
/// Convert timestamp to seconds
|
||||||
|
/// @param timestamp Timestamp
|
||||||
|
double timestampToSeconds(uint32_t timestamp);
|
||||||
|
|
||||||
|
/// Convert seconds to timestamp
|
||||||
|
/// @param seconds Number of seconds
|
||||||
|
/// @param clockRate Clock rate for timestamp calculation
|
||||||
|
static uint32_t getTimestampFromSeconds(double seconds, uint32_t clockRate);
|
||||||
|
|
||||||
|
/// Convert seconds to timestamp
|
||||||
|
/// @param seconds Number of seconds
|
||||||
|
uint32_t secondsToTimestamp(double seconds);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#endif /* RTC_ENABLE_MEDIA */
|
||||||
|
|
||||||
|
#endif /* RTPPacketizationConfig_hpp */
|
52
include/rtc/rtppacketizer.hpp
Normal file
52
include/rtc/rtppacketizer.hpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* libdatachannel streamer example
|
||||||
|
* Copyright (c) 2020 Filip Klembara (in2core)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RTPPacketizer_hpp
|
||||||
|
#define RTPPacketizer_hpp
|
||||||
|
|
||||||
|
#include "rtppacketizationconfig.hpp"
|
||||||
|
#include "message.hpp"
|
||||||
|
|
||||||
|
#if RTC_ENABLE_MEDIA
|
||||||
|
|
||||||
|
namespace rtc {
|
||||||
|
|
||||||
|
/// Class responsizble for rtp packetization
|
||||||
|
class RTPPacketizer {
|
||||||
|
static const auto rtpHeaderSize = 12;
|
||||||
|
public:
|
||||||
|
// rtp configuration
|
||||||
|
const std::shared_ptr<RTPPacketizationConfig> rtpConfig;
|
||||||
|
|
||||||
|
/// Constructs packetizer with given RTP configuration.
|
||||||
|
/// @note RTP configuration is used in packetization process which may change some configuration properties such as sequence number.
|
||||||
|
/// @param rtpConfig RTP configuration
|
||||||
|
RTPPacketizer(std::shared_ptr<RTPPacketizationConfig> rtpConfig);
|
||||||
|
|
||||||
|
/// Creates RTP packet for given payload based on `rtpConfig`.
|
||||||
|
/// @note This function increase sequence number after packetization.
|
||||||
|
/// @param payload RTP payload
|
||||||
|
/// @param setMark Set marker flag in RTP packet if true
|
||||||
|
virtual message_ptr packetize(binary payload, bool setMark);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#endif /* RTC_ENABLE_MEDIA */
|
||||||
|
|
||||||
|
#endif /* RTPPacketizer_hpp */
|
164
src/h264packetizationhandler.cpp
Normal file
164
src/h264packetizationhandler.cpp
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* libdatachannel client example
|
||||||
|
* Copyright (c) 2020 Filip Klembara (in2core)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "h264packetizationhandler.hpp"
|
||||||
|
|
||||||
|
#if RTC_ENABLE_MEDIA
|
||||||
|
|
||||||
|
using namespace rtc;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NUSM_noMatch,
|
||||||
|
NUSM_firstZero,
|
||||||
|
NUSM_secondZero,
|
||||||
|
NUSM_thirdZero,
|
||||||
|
NUSM_shortMatch,
|
||||||
|
NUSM_longMatch
|
||||||
|
} NalUnitStartSequenceMatch;
|
||||||
|
|
||||||
|
NalUnitStartSequenceMatch StartSequenceMatchSucc(NalUnitStartSequenceMatch match, byte _byte, H264PacketizationHandler::Separator separator) {
|
||||||
|
assert(separator != H264PacketizationHandler::Separator::Length);
|
||||||
|
auto byte = (uint8_t) _byte;
|
||||||
|
auto detectShort = separator == H264PacketizationHandler::Separator::ShortStartSequence || separator == H264PacketizationHandler::Separator::StartSequence;
|
||||||
|
auto detectLong = separator == H264PacketizationHandler::Separator::LongStartSequence || separator == H264PacketizationHandler::Separator::StartSequence;
|
||||||
|
switch (match) {
|
||||||
|
case NUSM_noMatch:
|
||||||
|
if (byte == 0x00) {
|
||||||
|
return NUSM_firstZero;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NUSM_firstZero:
|
||||||
|
if (byte == 0x00) {
|
||||||
|
return NUSM_secondZero;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NUSM_secondZero:
|
||||||
|
if (byte == 0x00 && detectLong) {
|
||||||
|
return NUSM_thirdZero;
|
||||||
|
} else if (byte == 0x01 && detectShort) {
|
||||||
|
return NUSM_shortMatch;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NUSM_thirdZero:
|
||||||
|
if (byte == 0x01 && detectLong) {
|
||||||
|
return NUSM_longMatch;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NUSM_shortMatch:
|
||||||
|
return NUSM_shortMatch;
|
||||||
|
case NUSM_longMatch:
|
||||||
|
return NUSM_longMatch;
|
||||||
|
}
|
||||||
|
return NUSM_noMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
message_ptr H264PacketizationHandler::incoming(message_ptr ptr) {
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_ptr<NalUnits> H264PacketizationHandler::splitMessage(rtc::message_ptr message) {
|
||||||
|
auto nalus = make_shared<NalUnits>();
|
||||||
|
if (separator == Separator::Length) {
|
||||||
|
unsigned long long index = 0;
|
||||||
|
while (index < message->size()) {
|
||||||
|
assert(index + 4 < message->size());
|
||||||
|
if (index + 4 >= message->size()) {
|
||||||
|
LOG_WARNING << "Invalid NAL Unit data (incomplete length), ignoring!";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto lengthPtr = (uint32_t *) (message->data() + index);
|
||||||
|
uint32_t length = ntohl(*lengthPtr);
|
||||||
|
auto naluStartIndex = index + 4;
|
||||||
|
auto naluEndIndex = naluStartIndex + length;
|
||||||
|
|
||||||
|
assert(naluEndIndex <= message->size());
|
||||||
|
if (naluEndIndex > message->size()) {
|
||||||
|
LOG_WARNING << "Invalid NAL Unit data (incomplete unit), ignoring!";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nalus->push_back(NalUnit(message->begin() + naluStartIndex, message->begin() + naluEndIndex));
|
||||||
|
index = naluEndIndex;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NalUnitStartSequenceMatch match = NUSM_noMatch;
|
||||||
|
unsigned long long index = 0;
|
||||||
|
while (index < message->size()) {
|
||||||
|
match = StartSequenceMatchSucc(match, (*message)[index++], separator);
|
||||||
|
if (match == NUSM_longMatch || match == NUSM_shortMatch) {
|
||||||
|
match = NUSM_noMatch;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
unsigned long long naluStartIndex = index;
|
||||||
|
|
||||||
|
while (index < message->size()) {
|
||||||
|
match = StartSequenceMatchSucc(match, (*message)[index], separator);
|
||||||
|
if (match == NUSM_longMatch || match == NUSM_shortMatch) {
|
||||||
|
auto sequenceLength = match == NUSM_longMatch ? 4 : 3;
|
||||||
|
unsigned long long naluEndIndex = index - sequenceLength;
|
||||||
|
match = NUSM_noMatch;
|
||||||
|
nalus->push_back(NalUnit(message->begin() + naluStartIndex, message->begin() + naluEndIndex + 1));
|
||||||
|
naluStartIndex = index + 1;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
nalus->push_back(NalUnit(message->begin() + naluStartIndex, message->end()));
|
||||||
|
}
|
||||||
|
return nalus;
|
||||||
|
}
|
||||||
|
|
||||||
|
message_ptr H264PacketizationHandler::outgoing(message_ptr ptr) {
|
||||||
|
if (ptr->type == Message::Binary) {
|
||||||
|
auto nalus = splitMessage(ptr);
|
||||||
|
auto fragments = nalus->generateFragments(maximumFragmentSize);
|
||||||
|
|
||||||
|
auto lastPacket = withStatsRecording<message_ptr>([fragments, this](function<void (message_ptr)> addToReport) {
|
||||||
|
for(unsigned long long index = 0; index < fragments.size() - 1; index++) {
|
||||||
|
auto packet = packetizer->packetize(fragments[index], false);
|
||||||
|
|
||||||
|
addToReport(packet);
|
||||||
|
|
||||||
|
outgoingCallback(std::move(packet));
|
||||||
|
}
|
||||||
|
// packet is last, marker must be set
|
||||||
|
auto lastPacket = packetizer->packetize(fragments[fragments.size() - 1], true);
|
||||||
|
addToReport(lastPacket);
|
||||||
|
return lastPacket;
|
||||||
|
});
|
||||||
|
return lastPacket;
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
H264PacketizationHandler::H264PacketizationHandler(Separator separator,
|
||||||
|
std::shared_ptr<H264RTPPacketizer> packetizer,
|
||||||
|
uint16_t maximumFragmentSize):
|
||||||
|
RtcpHandler(),
|
||||||
|
rtc::RTCPSenderReportable(packetizer->rtpConfig),
|
||||||
|
packetizer(packetizer),
|
||||||
|
maximumFragmentSize(maximumFragmentSize),
|
||||||
|
separator(separator) {
|
||||||
|
|
||||||
|
senderReportOutgoingCallback = [this](message_ptr msg) {
|
||||||
|
outgoingCallback(msg);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* RTC_ENABLE_MEDIA */
|
28
src/h264rtppacketizer.cpp
Normal file
28
src/h264rtppacketizer.cpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* libdatachannel streamer example
|
||||||
|
* Copyright (c) 2020 Filip Klembara (in2core)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "h264rtppacketizer.hpp"
|
||||||
|
|
||||||
|
#if RTC_ENABLE_MEDIA
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace rtc;
|
||||||
|
|
||||||
|
H264RTPPacketizer::H264RTPPacketizer(std::shared_ptr<RTPPacketizationConfig> rtpConfig): RTPPacketizer(rtpConfig) { }
|
||||||
|
|
||||||
|
#endif /* RTC_ENABLE_MEDIA */
|
103
src/nalunit.cpp
Normal file
103
src/nalunit.cpp
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* libdatachannel streamer example
|
||||||
|
* Copyright (c) 2020 Filip Klembara (in2core)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "nalunit.hpp"
|
||||||
|
|
||||||
|
#if RTC_ENABLE_MEDIA
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace rtc;
|
||||||
|
|
||||||
|
NalUnitFragmentA::NalUnitFragmentA(FragmentType type, bool forbiddenBit, uint8_t nri, uint8_t unitType, binary data): NalUnit(data.size() + 2) {
|
||||||
|
setForbiddenBit(forbiddenBit);
|
||||||
|
setNRI(nri);
|
||||||
|
fragmentIndicator()->setUnitType(NalUnitFragmentA::nal_type_fu_A);
|
||||||
|
setFragmentType(type);
|
||||||
|
setUnitType(unitType);
|
||||||
|
copy(data.begin(), data.end(), begin() + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<NalUnitFragmentA> NalUnitFragmentA::fragmentsFrom(NalUnit nalu, uint16_t maximumFragmentSize) {
|
||||||
|
assert(nalu.size() > maximumFragmentSize);
|
||||||
|
if (nalu.size() <= maximumFragmentSize) {
|
||||||
|
// we need to change `maximum_fragment_size` to have at least two fragments
|
||||||
|
maximumFragmentSize = nalu.size() / 2;
|
||||||
|
}
|
||||||
|
auto fragments_count = ceil(double(nalu.size()) / maximumFragmentSize);
|
||||||
|
maximumFragmentSize = ceil(nalu.size() / fragments_count);
|
||||||
|
|
||||||
|
// 2 bytes for FU indicator and FU header
|
||||||
|
maximumFragmentSize -= 2;
|
||||||
|
auto f = nalu.forbiddenBit();
|
||||||
|
uint8_t nri = nalu.nri() & 0x03;
|
||||||
|
uint8_t naluType = nalu.unitType() & 0x1F;
|
||||||
|
auto payload = nalu.payload();
|
||||||
|
vector<NalUnitFragmentA> result{};
|
||||||
|
uint64_t offset = 0;
|
||||||
|
while (offset < payload.size()) {
|
||||||
|
vector<byte> fragmentData;
|
||||||
|
FragmentType fragmentType;
|
||||||
|
if (offset == 0) {
|
||||||
|
fragmentType = FragmentType::Start;
|
||||||
|
} else if (offset + maximumFragmentSize < payload.size()) {
|
||||||
|
fragmentType = FragmentType::Middle;
|
||||||
|
} else {
|
||||||
|
if (offset + maximumFragmentSize > payload.size()) {
|
||||||
|
maximumFragmentSize = payload.size() - offset;
|
||||||
|
}
|
||||||
|
fragmentType = FragmentType::End;
|
||||||
|
}
|
||||||
|
fragmentData = {payload.begin() + offset, payload.begin() + offset + maximumFragmentSize};
|
||||||
|
NalUnitFragmentA fragment{fragmentType, f, nri, naluType, fragmentData};
|
||||||
|
result.push_back(fragment);
|
||||||
|
offset += maximumFragmentSize;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NalUnitFragmentA::setFragmentType(FragmentType type) {
|
||||||
|
fragmentHeader()->setReservedBit6(false);
|
||||||
|
switch (type) {
|
||||||
|
case FragmentType::Start:
|
||||||
|
fragmentHeader()->setStart(true);
|
||||||
|
fragmentHeader()->setEnd(false);
|
||||||
|
break;
|
||||||
|
case FragmentType::End:
|
||||||
|
fragmentHeader()->setStart(false);
|
||||||
|
fragmentHeader()->setEnd(true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fragmentHeader()->setStart(false);
|
||||||
|
fragmentHeader()->setEnd(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<binary> NalUnits::generateFragments(uint16_t maximumFragmentSize) {
|
||||||
|
vector<binary> result{};
|
||||||
|
for (auto nalu: *this) {
|
||||||
|
if (nalu.size() > maximumFragmentSize) {
|
||||||
|
std::vector<NalUnitFragmentA> fragments = NalUnitFragmentA::fragmentsFrom(nalu, maximumFragmentSize);
|
||||||
|
result.insert(result.end(), fragments.begin(), fragments.end());
|
||||||
|
} else {
|
||||||
|
result.push_back(nalu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* RTC_ENABLE_MEDIA */
|
46
src/opuspacketizationhandler.cpp
Normal file
46
src/opuspacketizationhandler.cpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* libdatachannel client example
|
||||||
|
* Copyright (c) 2020 Filip Klembara (in2core)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "opuspacketizationhandler.hpp"
|
||||||
|
|
||||||
|
#if RTC_ENABLE_MEDIA
|
||||||
|
|
||||||
|
using namespace rtc;
|
||||||
|
|
||||||
|
OpusPacketizationHandler::OpusPacketizationHandler(std::shared_ptr<OpusRTPPacketizer> packetizer): RtcpHandler(), RTCPSenderReportable(packetizer->rtpConfig), packetizer(packetizer) {
|
||||||
|
senderReportOutgoingCallback = [this](message_ptr msg) {
|
||||||
|
outgoingCallback(msg);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
message_ptr OpusPacketizationHandler::incoming(message_ptr ptr) {
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
message_ptr OpusPacketizationHandler::outgoing(message_ptr ptr) {
|
||||||
|
if (ptr->type == Message::Binary) {
|
||||||
|
return withStatsRecording<message_ptr>([this, ptr](std::function<void (message_ptr)> addToReport) {
|
||||||
|
auto rtp = packetizer->packetize(*ptr, false);
|
||||||
|
addToReport(rtp);
|
||||||
|
return rtp;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* RTC_ENABLE_MEDIA */
|
33
src/opusrtppacketizer.cpp
Normal file
33
src/opusrtppacketizer.cpp
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* libdatachannel streamer example
|
||||||
|
* Copyright (c) 2020 Filip Klembara (in2core)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "opusrtppacketizer.hpp"
|
||||||
|
|
||||||
|
#if RTC_ENABLE_MEDIA
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace rtc;
|
||||||
|
|
||||||
|
OpusRTPPacketizer::OpusRTPPacketizer(std::shared_ptr<RTPPacketizationConfig> rtpConfig): RTPPacketizer(rtpConfig) { }
|
||||||
|
|
||||||
|
message_ptr OpusRTPPacketizer::packetize(binary payload, bool setMark) {
|
||||||
|
assert(!setMark);
|
||||||
|
return RTPPacketizer::packetize(payload, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* RTC_ENABLE_MEDIA */
|
74
src/rtcpsenderreportable.cpp
Normal file
74
src/rtcpsenderreportable.cpp
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* libdatachannel streamer example
|
||||||
|
* Copyright (c) 2020 Filip Klembara (in2core)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rtcpsenderreportable.hpp"
|
||||||
|
|
||||||
|
#if RTC_ENABLE_MEDIA
|
||||||
|
|
||||||
|
using namespace rtc;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
void RTCPSenderReportable::startRecording() {
|
||||||
|
_previousReportedTimestamp = rtpConfig->timestamp;
|
||||||
|
timeOffset = rtpConfig->startTime_s - rtpConfig->timestampToSeconds(rtpConfig->timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTCPSenderReportable::sendReport(uint32_t timestamp) {
|
||||||
|
auto sr = getSenderReport(timestamp);
|
||||||
|
_previousReportedTimestamp = timestamp;
|
||||||
|
senderReportOutgoingCallback(move(sr));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTCPSenderReportable::addToReport(RTP * rtp, uint32_t rtpSize) {
|
||||||
|
packetCount += 1;
|
||||||
|
assert(!rtp->padding());
|
||||||
|
payloadOctets += rtpSize - rtp->getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
RTCPSenderReportable::RTCPSenderReportable(std::shared_ptr<RTPPacketizationConfig> rtpConfig): rtpConfig(rtpConfig) { }
|
||||||
|
|
||||||
|
uint64_t RTCPSenderReportable::secondsToNTP(double seconds) {
|
||||||
|
return std::round(seconds * double(uint64_t(1) << 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTCPSenderReportable::setNeedsToReport() {
|
||||||
|
needsToReport = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
message_ptr RTCPSenderReportable::getSenderReport(uint32_t timestamp) {
|
||||||
|
auto srSize = RTCP_SR::size(0);
|
||||||
|
auto msg = make_message(srSize + RTCP_SDES::size({uint8_t(rtpConfig->cname.size())}), Message::Type::Control);
|
||||||
|
auto sr = reinterpret_cast<RTCP_SR *>(msg->data());
|
||||||
|
auto timestamp_s = rtpConfig->timestampToSeconds(timestamp);
|
||||||
|
auto currentTime = timeOffset + timestamp_s;
|
||||||
|
sr->setNtpTimestamp(secondsToNTP(currentTime));
|
||||||
|
sr->setRtpTimestamp(timestamp);
|
||||||
|
sr->setPacketCount(packetCount);
|
||||||
|
sr->setOctetCount(payloadOctets);
|
||||||
|
sr->preparePacket(rtpConfig->ssrc, 0);
|
||||||
|
|
||||||
|
auto sdes = reinterpret_cast<RTCP_SDES *>(msg->data() + srSize);
|
||||||
|
auto chunk = sdes->getChunk(0);
|
||||||
|
chunk->setSSRC(rtpConfig->ssrc);
|
||||||
|
chunk->type = 1;
|
||||||
|
chunk->setText(rtpConfig->cname);
|
||||||
|
sdes->preparePacket(1);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* RTC_ENABLE_MEDIA */
|
73
src/rtppacketizationconfig.cpp
Normal file
73
src/rtppacketizationconfig.cpp
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* libdatachannel streamer example
|
||||||
|
* Copyright (c) 2020 Filip Klembara (in2core)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rtppacketizationconfig.hpp"
|
||||||
|
|
||||||
|
#if RTC_ENABLE_MEDIA
|
||||||
|
|
||||||
|
using namespace rtc;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
RTPPacketizationConfig::RTPPacketizationConfig(SSRC ssrc,
|
||||||
|
string cname,
|
||||||
|
uint8_t payloadType,
|
||||||
|
uint32_t clockRate,
|
||||||
|
optional<uint16_t> sequenceNumber,
|
||||||
|
optional<uint32_t> timestamp): ssrc(ssrc), cname(cname), payloadType(payloadType), clockRate(clockRate) {
|
||||||
|
assert(clockRate > 0);
|
||||||
|
srand((unsigned)time(NULL));
|
||||||
|
if (sequenceNumber.has_value()) {
|
||||||
|
this->sequenceNumber = sequenceNumber.value();
|
||||||
|
} else {
|
||||||
|
this->sequenceNumber = rand();
|
||||||
|
}
|
||||||
|
if (timestamp.has_value()) {
|
||||||
|
this->timestamp = timestamp.value();
|
||||||
|
} else {
|
||||||
|
this->timestamp = rand();
|
||||||
|
}
|
||||||
|
this->_startTimestamp = this->timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTPPacketizationConfig::setStartTime(double startTime_s, EpochStart epochStart, std::optional<uint32_t> startTimestamp) {
|
||||||
|
this->_startTime_s = startTime_s + static_cast<unsigned long long>(epochStart);
|
||||||
|
if (startTimestamp.has_value()) {
|
||||||
|
this->_startTimestamp = startTimestamp.value();
|
||||||
|
timestamp = this->startTimestamp;
|
||||||
|
} else {
|
||||||
|
this->_startTimestamp = timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double RTPPacketizationConfig::getSecondsFromTimestamp(uint32_t timestamp, uint32_t clockRate) {
|
||||||
|
return double(timestamp) / double(clockRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
double RTPPacketizationConfig::timestampToSeconds(uint32_t timestamp) {
|
||||||
|
return RTPPacketizationConfig::getSecondsFromTimestamp(timestamp, clockRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t RTPPacketizationConfig::getTimestampFromSeconds(double seconds, uint32_t clockRate) {
|
||||||
|
return uint32_t(seconds * clockRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t RTPPacketizationConfig::secondsToTimestamp(double seconds) {
|
||||||
|
return RTPPacketizationConfig::getTimestampFromSeconds(seconds, clockRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* RTC_ENABLE_MEDIA */
|
44
src/rtppacketizer.cpp
Normal file
44
src/rtppacketizer.cpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* libdatachannel streamer example
|
||||||
|
* Copyright (c) 2020 Filip Klembara (in2core)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rtppacketizer.hpp"
|
||||||
|
|
||||||
|
#if RTC_ENABLE_MEDIA
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace rtc;
|
||||||
|
|
||||||
|
RTPPacketizer::RTPPacketizer(shared_ptr<RTPPacketizationConfig> rtpConfig): rtpConfig(rtpConfig) { }
|
||||||
|
|
||||||
|
message_ptr RTPPacketizer::packetize(binary payload, bool setMark) {
|
||||||
|
auto msg = make_message(rtpHeaderSize + payload.size());
|
||||||
|
auto * rtp = (RTP *)msg->data();
|
||||||
|
rtp->setPayloadType(rtpConfig->payloadType);
|
||||||
|
// increase sequence number
|
||||||
|
rtp->setSeqNumber(rtpConfig->sequenceNumber++);
|
||||||
|
rtp->setTimestamp(rtpConfig->timestamp);
|
||||||
|
rtp->setSsrc(rtpConfig->ssrc);
|
||||||
|
if (setMark) {
|
||||||
|
rtp->setMarker(true);
|
||||||
|
}
|
||||||
|
rtp->preparePacket();
|
||||||
|
copy(payload.begin(), payload.end(), msg->begin() + rtpHeaderSize);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* RTC_ENABLE_MEDIA */
|
Reference in New Issue
Block a user