Browse Source

Added Track C API

Paul-Louis Ageneau 4 years ago
parent
commit
a43dbdd1b0
4 changed files with 155 additions and 53 deletions
  1. 3 1
      include/rtc/description.hpp
  2. 12 8
      include/rtc/rtc.h
  3. 2 0
      src/description.cpp
  4. 138 44
      src/rtc.cpp

+ 3 - 1
include/rtc/description.hpp

@@ -71,9 +71,11 @@ public:
 		Direction direction() const { return mDirection; }
 		Direction direction() const { return mDirection; }
 		void setDirection(Direction dir);
 		void setDirection(Direction dir);
 
 
-		virtual void parseSdpLine(string_view line);
+		operator string() const;
 		string generateSdp(string_view eol) const;
 		string generateSdp(string_view eol) const;
 
 
+		virtual void parseSdpLine(string_view line);
+
 	protected:
 	protected:
 		Entry(const string &mline, string mid, Direction dir = Direction::Unknown);
 		Entry(const string &mline, string mid, Direction dir = Direction::Unknown);
 		virtual string generateSdpLines(string_view eol) const;
 		virtual string generateSdpLines(string_view eol) const;

+ 12 - 8
include/rtc/rtc.h

@@ -29,10 +29,6 @@ extern "C" {
 #define RTC_EXPORT
 #define RTC_EXPORT
 #endif
 #endif
 
 
-#ifndef RTC_ENABLE_MEDIA
-#define RTC_ENABLE_MEDIA 1
-#endif
-
 #ifndef RTC_ENABLE_WEBSOCKET
 #ifndef RTC_ENABLE_WEBSOCKET
 #define RTC_ENABLE_WEBSOCKET 1
 #define RTC_ENABLE_WEBSOCKET 1
 #endif
 #endif
@@ -86,11 +82,12 @@ typedef struct {
 } rtcReliability;
 } rtcReliability;
 
 
 typedef void (*rtcLogCallbackFunc)(rtcLogLevel level, const char *message);
 typedef void (*rtcLogCallbackFunc)(rtcLogLevel level, const char *message);
-typedef void (*rtcDataChannelCallbackFunc)(int dc, void *ptr);
 typedef void (*rtcDescriptionCallbackFunc)(const char *sdp, const char *type, void *ptr);
 typedef void (*rtcDescriptionCallbackFunc)(const char *sdp, const char *type, void *ptr);
 typedef void (*rtcCandidateCallbackFunc)(const char *cand, const char *mid, void *ptr);
 typedef void (*rtcCandidateCallbackFunc)(const char *cand, const char *mid, void *ptr);
 typedef void (*rtcStateChangeCallbackFunc)(rtcState state, void *ptr);
 typedef void (*rtcStateChangeCallbackFunc)(rtcState state, void *ptr);
 typedef void (*rtcGatheringStateCallbackFunc)(rtcGatheringState state, void *ptr);
 typedef void (*rtcGatheringStateCallbackFunc)(rtcGatheringState state, void *ptr);
+typedef void (*rtcDataChannelCallbackFunc)(int dc, void *ptr);
+typedef void (*rtcTrackCallbackFunc)(int tr, void *ptr);
 typedef void (*rtcOpenCallbackFunc)(void *ptr);
 typedef void (*rtcOpenCallbackFunc)(void *ptr);
 typedef void (*rtcClosedCallbackFunc)(void *ptr);
 typedef void (*rtcClosedCallbackFunc)(void *ptr);
 typedef void (*rtcErrorCallbackFunc)(const char *error, void *ptr);
 typedef void (*rtcErrorCallbackFunc)(const char *error, void *ptr);
@@ -108,7 +105,6 @@ RTC_EXPORT void rtcSetUserPointer(int id, void *ptr);
 RTC_EXPORT int rtcCreatePeerConnection(const rtcConfiguration *config); // returns pc id
 RTC_EXPORT int rtcCreatePeerConnection(const rtcConfiguration *config); // returns pc id
 RTC_EXPORT int rtcDeletePeerConnection(int pc);
 RTC_EXPORT int rtcDeletePeerConnection(int pc);
 
 
