Quellcode durchsuchen

Adding basic current video orientation support

Asgeir Bjarni Ingvarsson vor 3 Jahren
Ursprung
Commit
4b6aa3f1b7

+ 19 - 0
include/rtc/description.hpp

@@ -95,6 +95,24 @@ public:
 		std::vector<string>::iterator endAttributes();
 		std::vector<string>::iterator removeAttribute(std::vector<string>::iterator iterator);
 
+		struct ExtMap {
+			ExtMap(string_view mline);
+			ExtMap() {}
+
+			int id;
+			string uri;
+			string attributes;
+
+			static int parseId(string_view view);
+			void setMLine(string_view view);
+		};
+
+		void addExtMap(const ExtMap &map);
+
+		std::map<int, ExtMap>::iterator beginExtMaps();
+		std::map<int, ExtMap>::iterator endExtMaps();
+		std::map<int, ExtMap>::iterator removeExtMap(std::map<int, ExtMap>::iterator iterator);
+
 	protected:
 		Entry(const string &mline, string mid, Direction dir = Direction::Unknown);
 		virtual string generateSdpLines(string_view eol) const;
@@ -106,6 +124,7 @@ public:
 		string mDescription;
 		string mMid;
 		Direction mDirection;
+		std::map<int, ExtMap> mExtMap;
 	};
 
 	struct RTC_CPP_EXPORT Application : public Entry {

+ 22 - 0
include/rtc/rtp.hpp

@@ -31,6 +31,24 @@ typedef uint32_t SSRC;
 
 #pragma pack(push, 1)
 
+struct RTC_CPP_EXPORT RTP_ExtensionHeader {
+	uint16_t _profileSpecificId;
+	uint16_t _headerLength;
+
+	[[nodiscard]] uint16_t profileSpecificId() const;
+	[[nodiscard]] uint16_t headerLength() const;
+
+	[[nodiscard]] size_t getSize() const;
+	[[nodiscard]] const char *getBody() const;
+	[[nodiscard]] char *getBody();
+
+	void setProfileSpecificId(uint16_t profileSpecificId);
+	void setHeaderLength(uint16_t headerLength);
+
+	void clearBody();
+	void writeCurrentVideoOrientation(size_t offset, uint8_t id, uint8_t value);
+};
+
 struct RTC_CPP_EXPORT RTP {
 	uint8_t _first;
 	uint8_t _payloadType;
@@ -50,6 +68,9 @@ struct RTC_CPP_EXPORT RTP {
 	[[nodiscard]] uint32_t ssrc() const;
 
 	[[nodiscard]] size_t getSize() const;
+	[[nodiscard]] size_t getExtSize() const;
+	[[nodiscard]] const RTP_ExtensionHeader *getExt() const;
+	[[nodiscard]] RTP_ExtensionHeader *getExt();
 	[[nodiscard]] const char *getBody() const;
 	[[nodiscard]] char *getBody();
 
@@ -61,6 +82,7 @@ struct RTC_CPP_EXPORT RTP {
 	void setSsrc(uint32_t in_ssrc);
 	void setMarker(bool marker);
 	void setTimestamp(uint32_t i);
+	void setExtension(bool extension);
 };
 
 struct RTC_CPP_EXPORT RTCP_ReportBlock {

+ 22 - 1
include/rtc/rtppacketizationconfig.hpp

@@ -38,11 +38,31 @@ public:
 	const uint32_t clockRate;
 	const double &startTime_s = _startTime_s;
 	const uint32_t &startTimestamp = _startTimestamp;
+	const uint8_t videoOrientationId;
 
 	/// current sequence number
 	uint16_t sequenceNumber;
 	/// current timestamp
 	uint32_t timestamp;
+	/// Current video orientation
+	///
+	/// Bit#       7  6  5  4  3  2  1  0
+	/// Definition 0  0  0  0  C  F  R1 R0
+	///
+	/// C
+	///   0 - Front-facing camera (use this if unsure)
+	///   1 - Back-facing camera
+	///
+	/// F
+	///   0 - No Flip
+	///   1 - Horizontal flip
+	///
+	/// R1 R0 - CW rotation that receiver must apply
+	///   0 - 0 degrees
+	///   1 - 90 degrees
+	///   2 - 180 degrees
+	///   3 - 270 degrees
+	uint8_t videoOrientation = 0;
 
 	enum class EpochStart : unsigned long long {
 		T1970 = 2208988800, // number of seconds between 1970 and 1900
@@ -67,7 +87,8 @@ public:
 	/// @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,
 	                       optional<uint16_t> sequenceNumber = std::nullopt,
-	                       optional<uint32_t> timestamp = std::nullopt);
+	                       optional<uint32_t> timestamp = std::nullopt,
+	                       uint8_t videoOrientationId = 0);
 
 	/// Convert timestamp to seconds
 	/// @param timestamp Timestamp

+ 1 - 0
include/rtc/rtppacketizer.hpp

@@ -29,6 +29,7 @@ namespace rtc {
 /// Class responsible for RTP packetization
 class RTC_CPP_EXPORT RtpPacketizer {
 	static const auto rtpHeaderSize = 12;
+	static const auto rtpExtHeaderCvoSize = 8;
 
 public:
 	// RTP configuration

+ 60 - 1
src/description.cpp

@@ -500,6 +500,15 @@ string Description::Entry::generateSdpLines(string_view eol) const {
 	sdp << "a=bundle-only" << eol;
 	sdp << "a=mid:" << mMid << eol;
 
+	for (auto it = mExtMap.begin(); it != mExtMap.end(); ++it) {
+		auto &map = it->second;
+
+		sdp << "a=extmap:" << map.id << ' ' << map.uri;
+		if (!map.attributes.empty())
+			sdp << ' ' << map.attributes;
+		sdp << eol;
+	}
+
 	switch (mDirection) {
 	case Direction::SendOnly:
 		sdp << "a=sendonly" << eol;
@@ -533,7 +542,15 @@ void Description::Entry::parseSdpLine(string_view line) {
 
 		if (key == "mid")
 			mMid = value;
-		else if (attr == "sendonly")
+		else if (key == "extmap") {
+			auto id = Description::Media::ExtMap::parseId(value);
+			auto it = mExtMap.find(id);
+			if (it == mExtMap.end()) {
+				it = mExtMap.insert(std::make_pair(id, Description::Media::ExtMap(value))).first;
+			} else {
+				it->second.setMLine(value);
+			}
+		} else if (attr == "sendonly")
 			mDirection = Direction::SendOnly;
 		else if (attr == "recvonly")
 			mDirection = Direction::RecvOnly;
@@ -557,6 +574,48 @@ Description::Entry::removeAttribute(std::vector<string>::iterator it) {
 	return mAttributes.erase(it);
 }
 
+void Description::Entry::addExtMap(const Description::Entry::ExtMap &map) {
+	mExtMap.emplace(map.id, map);
+}
+
+std::map<int, Description::Entry::ExtMap>::iterator Description::Entry::beginExtMaps() {
+	return mExtMap.begin();
+}
+
+std::map<int, Description::Entry::ExtMap>::iterator Description::Entry::endExtMaps() {
+	return mExtMap.end();
+}
+
+std::map<int, Description::Entry::ExtMap>::iterator
+Description::Entry::removeExtMap(std::map<int, Description::Entry::ExtMap>::iterator iterator) {
+	return mExtMap.erase(iterator);
+}
+
+Description::Entry::ExtMap::ExtMap(string_view mline) { setMLine(mline); }
+
+int Description::Entry::ExtMap::parseId(string_view view) {
+	size_t p = view.find(' ');
+
+	return to_integer<int>(view.substr(0, p));
+}
+
+void Description::Entry::ExtMap::setMLine(string_view mline) {
+	size_t p = mline.find(' ');
+	if (p == string::npos)
+		throw std::invalid_argument("Invalid m-line");
+
+	this->id = to_integer<int>(mline.substr(0, p));
+	string_view line = mline.substr(p + 1);
+
+	size_t spl = line.find(' ');
+	if (spl == string::npos)
+		this->uri = line;
+	else {
+		this->uri = line.substr(0, spl);
+		this->attributes = line.substr(spl + 1);
+	}
+}
+
 void Description::Media::addSSRC(uint32_t ssrc, optional<string> name, optional<string> msid,
                                  optional<string> trackID) {
 	if (name) {

+ 56 - 2
src/rtp.cpp

@@ -56,11 +56,35 @@ size_t RTP::getSize() const {
 	       sizeof(SSRC) * csrcCount();
 }
 
+size_t RTP::getExtSize() const {
+	auto header = reinterpret_cast<const RTP_ExtensionHeader *>(getExt());
+	if (header) {
+		return header->getSize() + sizeof(RTP_ExtensionHeader);
+	}
+	return 0;
+}
+
+const RTP_ExtensionHeader *RTP::getExt() const {
+	if (extension()) {
+		auto header = reinterpret_cast<const char *>(&_csrc) + sizeof(SSRC) * csrcCount();
+		return reinterpret_cast<const RTP_ExtensionHeader *>(header);
+	}
+	return nullptr;
+}
+
+RTP_ExtensionHeader *RTP::getExt() {
+	if (extension()) {
+		auto header = reinterpret_cast<char *>(&_csrc) + sizeof(SSRC) * csrcCount();
+		return reinterpret_cast<RTP_ExtensionHeader *>(header);
+	}
+	return nullptr;
+}
+
 const char *RTP::getBody() const {
-	return reinterpret_cast<const char *>(&_csrc) + sizeof(SSRC) * csrcCount();
+	return reinterpret_cast<const char *>(&_csrc) + sizeof(SSRC) * csrcCount() + getExtSize();
 }
 
-char *RTP::getBody() { return reinterpret_cast<char *>(&_csrc) + sizeof(SSRC) * csrcCount(); }
+char *RTP::getBody() { return reinterpret_cast<char *>(&_csrc) + sizeof(SSRC) * csrcCount() + getExtSize(); }
 
 void RTP::preparePacket() { _first |= (1 << 7); }
 
@@ -76,6 +100,8 @@ void RTP::setMarker(bool marker) { _payloadType = (_payloadType & 0x7F) | (marke
 
 void RTP::setTimestamp(uint32_t i) { _timestamp = htonl(i); }
 
+void RTP::setExtension(bool extension) { _first = (_first & ~0x10) | ((extension & 1) << 4); }
+
 void RTP::log() const {
 	PLOG_VERBOSE << "RTP V: " << (int)version() << " P: " << (padding() ? "P" : " ")
 	             << " X: " << (extension() ? "X" : " ") << " CC: " << (int)csrcCount()
@@ -83,6 +109,34 @@ void RTP::log() const {
 	             << " SEQNO: " << seqNumber() << " TS: " << timestamp();
 }
 
+uint16_t RTP_ExtensionHeader::profileSpecificId() const { return ntohs(_profileSpecificId); }
+
+uint16_t RTP_ExtensionHeader::headerLength() const { return ntohs(_headerLength); }
+
+size_t RTP_ExtensionHeader::getSize() const { return headerLength() * 4; }
+
+const char *RTP_ExtensionHeader::getBody() const { return reinterpret_cast<const char *>((&_headerLength) + 1); }
+
+char *RTP_ExtensionHeader::getBody() { return reinterpret_cast<char *>((&_headerLength) + 1); }
+
+void RTP_ExtensionHeader::setProfileSpecificId(uint16_t profileSpecificId) {
+	_profileSpecificId = htons(profileSpecificId);
+}
+
+void RTP_ExtensionHeader::setHeaderLength(uint16_t headerLength) {
+	_headerLength = htons(headerLength);
+}
+
+void RTP_ExtensionHeader::clearBody() { std::memset(getBody(), 0, getSize()); }
+
+void RTP_ExtensionHeader::writeCurrentVideoOrientation(size_t offset, uint8_t id, uint8_t value)
+{
+	if ((id == 0) || (id > 14) || ((offset + 2) > getSize())) return;
+	auto buf = getBody() + offset;
+	buf[0] = id << 4;
+	buf[1] = value;
+}
+
 SSRC RTCP_ReportBlock::getSSRC() const { return ntohl(_ssrc); }
 
 void RTCP_ReportBlock::preparePacket(SSRC in_ssrc, [[maybe_unused]] unsigned int packetsLost,

+ 3 - 2
src/rtppacketizationconfig.cpp

@@ -27,8 +27,9 @@ namespace rtc {
 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) {
+                                               optional<uint32_t> timestamp,
+                                               uint8_t videoOrientationId)
+    : ssrc(ssrc), cname(cname), payloadType(payloadType), clockRate(clockRate), videoOrientationId(videoOrientationId) {
 	assert(clockRate > 0);
 	srand((unsigned)time(NULL));
 	if (sequenceNumber.has_value()) {

+ 21 - 2
src/rtppacketizer.cpp

@@ -27,7 +27,16 @@ namespace rtc {
 RtpPacketizer::RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig) : rtpConfig(rtpConfig) {}
 
 binary_ptr RtpPacketizer::packetize(shared_ptr<binary> payload, bool setMark) {
-	auto msg = std::make_shared<binary>(rtpHeaderSize + payload->size());
+	int rtpExtHeaderSize = 0;
+	const bool setVideoRotation =
+		(rtpConfig->videoOrientationId != 0) &&
+		(rtpConfig->videoOrientationId < 15) &&  // needs fixing if longer extension headers are supported
+		setMark &&
+		(rtpConfig->videoOrientation != 0);
+	if (setVideoRotation) {
+		rtpExtHeaderSize = rtpExtHeaderCvoSize;
+	}
+	auto msg = std::make_shared<binary>(rtpHeaderSize + rtpExtHeaderSize + payload->size());
 	auto *rtp = (RTP *)msg->data();
 	rtp->setPayloadType(rtpConfig->payloadType);
 	// increase sequence number
@@ -37,8 +46,18 @@ binary_ptr RtpPacketizer::packetize(shared_ptr<binary> payload, bool setMark) {
 	if (setMark) {
 		rtp->setMarker(true);
 	}
+	if (rtpExtHeaderSize) {
+		rtp->setExtension(true);
+
+		auto extHeader = rtp->getExt();
+		extHeader->setProfileSpecificId(0xbede);
+		extHeader->setHeaderLength(1);
+		extHeader->clearBody();
+		extHeader->writeCurrentVideoOrientation(0,
+			rtpConfig->videoOrientationId, rtpConfig->videoOrientation);
+	}
 	rtp->preparePacket();
-	std::memcpy(msg->data() + rtpHeaderSize, payload->data(), payload->size());
+	std::memcpy(msg->data() + rtpHeaderSize + rtpExtHeaderSize, payload->data(), payload->size());
 	return msg;
 }