VT: ARTPWriter: Enhanced ARTPWriter as a RTP output
Enhances ARTPWriter that sends RTP packets to network.
Code modification as per RFC 3550, RFC 1889, RFC 894.
1. Interfaces are added to provide local-ip and remote-ip address to ARTPWriter.
2. By the RFC 3550, RTP protocol is run on top of UDP typically.
2-1. A port number for RTP is EVEN.
3. By the RFC 3550, RTCP protocol exists with RTP as coupled one.
3-1. By the RFC 1889, a port number for RTCP is the next ODD number of RTP port.
4. SPS/PPS are sent for every I-frame.
Basically encoder generates SPS/PPS at the first video stream.
But those can be lost because RTP is run on UDP.
So those are sent for every I-frame, and this is common action for RTP.
5. By the RFC 894, the maximum length of an IP datagram sent over an Ethernet is 1500 byte.
A packet would be fragmented if its size overs.
So some margin is needed for various cases. (various headers for UDP, RTP, encryption, etc...)
Change-Id: I34e18ed5ada6b51be3645eb8f725a6c16fe4878d
Signed-off-by: Kim Sungyeon <sy85.kim@samsung.com>
Signed-off-by: Byeongjo Park <bjo.park@samsung.com>
Merged-in: I34e18ed5ada6b51be3645eb8f725a6c16fe4878d
diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h
index 64eb8b4..d455093 100644
--- a/media/libstagefright/include/media/stagefright/MetaDataBase.h
+++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h
@@ -247,6 +247,8 @@
// Treat empty track as malformed for MediaRecorder.
kKeyEmptyTrackMalFormed = 'nemt', // bool (int32_t)
+ kKeySps = 'sSps', // int32_t, indicates that a buffer is sps.
+ kKeyPps = 'sPps', // int32_t, indicates that a buffer is pps.
};
enum {
diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp
index 58d6086..92eaa70 100644
--- a/media/libstagefright/rtsp/ARTPWriter.cpp
+++ b/media/libstagefright/rtsp/ARTPWriter.cpp
@@ -35,6 +35,18 @@
#define PT 97
#define PT_STR "97"
+#define H264_NALU_MASK 0x1F
+#define H264_NALU_SPS 0x7
+#define H264_NALU_PPS 0x8
+#define H264_NALU_IFRAME 0x5
+#define H264_NALU_PFRAME 0x1
+#define UDP_HEADER_SIZE 8
+#define RTP_HEADER_SIZE 12
+#define RTP_HEADER_EXT_SIZE 1
+#define RTP_FU_HEADER_SIZE 2
+#define RTP_PAYLOAD_ROOM_SIZE 140
+
+
namespace android {
// static const size_t kMaxPacketSize = 65507; // maximum payload in UDP over IP
@@ -55,8 +67,10 @@
mLooper->registerHandler(mReflector);
mLooper->start();
- mSocket = socket(AF_INET, SOCK_DGRAM, 0);
- CHECK_GE(mSocket, 0);
+ mRTPSocket = socket(AF_INET, SOCK_DGRAM, 0);
+ CHECK_GE(mRTPSocket, 0);
+ mRTCPSocket = socket(AF_INET, SOCK_DGRAM, 0);
+ CHECK_GE(mRTCPSocket, 0);
memset(mRTPAddr.sin_zero, 0, sizeof(mRTPAddr.sin_zero));
mRTPAddr.sin_family = AF_INET;
@@ -72,6 +86,83 @@
mRTCPAddr = mRTPAddr;
mRTCPAddr.sin_port = htons(ntohs(mRTPAddr.sin_port) | 1);
+ mSPSBuf = NULL;
+ mPPSBuf = NULL;
+
+#if LOG_TO_FILES
+ mRTPFd = open(
+ "/data/misc/rtpout.bin",
+ O_WRONLY | O_CREAT | O_TRUNC,
+ 0644);
+ CHECK_GE(mRTPFd, 0);
+
+ mRTCPFd = open(
+ "/data/misc/rtcpout.bin",
+ O_WRONLY | O_CREAT | O_TRUNC,
+ 0644);
+ CHECK_GE(mRTCPFd, 0);
+#endif
+}
+
+ARTPWriter::ARTPWriter(int fd, String8& localIp, int localPort, String8& remoteIp,
+ int remotePort)
+ : mFlags(0),
+ mFd(dup(fd)),
+ mLooper(new ALooper),
+ mReflector(new AHandlerReflector<ARTPWriter>(this)) {
+ CHECK_GE(fd, 0);
+
+ mLooper->setName("rtp writer");
+ mLooper->registerHandler(mReflector);
+ mLooper->start();
+
+ mRTPSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ CHECK_GE(mRTPSocket, 0);
+ mRTCPSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ CHECK_GE(mRTCPSocket, 0);
+
+ // socket need to be SO_REUSEADDR to avoid "Address already in use" error.
+ int sockopt = 1;
+ setsockopt(mRTPSocket, SOL_SOCKET, SO_REUSEADDR, (int *)&sockopt, sizeof(sockopt));
+ setsockopt(mRTPSocket, SOL_SOCKET, SO_REUSEPORT, (int *)&sockopt, sizeof(sockopt));
+ setsockopt(mRTCPSocket, SOL_SOCKET, SO_REUSEADDR, (int *)&sockopt, sizeof(sockopt));
+ setsockopt(mRTCPSocket, SOL_SOCKET, SO_REUSEPORT, (int *)&sockopt, sizeof(sockopt));
+
+ // binding rtp socket to assign source ip/port.
+ memset(mLocalAddr.sin_zero, 0, sizeof(mRTPAddr.sin_zero));
+ mLocalAddr.sin_family = AF_INET;
+ mLocalAddr.sin_addr.s_addr = inet_addr(localIp.string());
+ mLocalAddr.sin_port = htons((unsigned short int)localPort);
+ if (bind(mRTPSocket, (struct sockaddr*)&mLocalAddr, sizeof(mLocalAddr)) == -1) {
+ ALOGE("failed to bind rtp %s:%d err=%s", localIp.string(),
+ ntohs(mLocalAddr.sin_port), strerror(errno));
+ } else {
+ ALOGD("succeed to bind rtp %s:%d", localIp.string(), ntohs(mLocalAddr.sin_port));
+ }
+ // binding rtcp socket to assign source ip/port.
+ mLocalAddr.sin_port = htons(ntohs(mLocalAddr.sin_port) + 1);
+ if (bind(mRTCPSocket, (struct sockaddr*)&mLocalAddr, sizeof(mLocalAddr)) == -1) {
+ ALOGE("failed to bind rtcp %s:%d", localIp.string(), ntohs(mLocalAddr.sin_port));
+ } else {
+ ALOGD("succeed to bind rtcp %s:%d", localIp.string(), ntohs(mLocalAddr.sin_port) | 1);
+ }
+ mLocalAddr.sin_port = htons(ntohs(mLocalAddr.sin_port) - 1);
+
+ // update client settings
+ memset(mRTPAddr.sin_zero, 0, sizeof(mRTPAddr.sin_zero));
+ mRTPAddr.sin_family = AF_INET;
+ mRTPAddr.sin_addr.s_addr = inet_addr(remoteIp.string());
+ mRTPAddr.sin_port = htons((unsigned short int)remotePort);
+
+ mRTCPAddr = mRTPAddr;
+ mRTCPAddr.sin_port = htons(ntohs(mRTPAddr.sin_port) | 1);
+
+ ALOGD("localIp=%s, rtp=%d, rtcp=%d", localIp.string(), localPort, localPort+1);
+ ALOGD("remoteIp=%s, rtp=%d, rtcp=%d", remoteIp.string(), ntohs(mRTPAddr.sin_port),
+ ntohs(mRTCPAddr.sin_port));
+
+ mSPSBuf = NULL;
+ mPPSBuf = NULL;
#if LOG_TO_FILES
mRTPFd = open(
@@ -97,11 +188,24 @@
mRTPFd = -1;
#endif
- close(mSocket);
- mSocket = -1;
+ close(mRTPSocket);
+ mRTPSocket = -1;
+
+ close(mRTCPSocket);
+ mRTCPSocket = -1;
close(mFd);
mFd = -1;
+
+ if(mSPSBuf != NULL) {
+ mSPSBuf->release();
+ mSPSBuf = NULL;
+ }
+
+ if(mPPSBuf != NULL) {
+ mPPSBuf->release();
+ mPPSBuf = NULL;
+ }
}
status_t ARTPWriter::addSource(const sp<MediaSource> &source) {
@@ -123,7 +227,7 @@
mFlags &= ~kFlagEOS;
mSourceID = rand();
mSeqNo = UniformRand(65536);
- mRTPTimeBase = rand();
+ mRTPTimeBase = 0;
mNumRTPSent = 0;
mNumRTPOctetsSent = 0;
mLastRTPTime = 0;
@@ -173,9 +277,12 @@
return OK;
}
-static void StripStartcode(MediaBufferBase *buffer) {
+// return size of SPS if there is more NAL unit found following to SPS.
+static uint32_t StripStartcode(MediaBufferBase *buffer) {
+ uint32_t nalSize = 0;
+
if (buffer->range_length() < 4) {
- return;
+ return 0;
}
const uint8_t *ptr =
@@ -185,6 +292,56 @@
buffer->set_range(
buffer->range_offset() + 4, buffer->range_length() - 4);
}
+
+ ptr = (const uint8_t *)buffer->data() + buffer->range_offset();
+
+ if ((*ptr & H264_NALU_MASK) == H264_NALU_SPS) {
+ for (uint32_t i = 0; i < buffer->range_length(); i++) {
+
+ if (!memcmp(ptr + i, "\x00\x00\x00\x01", 4)) {
+ // Now, we found one more NAL unit in the media buffer.
+ // Mostly, it will be a PPS.
+ nalSize = i;
+ ALOGV("SPS found. size=%d", nalSize);
+ }
+ }
+ }
+
+ return nalSize;
+}
+
+static void SpsPpsParser(MediaBufferBase *mediaBuffer,
+ MediaBufferBase **spsBuffer, MediaBufferBase **ppsBuffer, uint32_t spsSize) {
+
+ if (mediaBuffer == NULL || mediaBuffer->range_length() < 4)
+ return;
+
+ if((*spsBuffer) != NULL) {
+ (*spsBuffer)->release();
+ (*spsBuffer) = NULL;
+ }
+
+ if((*ppsBuffer) != NULL) {
+ (*ppsBuffer)->release();
+ (*ppsBuffer) = NULL;
+ }
+
+ // we got sps/pps but startcode of sps is striped.
+ (*spsBuffer) = MediaBufferBase::Create(spsSize);
+ int32_t ppsSize = mediaBuffer->range_length() - spsSize - 4/*startcode*/;
+ (*ppsBuffer) = MediaBufferBase::Create(ppsSize);
+ memcpy((*spsBuffer)->data(),
+ (const uint8_t *)mediaBuffer->data() + mediaBuffer->range_offset(),
+ spsSize);
+
+ if (ppsSize > 0) {
+ ALOGV("PPS found. size=%d", (int)ppsSize);
+ mediaBuffer->set_range(mediaBuffer->range_offset() + spsSize + 4/*startcode*/,
+ mediaBuffer->range_length() - spsSize - 4/*startcode*/);
+ memcpy((*ppsBuffer)->data(),
+ (const uint8_t *)mediaBuffer->data() + mediaBuffer->range_offset(),
+ ppsSize);
+ }
}
void ARTPWriter::onMessageReceived(const sp<AMessage> &msg) {
@@ -280,8 +437,12 @@
ALOGV("read buffer of size %zu", mediaBuf->range_length());
if (mMode == H264) {
- StripStartcode(mediaBuf);
- sendAVCData(mediaBuf);
+ uint32_t spsSize = 0;
+ if ((spsSize = StripStartcode(mediaBuf)) > 0) {
+ SpsPpsParser(mediaBuf, &mSPSBuf, &mPPSBuf, spsSize);
+ } else {
+ sendAVCData(mediaBuf);
+ }
} else if (mMode == H263) {
sendH263Data(mediaBuf);
} else if (mMode == AMR_NB || mMode == AMR_WB) {
@@ -310,7 +471,7 @@
void ARTPWriter::send(const sp<ABuffer> &buffer, bool isRTCP) {
ssize_t n = sendto(
- mSocket, buffer->data(), buffer->size(), 0,
+ isRTCP ? mRTCPSocket : mRTPSocket, buffer->data(), buffer->size(), 0,
(const struct sockaddr *)(isRTCP ? &mRTCPAddr : &mRTPAddr),
sizeof(mRTCPAddr));
@@ -569,6 +730,26 @@
send(buffer, true /* isRTCP */);
}
+void ARTPWriter::sendSPSPPSIfIFrame(MediaBufferBase *mediaBuf, int64_t timeUs) {
+ const uint8_t *mediaData =
+ (const uint8_t *)mediaBuf->data() + mediaBuf->range_offset();
+
+ if ((mediaData[0] & H264_NALU_MASK) != H264_NALU_IFRAME)
+ return;
+
+ if (mSPSBuf != NULL) {
+ mSPSBuf->meta_data().setInt64(kKeyTime, timeUs);
+ mSPSBuf->meta_data().setInt32(kKeySps, 1);
+ sendAVCData(mSPSBuf);
+ }
+
+ if (mPPSBuf != NULL) {
+ mPPSBuf->meta_data().setInt64(kKeyTime, timeUs);
+ mPPSBuf->meta_data().setInt32(kKeyPps, 1);
+ sendAVCData(mPPSBuf);
+ }
+}
+
void ARTPWriter::sendAVCData(MediaBufferBase *mediaBuf) {
// 12 bytes RTP header + 2 bytes for the FU-indicator and FU-header.
CHECK_GE(kMaxPacketSize, 12u + 2u);
@@ -576,17 +757,30 @@
int64_t timeUs;
CHECK(mediaBuf->meta_data().findInt64(kKeyTime, &timeUs));
+ sendSPSPPSIfIFrame(mediaBuf, timeUs);
+
uint32_t rtpTime = mRTPTimeBase + (timeUs * 9 / 100LL);
const uint8_t *mediaData =
(const uint8_t *)mediaBuf->data() + mediaBuf->range_offset();
+ int32_t sps, pps;
+ bool isSpsPps = false;
+ if (mediaBuf->meta_data().findInt32(kKeySps, &sps) ||
+ mediaBuf->meta_data().findInt32(kKeyPps, &pps)) {
+ isSpsPps = true;
+ }
+
sp<ABuffer> buffer = new ABuffer(kMaxPacketSize);
- if (mediaBuf->range_length() + 12 <= buffer->capacity()) {
+ if (mediaBuf->range_length() + UDP_HEADER_SIZE + RTP_HEADER_SIZE + RTP_PAYLOAD_ROOM_SIZE
+ <= buffer->capacity()) {
// The data fits into a single packet
uint8_t *data = buffer->data();
data[0] = 0x80;
- data[1] = (1 << 7) | PT; // M-bit
+ if (isSpsPps)
+ data[1] = PT; // Marker bit should not be set in case of sps/pps
+ else
+ data[1] = (1 << 7) | PT;
data[2] = (mSeqNo >> 8) & 0xff;
data[3] = mSeqNo & 0xff;
data[4] = rtpTime >> 24;
@@ -618,9 +812,11 @@
while (offset < mediaBuf->range_length()) {
size_t size = mediaBuf->range_length() - offset;
bool lastPacket = true;
- if (size + 12 + 2 > buffer->capacity()) {
+ if (size + UDP_HEADER_SIZE + RTP_HEADER_SIZE + RTP_FU_HEADER_SIZE +
+ RTP_PAYLOAD_ROOM_SIZE > buffer->capacity()) {
lastPacket = false;
- size = buffer->capacity() - 12 - 2;
+ size = buffer->capacity() - UDP_HEADER_SIZE - RTP_HEADER_SIZE -
+ RTP_FU_HEADER_SIZE - RTP_PAYLOAD_ROOM_SIZE;
}
uint8_t *data = buffer->data();
diff --git a/media/libstagefright/rtsp/ARTPWriter.h b/media/libstagefright/rtsp/ARTPWriter.h
index 2f13486..6795d28 100644
--- a/media/libstagefright/rtsp/ARTPWriter.h
+++ b/media/libstagefright/rtsp/ARTPWriter.h
@@ -36,6 +36,8 @@
struct ARTPWriter : public MediaWriter {
explicit ARTPWriter(int fd);
+ explicit ARTPWriter(int fd, String8& localIp, int localPort,
+ String8& remoteIp, int remotePort);
virtual status_t addSource(const sp<MediaSource> &source);
virtual bool reachedEOS();
@@ -76,7 +78,8 @@
sp<ALooper> mLooper;
sp<AHandlerReflector<ARTPWriter> > mReflector;
- int mSocket;
+ int mRTPSocket, mRTCPSocket;
+ struct sockaddr_in mLocalAddr;
struct sockaddr_in mRTPAddr;
struct sockaddr_in mRTCPAddr;
@@ -84,6 +87,9 @@
AString mSeqParamSet;
AString mPicParamSet;
+ MediaBufferBase *mSPSBuf;
+ MediaBufferBase *mPPSBuf;
+
uint32_t mSourceID;
uint32_t mSeqNo;
uint32_t mRTPTimeBase;
@@ -114,6 +120,7 @@
void dumpSessionDesc();
void sendBye();
+ void sendSPSPPSIfIFrame(MediaBufferBase *mediaBuf, int64_t timeUs);
void sendAVCData(MediaBufferBase *mediaBuf);
void sendH263Data(MediaBufferBase *mediaBuf);
void sendAMRData(MediaBufferBase *mediaBuf);