-RTC_EXPORT int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb);
 RTC_EXPORT int rtcSetLocalDescriptionCallback(int pc, rtcDescriptionCallbackFunc cb);
 RTC_EXPORT int rtcSetLocalDescriptionCallback(int pc, rtcDescriptionCallbackFunc cb);
 RTC_EXPORT int rtcSetLocalCandidateCallback(int pc, rtcCandidateCallbackFunc cb);
 RTC_EXPORT int rtcSetLocalCandidateCallback(int pc, rtcCandidateCallbackFunc cb);
 RTC_EXPORT int rtcSetStateChangeCallback(int pc, rtcStateChangeCallbackFunc cb);
 RTC_EXPORT int rtcSetStateChangeCallback(int pc, rtcStateChangeCallbackFunc cb);
@@ -121,6 +117,7 @@ RTC_EXPORT int rtcGetLocalAddress(int pc, char *buffer, int size);
 RTC_EXPORT int rtcGetRemoteAddress(int pc, char *buffer, int size);
 RTC_EXPORT int rtcGetRemoteAddress(int pc, char *buffer, int size);
 
 
 // DataChannel
 // DataChannel
+RTC_EXPORT int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb);
 RTC_EXPORT int rtcCreateDataChannel(int pc, const char *label); // returns dc id
 RTC_EXPORT int rtcCreateDataChannel(int pc, const char *label); // returns dc id
 RTC_EXPORT int rtcCreateDataChannelExt(int pc, const char *label, const char *protocol,
 RTC_EXPORT int rtcCreateDataChannelExt(int pc, const char *label, const char *protocol,
                                        const rtcReliability *reliability); // returns dc id
                                        const rtcReliability *reliability); // returns dc id
@@ -130,6 +127,13 @@ RTC_EXPORT int rtcGetDataChannelLabel(int dc, char *buffer, int size);
 RTC_EXPORT int rtcGetDataChannelProtocol(int dc, char *buffer, int size);
 RTC_EXPORT int rtcGetDataChannelProtocol(int dc, char *buffer, int size);
 RTC_EXPORT int rtcGetDataChannelReliability(int dc, rtcReliability *reliability);
 RTC_EXPORT int rtcGetDataChannelReliability(int dc, rtcReliability *reliability);
 
 
+// Track
+RTC_EXPORT int rtcSetTrackCallback(int pc, rtcTrackCallbackFunc cb);
+RTC_EXPORT int rtcCreateTrack(int pc, const char *mediaDescriptionSdp); // returns tr id
+RTC_EXPORT int rtcDeleteTrack(int tr);
+
+RTC_EXPORT int rtcGetTrackDescription(int tr, char *buffer, int size);
+
 // WebSocket
 // WebSocket
 #if RTC_ENABLE_WEBSOCKET
 #if RTC_ENABLE_WEBSOCKET
 typedef struct {
 typedef struct {
@@ -141,7 +145,7 @@ RTC_EXPORT int rtcCreateWebSocketEx(const char *url, const rtcWsConfiguration *c
 RTC_EXPORT int rtcDeleteWebsocket(int ws);
 RTC_EXPORT int rtcDeleteWebsocket(int ws);
 #endif
 #endif
 
 
-// DataChannel and WebSocket common API
+// DataChannel, Track, and WebSocket common API
 RTC_EXPORT int rtcSetOpenCallback(int id, rtcOpenCallbackFunc cb);
 RTC_EXPORT int rtcSetOpenCallback(int id, rtcOpenCallbackFunc cb);
 RTC_EXPORT int rtcSetClosedCallback(int id, rtcClosedCallbackFunc cb);
 RTC_EXPORT int rtcSetClosedCallback(int id, rtcClosedCallbackFunc cb);
 RTC_EXPORT int rtcSetErrorCallback(int id, rtcErrorCallbackFunc cb);
 RTC_EXPORT int rtcSetErrorCallback(int id, rtcErrorCallbackFunc cb);
@@ -152,7 +156,7 @@ RTC_EXPORT int rtcGetBufferedAmount(int id); // total size buffered to send
 RTC_EXPORT int rtcSetBufferedAmountLowThreshold(int id, int amount);
 RTC_EXPORT int rtcSetBufferedAmountLowThreshold(int id, int amount);
 RTC_EXPORT int rtcSetBufferedAmountLowCallback(int id, rtcBufferedAmountLowCallbackFunc cb);
 RTC_EXPORT int rtcSetBufferedAmountLowCallback(int id, rtcBufferedAmountLowCallbackFunc cb);
 
 
-// DataChannel and WebSocket common extended API
+// DataChannel, Track, and WebSocket common extended API
 RTC_EXPORT int rtcGetAvailableAmount(int id); // total size available to receive
 RTC_EXPORT int rtcGetAvailableAmount(int id); // total size available to receive
 RTC_EXPORT int rtcSetAvailableCallback(int id, rtcAvailableCallbackFunc cb);
 RTC_EXPORT int rtcSetAvailableCallback(int id, rtcAvailableCallbackFunc cb);
 RTC_EXPORT int rtcReceiveMessage(int id, char *buffer, int *size);
 RTC_EXPORT int rtcReceiveMessage(int id, char *buffer, int *size);

+ 2 - 0
src/description.cpp

@@ -378,6 +378,8 @@ Description::Entry::Entry(const string &mline, string mid, Direction dir)
 
 
 void Description::Entry::setDirection(Direction dir) { mDirection = dir; }
 void Description::Entry::setDirection(Direction dir) { mDirection = dir; }
 
 
+Description::Entry::operator string() const { return generateSdp("\r\n"); }
+
 string Description::Entry::generateSdp(string_view eol) const {
 string Description::Entry::generateSdp(string_view eol) const {
 	std::ostringstream sdp;
 	std::ostringstream sdp;
 	// Port 9 is the discard protocol
 	// Port 9 is the discard protocol

+ 138 - 44
src/rtc.cpp

@@ -50,6 +50,7 @@ namespace {
 
 
 std::unordered_map<int, shared_ptr<PeerConnection>> peerConnectionMap;
 std::unordered_map<int, shared_ptr<PeerConnection>> peerConnectionMap;
 std::unordered_map<int, shared_ptr<DataChannel>> dataChannelMap;
 std::unordered_map<int, shared_ptr<DataChannel>> dataChannelMap;
+std::unordered_map<int, shared_ptr<Track>> trackMap;
 #if RTC_ENABLE_WEBSOCKET
 #if RTC_ENABLE_WEBSOCKET
 std::unordered_map<int, shared_ptr<WebSocket>> webSocketMap;
 std::unordered_map<int, shared_ptr<WebSocket>> webSocketMap;
 #endif
 #endif
@@ -84,6 +85,14 @@ shared_ptr<DataChannel> getDataChannel(int id) {
 		throw std::invalid_argument("DataChannel ID does not exist");
 		throw std::invalid_argument("DataChannel ID does not exist");
 }
 }
 
 
+shared_ptr<Track> getTrack(int id) {
+	std::lock_guard lock(mutex);
+	if (auto it = trackMap.find(id); it != trackMap.end())
+		return it->second;
+	else
+		throw std::invalid_argument("Track ID does not exist");
+}
+
 int emplacePeerConnection(shared_ptr<PeerConnection> ptr) {
 int emplacePeerConnection(shared_ptr<PeerConnection> ptr) {
 	std::lock_guard lock(mutex);
 	std::lock_guard lock(mutex);
 	int pc = ++lastId;
 	int pc = ++lastId;
@@ -100,6 +109,14 @@ int emplaceDataChannel(shared_ptr<DataChannel> ptr) {
 	return dc;
 	return dc;
 }
 }
 
 
+int emplaceTrack(shared_ptr<Track> ptr) {
+	std::lock_guard lock(mutex);
+	int tr = ++lastId;
+	trackMap.emplace(std::make_pair(tr, ptr));
+	userPointerMap.emplace(std::make_pair(tr, nullptr));
+	return tr;
+}
+
 void erasePeerConnection(int pc) {
 void erasePeerConnection(int pc) {
 	std::lock_guard lock(mutex);
 	std::lock_guard lock(mutex);
 	if (peerConnectionMap.erase(pc) == 0)
 	if (peerConnectionMap.erase(pc) == 0)
@@ -114,6 +131,13 @@ void eraseDataChannel(int dc) {
 	userPointerMap.erase(dc);
 	userPointerMap.erase(dc);
 }
 }
 
 
+void eraseTrack(int tr) {
+	std::lock_guard lock(mutex);
+	if (trackMap.erase(tr) == 0)
+		throw std::invalid_argument("Track ID does not exist");
+	userPointerMap.erase(tr);
+}
+
 #if RTC_ENABLE_WEBSOCKET
 #if RTC_ENABLE_WEBSOCKET
 shared_ptr<WebSocket> getWebSocket(int id) {
 shared_ptr<WebSocket> getWebSocket(int id) {
 	std::lock_guard lock(mutex);
 	std::lock_guard lock(mutex);
@@ -143,11 +167,13 @@ shared_ptr<Channel> getChannel(int id) {
 	std::lock_guard lock(mutex);
 	std::lock_guard lock(mutex);
 	if (auto it = dataChannelMap.find(id); it != dataChannelMap.end())
 	if (auto it = dataChannelMap.find(id); it != dataChannelMap.end())
 		return it->second;
 		return it->second;
+	if (auto it = trackMap.find(id); it != trackMap.end())
+		return it->second;
 #if RTC_ENABLE_WEBSOCKET
 #if RTC_ENABLE_WEBSOCKET
 	if (auto it = webSocketMap.find(id); it != webSocketMap.end())
 	if (auto it = webSocketMap.find(id); it != webSocketMap.end())
 		return it->second;
 		return it->second;
 #endif
 #endif
-	throw std::invalid_argument("DataChannel or WebSocket ID does not exist");
+	throw std::invalid_argument("DataChannel, Track, or WebSocket ID does not exist");
 }
 }
 
 
 template <typename F> int wrap(F func) {
 template <typename F> int wrap(F func) {
@@ -287,6 +313,51 @@ int rtcDeleteDataChannel(int dc) {
 	});
 	});
 }
 }
 
 
+int rtcCreateTrack(int pc, const char *mediaDescriptionSdp) {
+	if (!mediaDescriptionSdp)
+		throw std::invalid_argument("Unexpected null pointer for track media description");
+
+	auto peerConnection = getPeerConnection(pc);
+	Description::Media media{string(mediaDescriptionSdp)};
+	int tr = emplaceTrack(peerConnection->createTrack(std::move(media)));
+	if (auto ptr = getUserPointer(pc))
+		rtcSetUserPointer(tr, *ptr);
+	return tr;
+}
+
+int rtcDeleteTrack(int tr) {
+	return WRAP({
+		auto track = getTrack(tr);
+		track->onOpen(nullptr);
+		track->onClosed(nullptr);
+		track->onError(nullptr);
+		track->onMessage(nullptr);
+		track->onBufferedAmountLow(nullptr);
+		track->onAvailable(nullptr);
+
+		eraseTrack(tr);
+	});
+}
+
+int rtcGetTrackDescription(int tr, char *buffer, int size) {
+	return WRAP({
+		auto track = getTrack(tr);
+
+		if (size <= 0)
+			return 0;
+
+		if (!buffer)
+			throw std::invalid_argument("Unexpected null pointer for buffer");
+
+		string description(track->description());
+		const char *data = description.data();
+		size = std::min(size - 1, int(description.size()));
+		std::copy(data, data + size, buffer);
+		buffer[size] = '\0';
+		return int(size + 1);
+	});
+}
+
 #if RTC_ENABLE_WEBSOCKET
 #if RTC_ENABLE_WEBSOCKET
 int rtcCreateWebSocket(const char *url) {
 int rtcCreateWebSocket(const char *url) {
 	return WRAP({
 	return WRAP({
@@ -321,22 +392,6 @@ int rtcDeleteWebsocket(int ws) {
 }
 }
 #endif
 #endif
 
 
-int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb) {
-	return WRAP({
-		auto peerConnection = getPeerConnection(pc);
-		if (cb)
-			peerConnection->onDataChannel([pc, cb](std::shared_ptr<DataChannel> dataChannel) {
-				int dc = emplaceDataChannel(dataChannel);
-				if (auto ptr = getUserPointer(pc)) {
-					rtcSetUserPointer(dc, *ptr);
-					cb(dc, *ptr);
-				}
-			});
-		else
-			peerConnection->onDataChannel(nullptr);
-	});
-}
-
 int rtcSetLocalDescriptionCallback(int pc, rtcDescriptionCallbackFunc cb) {
 int rtcSetLocalDescriptionCallback(int pc, rtcDescriptionCallbackFunc cb) {
 	return WRAP({
 	return WRAP({
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
@@ -389,12 +444,44 @@ int rtcSetGatheringStateChangeCallback(int pc, rtcGatheringStateCallbackFunc cb)
 	});
 	});
 }
 }
 
 
+int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb) {
+	return WRAP({
+		auto peerConnection = getPeerConnection(pc);
+		if (cb)
+			peerConnection->onDataChannel([pc, cb](std::shared_ptr<DataChannel> dataChannel) {
+				int dc = emplaceDataChannel(dataChannel);
+				if (auto ptr = getUserPointer(pc)) {
+					rtcSetUserPointer(dc, *ptr);
+					cb(dc, *ptr);
+				}
+			});
+		else
+			peerConnection->onDataChannel(nullptr);
+	});
+}
+
+int rtcSetTrackCallback(int pc, rtcTrackCallbackFunc cb) {
+	return WRAP({
+		auto peerConnection = getPeerConnection(pc);
+		if (cb)
+			peerConnection->onTrack([pc, cb](std::shared_ptr<Track> track) {
+				int tr = emplaceTrack(track);
+				if (auto ptr = getUserPointer(pc)) {
+					rtcSetUserPointer(tr, *ptr);
+					cb(tr, *ptr);
+				}
+			});
+		else
+			peerConnection->onTrack(nullptr);
+	});
+}
+
 int rtcSetRemoteDescription(int pc, const char *sdp, const char *type) {
 int rtcSetRemoteDescription(int pc, const char *sdp, const char *type) {
 	return WRAP({
 	return WRAP({
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 
 
 		if (!sdp)
 		if (!sdp)
-			throw std::invalid_argument("Unexpected null pointer");
+			throw std::invalid_argument("Unexpected null pointer for remote description");
 
 
 		peerConnection->setRemoteDescription({string(sdp), type ? string(type) : ""});
 		peerConnection->setRemoteDescription({string(sdp), type ? string(type) : ""});
 	});
 	});
@@ -405,7 +492,7 @@ int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid) {
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 
 
 		if (!cand)
 		if (!cand)
-			throw std::invalid_argument("Unexpected null pointer");
+			throw std::invalid_argument("Unexpected null pointer for remote candidate");
 
 
 		peerConnection->addRemoteCandidate({string(cand), mid ? string(mid) : ""});
 		peerConnection->addRemoteCandidate({string(cand), mid ? string(mid) : ""});
 	});
 	});
@@ -415,12 +502,12 @@ int rtcGetLocalAddress(int pc, char *buffer, int size) {
 	return WRAP({
 	return WRAP({
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 
 
-		if (!buffer)
-			throw std::invalid_argument("Unexpected null pointer");
-
 		if (size <= 0)
 		if (size <= 0)
 			return 0;
 			return 0;
 
 
+		if (!buffer)
+			throw std::invalid_argument("Unexpected null pointer for buffer");
+
 		if (auto addr = peerConnection->localAddress()) {
 		if (auto addr = peerConnection->localAddress()) {
 			const char *data = addr->data();
 			const char *data = addr->data();
 			size = std::min(size - 1, int(addr->size()));
 			size = std::min(size - 1, int(addr->size()));
@@ -435,12 +522,12 @@ int rtcGetRemoteAddress(int pc, char *buffer, int size) {
 	return WRAP({
 	return WRAP({
 		auto peerConnection = getPeerConnection(pc);
 		auto peerConnection = getPeerConnection(pc);
 
 
-		if (!buffer)
-			throw std::invalid_argument("Unexpected null pointer");
-
 		if (size <= 0)
 		if (size <= 0)
 			return 0;
 			return 0;
 
 
+		if (!buffer)
+			throw std::invalid_argument("Unexpected null pointer for buffer");
+
 		if (auto addr = peerConnection->remoteAddress()) {
 		if (auto addr = peerConnection->remoteAddress()) {
 			const char *data = addr->data();
 			const char *data = addr->data();
 			size = std::min(size - 1, int(addr->size()));
 			size = std::min(size - 1, int(addr->size()));
@@ -455,12 +542,12 @@ int rtcGetDataChannelLabel(int dc, char *buffer, int size) {
 	return WRAP({
 	return WRAP({
 		auto dataChannel = getDataChannel(dc);
 		auto dataChannel = getDataChannel(dc);
 
 
-		if (!buffer)
-			throw std::invalid_argument("Unexpected null pointer");
-
 		if (size <= 0)
 		if (size <= 0)
 			return 0;
 			return 0;
 
 
+		if (!buffer)
+			throw std::invalid_argument("Unexpected null pointer for buffer");
+
 		string label = dataChannel->label();
 		string label = dataChannel->label();
 		const char *data = label.data();
 		const char *data = label.data();
 		size = std::min(size - 1, int(label.size()));
 		size = std::min(size - 1, int(label.size()));
@@ -474,12 +561,12 @@ int rtcGetDataChannelProtocol(int dc, char *buffer, int size) {
 	return WRAP({
 	return WRAP({
 		auto dataChannel = getDataChannel(dc);
 		auto dataChannel = getDataChannel(dc);
 
 
-		if (!buffer)
-			throw std::invalid_argument("Unexpected null pointer");
-
 		if (size <= 0)
 		if (size <= 0)
 			return 0;
 			return 0;
 
 
+		if (!buffer)
+			throw std::invalid_argument("Unexpected null pointer for buffer");
+
 		string protocol = dataChannel->protocol();
 		string protocol = dataChannel->protocol();
 		const char *data = protocol.data();
 		const char *data = protocol.data();
 		size = std::min(size - 1, int(protocol.size()));
 		size = std::min(size - 1, int(protocol.size()));
@@ -494,7 +581,7 @@ int rtcGetDataChannelReliability(int dc, rtcReliability *reliability) {
 		auto dataChannel = getDataChannel(dc);
 		auto dataChannel = getDataChannel(dc);
 
 
 		if (!reliability)
 		if (!reliability)
-			throw std::invalid_argument("Unexpected null pointer");
+			throw std::invalid_argument("Unexpected null pointer for reliability");
 
 
 		Reliability r = dataChannel->reliability();
 		Reliability r = dataChannel->reliability();
 		std::memset(reliability, 0, sizeof(*reliability));
 		std::memset(reliability, 0, sizeof(*reliability));
@@ -573,8 +660,8 @@ int rtcSendMessage(int id, const char *data, int size) {
 	return WRAP({
 	return WRAP({
 		auto channel = getChannel(id);
 		auto channel = getChannel(id);
 
 
-		if (!data)
-			throw std::invalid_argument("Unexpected null pointer");
+		if (!data && size != 0)
+			throw std::invalid_argument("Unexpected null pointer for data");
 
 
 		if (size >= 0) {
 		if (size >= 0) {
 			auto b = reinterpret_cast<const byte *>(data);
 			auto b = reinterpret_cast<const byte *>(data);
@@ -637,25 +724,32 @@ int rtcReceiveMessage(int id, char *buffer, int *size) {
 	return WRAP({
 	return WRAP({
 		auto channel = getChannel(id);
 		auto channel = getChannel(id);
 
 
-		if (!buffer || !size)
-			throw std::invalid_argument("Unexpected null pointer");
+		if (!size)
+			throw std::invalid_argument("Unexpected null pointer for size");
+
+		if (!buffer && *size != 0)
+			throw std::invalid_argument("Unexpected null pointer for buffer");
 
 
 		if (auto message = channel->receive())
 		if (auto message = channel->receive())
 			return std::visit( //
 			return std::visit( //
 			    overloaded{    //
 			    overloaded{    //
 			               [&](binary b) {
 			               [&](binary b) {
-				               *size = std::min(*size, int(b.size()));
-				               auto data = reinterpret_cast<const char *>(b.data());
-				               std::copy(data, data + *size, buffer);
+				               if (*size > 0) {
+					               *size = std::min(*size, int(b.size()));
+					               auto data = reinterpret_cast<const char *>(b.data());
+					               std::copy(data, data + *size, buffer);
+				               }
 				               return 1;
 				               return 1;
 			               },
 			               },
 			               [&](string s) {
 			               [&](string s) {
-				               int len = std::min(*size - 1, int(s.size()));
-				               if (len >= 0) {
-					               std::copy(s.data(), s.data() + len, buffer);
-					               buffer[len] = '\0';
+				               if (*size > 0) {
+					               int len = std::min(*size - 1, int(s.size()));
+					               if (len >= 0) {
+						               std::copy(s.data(), s.data() + len, buffer);
+						               buffer[len] = '\0';
+					               }
+					               *size = -(len + 1);
 				               }
 				               }
-				               *size = -(len + 1);
 				               return 1;
 				               return 1;
 			               }},
 			               }},
 			    *message);
 			    *message);