summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/rtsp/ARTPWriter.cpp
diff options
context:
space:
mode:
authorAndreas Huber <andih@google.com>2010-08-04 10:14:30 -0700
committerAndreas Huber <andih@google.com>2010-08-04 11:49:24 -0700
commit39ddf8e0f18766f7ba1e3246b774aa6ebd93eea8 (patch)
tree87f5d0d68c1779f113843e939c41440ff6b00389 /media/libstagefright/rtsp/ARTPWriter.cpp
parent610959a52fe22a88e50d158f5f5f492fee4f1921 (diff)
downloadframeworks_av-39ddf8e0f18766f7ba1e3246b774aa6ebd93eea8.zip
frameworks_av-39ddf8e0f18766f7ba1e3246b774aa6ebd93eea8.tar.gz
frameworks_av-39ddf8e0f18766f7ba1e3246b774aa6ebd93eea8.tar.bz2
Support for Gtalk video, includes AMR/H.263 assembler and packetization support, extensions to MediaRecorder to stream via RTP over a pair of UDP sockets as well as various fixes to the RTP implementation.
Change-Id: I95b8dd487061add9bade15749e563b01cd99d9a6
Diffstat (limited to 'media/libstagefright/rtsp/ARTPWriter.cpp')
-rw-r--r--media/libstagefright/rtsp/ARTPWriter.cpp813
1 files changed, 813 insertions, 0 deletions
diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp
new file mode 100644
index 0000000..cc23856
--- /dev/null
+++ b/media/libstagefright/rtsp/ARTPWriter.cpp
@@ -0,0 +1,813 @@
+#include "ARTPWriter.h"
+
+#include <fcntl.h>
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/ByteOrder.h>
+
+#define PT 97
+#define PT_STR "97"
+
+namespace android {
+
+// static const size_t kMaxPacketSize = 65507; // maximum payload in UDP over IP
+static const size_t kMaxPacketSize = 1500;
+
+static int UniformRand(int limit) {
+ return ((double)rand() * limit) / RAND_MAX;
+}
+
+ARTPWriter::ARTPWriter(int fd)
+ : mFlags(0),
+ mFd(fd),
+ mLooper(new ALooper),
+ mReflector(new AHandlerReflector<ARTPWriter>(this)) {
+ CHECK_GE(fd, 0);
+
+ mLooper->registerHandler(mReflector);
+ mLooper->start();
+
+ mSocket = socket(AF_INET, SOCK_DGRAM, 0);
+ CHECK_GE(mSocket, 0);
+
+ memset(mRTPAddr.sin_zero, 0, sizeof(mRTPAddr.sin_zero));
+ mRTPAddr.sin_family = AF_INET;
+
+#if 1
+ mRTPAddr.sin_addr.s_addr = INADDR_ANY;
+#else
+ mRTPAddr.sin_addr.s_addr = inet_addr("172.19.19.74");
+#endif
+
+ mRTPAddr.sin_port = htons(5634);
+ CHECK_EQ(0, ntohs(mRTPAddr.sin_port) & 1);
+
+ mRTCPAddr = mRTPAddr;
+ mRTCPAddr.sin_port = htons(ntohs(mRTPAddr.sin_port) | 1);
+
+#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() {
+#if LOG_TO_FILES
+ close(mRTCPFd);
+ mRTCPFd = -1;
+
+ close(mRTPFd);
+ mRTPFd = -1;
+#endif
+
+ close(mSocket);
+ mSocket = -1;
+
+ close(mFd);
+ mFd = -1;
+}
+
+status_t ARTPWriter::addSource(const sp<MediaSource> &source) {
+ mSource = source;
+ return OK;
+}
+
+bool ARTPWriter::reachedEOS() {
+ Mutex::Autolock autoLock(mLock);
+ return (mFlags & kFlagEOS) != 0;
+}
+
+status_t ARTPWriter::start(MetaData *params) {
+ Mutex::Autolock autoLock(mLock);
+ if (mFlags & kFlagStarted) {
+ return INVALID_OPERATION;
+ }
+
+ mFlags &= ~kFlagEOS;
+ mSourceID = rand();
+ mSeqNo = UniformRand(65536);
+ mRTPTimeBase = rand();
+ mNumRTPSent = 0;
+ mNumRTPOctetsSent = 0;
+ mLastRTPTime = 0;
+ mLastNTPTime = 0;
+ mNumSRsSent = 0;
+
+ const char *mime;
+ CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
+
+ mMode = INVALID;
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+ mMode = H264;
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_H263)) {
+ mMode = H263;
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) {
+ mMode = AMR_NB;
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {
+ mMode = AMR_WB;
+ } else {
+ TRESPASS();
+ }
+
+ (new AMessage(kWhatStart, mReflector->id()))->post();
+
+ while (!(mFlags & kFlagStarted)) {
+ mCondition.wait(mLock);
+ }
+
+ return OK;
+}
+
+void ARTPWriter::stop() {
+ Mutex::Autolock autoLock(mLock);
+ if (!(mFlags & kFlagStarted)) {
+ return;
+ }
+
+ (new AMessage(kWhatStop, mReflector->id()))->post();
+
+ while (mFlags & kFlagStarted) {
+ mCondition.wait(mLock);
+ }
+}
+
+void ARTPWriter::pause() {
+}
+
+static void StripStartcode(MediaBuffer *buffer) {
+ if (buffer->range_length() < 4) {
+ return;
+ }
+
+ const uint8_t *ptr =
+ (const uint8_t *)buffer->data() + buffer->range_offset();
+
+ if (!memcmp(ptr, "\x00\x00\x00\x01", 4)) {
+ buffer->set_range(
+ buffer->range_offset() + 4, buffer->range_length() - 4);
+ }
+}
+
+void ARTPWriter::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatStart:
+ {
+ CHECK_EQ(mSource->start(), (status_t)OK);
+
+#if 0
+ if (mMode == H264) {
+ MediaBuffer *buffer;
+ CHECK_EQ(mSource->read(&buffer), (status_t)OK);
+
+ StripStartcode(buffer);
+ makeH264SPropParamSets(buffer);
+ buffer->release();
+ buffer = NULL;
+ }
+
+ dumpSessionDesc();
+#endif
+
+ {
+ Mutex::Autolock autoLock(mLock);
+ mFlags |= kFlagStarted;
+ mCondition.signal();
+ }
+
+ (new AMessage(kWhatRead, mReflector->id()))->post();
+ (new AMessage(kWhatSendSR, mReflector->id()))->post();
+ break;
+ }
+
+ case kWhatStop:
+ {
+ CHECK_EQ(mSource->stop(), (status_t)OK);
+
+ sendBye();
+
+ {
+ Mutex::Autolock autoLock(mLock);
+ mFlags &= ~kFlagStarted;
+ mCondition.signal();
+ }
+ break;
+ }
+
+ case kWhatRead:
+ {
+ {
+ Mutex::Autolock autoLock(mLock);
+ if (!(mFlags & kFlagStarted)) {
+ break;
+ }
+ }
+
+ onRead(msg);
+ break;
+ }
+
+ case kWhatSendSR:
+ {
+ {
+ Mutex::Autolock autoLock(mLock);
+ if (!(mFlags & kFlagStarted)) {
+ break;
+ }
+ }
+
+ onSendSR(msg);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+}
+
+void ARTPWriter::onRead(const sp<AMessage> &msg) {
+ MediaBuffer *mediaBuf;
+ status_t err = mSource->read(&mediaBuf);
+
+ if (err != OK) {
+ LOG(INFO) << "reached EOS.";
+
+ Mutex::Autolock autoLock(mLock);
+ mFlags |= kFlagEOS;
+ return;
+ }
+
+ if (mediaBuf->range_length() > 0) {
+ LOG(VERBOSE) << "read buffer of size " << mediaBuf->range_length();
+
+ if (mMode == H264) {
+ StripStartcode(mediaBuf);
+ sendAVCData(mediaBuf);
+ } else if (mMode == H263) {
+ sendH263Data(mediaBuf);
+ } else if (mMode == AMR_NB || mMode == AMR_WB) {
+ sendAMRData(mediaBuf);
+ }
+ }
+
+ mediaBuf->release();
+ mediaBuf = NULL;
+
+ msg->post();
+}
+
+void ARTPWriter::onSendSR(const sp<AMessage> &msg) {
+ sp<ABuffer> buffer = new ABuffer(65536);
+ buffer->setRange(0, 0);
+
+ addSR(buffer);
+ addSDES(buffer);
+
+ send(buffer, true /* isRTCP */);
+
+ ++mNumSRsSent;
+ msg->post(3000000);
+}
+
+void ARTPWriter::send(const sp<ABuffer> &buffer, bool isRTCP) {
+ ssize_t n = sendto(
+ mSocket, buffer->data(), buffer->size(), 0,
+ (const struct sockaddr *)(isRTCP ? &mRTCPAddr : &mRTPAddr),
+ sizeof(mRTCPAddr));
+
+ CHECK_EQ(n, (ssize_t)buffer->size());
+
+#if LOG_TO_FILES
+ int fd = isRTCP ? mRTCPFd : mRTPFd;
+
+ uint32_t ms = tolel(ALooper::GetNowUs() / 1000ll);
+ uint32_t length = tolel(buffer->size());
+ write(fd, &ms, sizeof(ms));
+ write(fd, &length, sizeof(length));
+ write(fd, buffer->data(), buffer->size());
+#endif
+}
+
+void ARTPWriter::addSR(const sp<ABuffer> &buffer) {
+ uint8_t *data = buffer->data() + buffer->size();
+
+ data[0] = 0x80 | 0;
+ data[1] = 200; // SR
+ data[2] = 0;
+ data[3] = 6;
+ data[4] = mSourceID >> 24;
+ data[5] = (mSourceID >> 16) & 0xff;
+ data[6] = (mSourceID >> 8) & 0xff;
+ data[7] = mSourceID & 0xff;
+
+ data[8] = mLastNTPTime >> (64 - 8);
+ data[9] = (mLastNTPTime >> (64 - 16)) & 0xff;
+ data[10] = (mLastNTPTime >> (64 - 24)) & 0xff;
+ data[11] = (mLastNTPTime >> 32) & 0xff;
+ data[12] = (mLastNTPTime >> 24) & 0xff;
+ data[13] = (mLastNTPTime >> 16) & 0xff;
+ data[14] = (mLastNTPTime >> 8) & 0xff;
+ data[15] = mLastNTPTime & 0xff;
+
+ data[16] = (mLastRTPTime >> 24) & 0xff;
+ data[17] = (mLastRTPTime >> 16) & 0xff;
+ data[18] = (mLastRTPTime >> 8) & 0xff;
+ data[19] = mLastRTPTime & 0xff;
+
+ data[20] = mNumRTPSent >> 24;
+ data[21] = (mNumRTPSent >> 16) & 0xff;
+ data[22] = (mNumRTPSent >> 8) & 0xff;
+ data[23] = mNumRTPSent & 0xff;
+
+ data[24] = mNumRTPOctetsSent >> 24;
+ data[25] = (mNumRTPOctetsSent >> 16) & 0xff;
+ data[26] = (mNumRTPOctetsSent >> 8) & 0xff;
+ data[27] = mNumRTPOctetsSent & 0xff;
+
+ buffer->setRange(buffer->offset(), buffer->size() + 28);
+}
+
+void ARTPWriter::addSDES(const sp<ABuffer> &buffer) {
+ uint8_t *data = buffer->data() + buffer->size();
+ data[0] = 0x80 | 1;
+ data[1] = 202; // SDES
+ data[4] = mSourceID >> 24;
+ data[5] = (mSourceID >> 16) & 0xff;
+ data[6] = (mSourceID >> 8) & 0xff;
+ data[7] = mSourceID & 0xff;
+
+ size_t offset = 8;
+
+ data[offset++] = 1; // CNAME
+
+ static const char *kCNAME = "someone@somewhere";
+ data[offset++] = strlen(kCNAME);
+
+ memcpy(&data[offset], kCNAME, strlen(kCNAME));
+ offset += strlen(kCNAME);
+
+ data[offset++] = 7; // NOTE
+
+ static const char *kNOTE = "Hell's frozen over.";
+ data[offset++] = strlen(kNOTE);
+
+ memcpy(&data[offset], kNOTE, strlen(kNOTE));
+ offset += strlen(kNOTE);
+
+ data[offset++] = 0;
+
+ if ((offset % 4) > 0) {
+ size_t count = 4 - (offset % 4);
+ switch (count) {
+ case 3:
+ data[offset++] = 0;
+ case 2:
+ data[offset++] = 0;
+ case 1:
+ data[offset++] = 0;
+ }
+ }
+
+ size_t numWords = (offset / 4) - 1;
+ data[2] = numWords >> 8;
+ data[3] = numWords & 0xff;
+
+ buffer->setRange(buffer->offset(), buffer->size() + offset);
+}
+
+// static
+uint64_t ARTPWriter::GetNowNTP() {
+ uint64_t nowUs = ALooper::GetNowUs();
+
+ nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll;
+
+ uint64_t hi = nowUs / 1000000ll;
+ uint64_t lo = ((1ll << 32) * (nowUs % 1000000ll)) / 1000000ll;
+
+ return (hi << 32) | lo;
+}
+
+void ARTPWriter::dumpSessionDesc() {
+ AString sdp;
+ sdp = "v=0\r\n";
+
+ sdp.append("o=- ");
+
+ uint64_t ntp = GetNowNTP();
+ sdp.append(ntp);
+ sdp.append(" ");
+ sdp.append(ntp);
+ sdp.append(" IN IP4 127.0.0.0\r\n");
+
+ sdp.append(
+ "s=Sample\r\n"
+ "i=Playing around\r\n"
+ "c=IN IP4 ");
+
+ struct in_addr addr;
+ addr.s_addr = ntohl(INADDR_LOOPBACK);
+
+ sdp.append(inet_ntoa(addr));
+
+ sdp.append(
+ "\r\n"
+ "t=0 0\r\n"
+ "a=range:npt=now-\r\n");
+
+ sp<MetaData> meta = mSource->getFormat();
+
+ if (mMode == H264 || mMode == H263) {
+ sdp.append("m=video ");
+ } else {
+ sdp.append("m=audio ");
+ }
+
+ sdp.append(StringPrintf("%d", ntohs(mRTPAddr.sin_port)));
+ sdp.append(
+ " RTP/AVP " PT_STR "\r\n"
+ "b=AS 320000\r\n"
+ "a=rtpmap:" PT_STR " ");
+
+ if (mMode == H264) {
+ sdp.append("H264/90000");
+ } else if (mMode == H263) {
+ sdp.append("H263-1998/90000");
+ } else if (mMode == AMR_NB || mMode == AMR_WB) {
+ int32_t sampleRate, numChannels;
+ CHECK(mSource->getFormat()->findInt32(kKeySampleRate, &sampleRate));
+ CHECK(mSource->getFormat()->findInt32(kKeyChannelCount, &numChannels));
+
+ CHECK_EQ(numChannels, 1);
+ CHECK_EQ(sampleRate, (mMode == AMR_NB) ? 8000 : 16000);
+
+ sdp.append(mMode == AMR_NB ? "AMR" : "AMR-WB");
+ sdp.append(StringPrintf("/%d/%d", sampleRate, numChannels));
+ } else {
+ TRESPASS();
+ }
+
+ sdp.append("\r\n");
+
+ if (mMode == H264 || mMode == H263) {
+ int32_t width, height;
+ CHECK(meta->findInt32(kKeyWidth, &width));
+ CHECK(meta->findInt32(kKeyHeight, &height));
+
+ sdp.append("a=cliprect 0,0,");
+ sdp.append(height);
+ sdp.append(",");
+ sdp.append(width);
+ sdp.append("\r\n");
+
+ sdp.append(
+ "a=framesize:" PT_STR " ");
+ sdp.append(width);
+ sdp.append("-");
+ sdp.append(height);
+ sdp.append("\r\n");
+ }
+
+ if (mMode == H264) {
+ sdp.append(
+ "a=fmtp:" PT_STR " profile-level-id=");
+ sdp.append(mProfileLevel);
+ sdp.append(";sprop-parameter-sets=");
+
+ sdp.append(mSeqParamSet);
+ sdp.append(",");
+ sdp.append(mPicParamSet);
+ sdp.append(";packetization-mode=1\r\n");
+ } else if (mMode == AMR_NB || mMode == AMR_WB) {
+ sdp.append("a=fmtp:" PT_STR " octed-align\r\n");
+ }
+
+ LOG(INFO) << sdp;
+}
+
+void ARTPWriter::makeH264SPropParamSets(MediaBuffer *buffer) {
+ static const char kStartCode[] = "\x00\x00\x00\x01";
+
+ const uint8_t *data =
+ (const uint8_t *)buffer->data() + buffer->range_offset();
+ size_t size = buffer->range_length();
+
+ CHECK_GE(size, 0u);
+
+ size_t startCodePos = 0;
+ while (startCodePos + 3 < size
+ && memcmp(kStartCode, &data[startCodePos], 4)) {
+ ++startCodePos;
+ }
+
+ CHECK_LT(startCodePos + 3, size);
+
+ CHECK_EQ((unsigned)data[0], 0x67u);
+
+ mProfileLevel =
+ StringPrintf("%02X%02X%02X", data[1], data[2], data[3]);
+
+ encodeBase64(data, startCodePos, &mSeqParamSet);
+
+ encodeBase64(&data[startCodePos + 4], size - startCodePos - 4,
+ &mPicParamSet);
+}
+
+void ARTPWriter::sendBye() {
+ sp<ABuffer> buffer = new ABuffer(8);
+ uint8_t *data = buffer->data();
+ *data++ = (2 << 6) | 1;
+ *data++ = 203;
+ *data++ = 0;
+ *data++ = 1;
+ *data++ = mSourceID >> 24;
+ *data++ = (mSourceID >> 16) & 0xff;
+ *data++ = (mSourceID >> 8) & 0xff;
+ *data++ = mSourceID & 0xff;
+ buffer->setRange(0, 8);
+
+ send(buffer, true /* isRTCP */);
+}
+
+void ARTPWriter::sendAVCData(MediaBuffer *mediaBuf) {
+ // 12 bytes RTP header + 2 bytes for the FU-indicator and FU-header.
+ CHECK_GE(kMaxPacketSize, 12u + 2u);
+
+ int64_t timeUs;
+ CHECK(mediaBuf->meta_data()->findInt64(kKeyTime, &timeUs));
+
+ uint32_t rtpTime = mRTPTimeBase + (timeUs * 9 / 100ll);
+
+ const uint8_t *mediaData =
+ (const uint8_t *)mediaBuf->data() + mediaBuf->range_offset();
+
+ sp<ABuffer> buffer = new ABuffer(kMaxPacketSize);
+ if (mediaBuf->range_length() + 12 <= buffer->capacity()) {
+ // The data fits into a single packet
+ uint8_t *data = buffer->data();
+ data[0] = 0x80;
+ data[1] = (1 << 7) | PT; // M-bit
+ data[2] = (mSeqNo >> 8) & 0xff;
+ data[3] = mSeqNo & 0xff;
+ data[4] = rtpTime >> 24;
+ data[5] = (rtpTime >> 16) & 0xff;
+ data[6] = (rtpTime >> 8) & 0xff;
+ data[7] = rtpTime & 0xff;
+ data[8] = mSourceID >> 24;
+ data[9] = (mSourceID >> 16) & 0xff;
+ data[10] = (mSourceID >> 8) & 0xff;
+ data[11] = mSourceID & 0xff;
+
+ memcpy(&data[12],
+ mediaData, mediaBuf->range_length());
+
+ buffer->setRange(0, mediaBuf->range_length() + 12);
+
+ send(buffer, false /* isRTCP */);
+
+ ++mSeqNo;
+ ++mNumRTPSent;
+ mNumRTPOctetsSent += buffer->size() - 12;
+ } else {
+ // FU-A
+
+ unsigned nalType = mediaData[0];
+ size_t offset = 1;
+
+ bool firstPacket = true;
+ while (offset < mediaBuf->range_length()) {
+ size_t size = mediaBuf->range_length() - offset;
+ bool lastPacket = true;
+ if (size + 12 + 2 > buffer->capacity()) {
+ lastPacket = false;
+ size = buffer->capacity() - 12 - 2;
+ }
+
+ uint8_t *data = buffer->data();
+ data[0] = 0x80;
+ data[1] = (lastPacket ? (1 << 7) : 0x00) | PT; // M-bit
+ data[2] = (mSeqNo >> 8) & 0xff;
+ data[3] = mSeqNo & 0xff;
+ data[4] = rtpTime >> 24;
+ data[5] = (rtpTime >> 16) & 0xff;
+ data[6] = (rtpTime >> 8) & 0xff;
+ data[7] = rtpTime & 0xff;
+ data[8] = mSourceID >> 24;
+ data[9] = (mSourceID >> 16) & 0xff;
+ data[10] = (mSourceID >> 8) & 0xff;
+ data[11] = mSourceID & 0xff;
+
+ data[12] = 28 | (nalType & 0xe0);
+
+ CHECK(!firstPacket || !lastPacket);
+
+ data[13] =
+ (firstPacket ? 0x80 : 0x00)
+ | (lastPacket ? 0x40 : 0x00)
+ | (nalType & 0x1f);
+
+ memcpy(&data[14], &mediaData[offset], size);
+
+ buffer->setRange(0, 14 + size);
+
+ send(buffer, false /* isRTCP */);
+
+ ++mSeqNo;
+ ++mNumRTPSent;
+ mNumRTPOctetsSent += buffer->size() - 12;
+
+ firstPacket = false;
+ offset += size;
+ }
+ }
+
+ mLastRTPTime = rtpTime;
+ mLastNTPTime = GetNowNTP();
+}
+
+void ARTPWriter::sendH263Data(MediaBuffer *mediaBuf) {
+ CHECK_GE(kMaxPacketSize, 12u + 2u);
+
+ int64_t timeUs;
+ CHECK(mediaBuf->meta_data()->findInt64(kKeyTime, &timeUs));
+
+ uint32_t rtpTime = mRTPTimeBase + (timeUs * 9 / 100ll);
+
+ const uint8_t *mediaData =
+ (const uint8_t *)mediaBuf->data() + mediaBuf->range_offset();
+
+ // hexdump(mediaData, mediaBuf->range_length());
+
+ CHECK_EQ((unsigned)mediaData[0], 0u);
+ CHECK_EQ((unsigned)mediaData[1], 0u);
+
+ size_t offset = 2;
+ size_t size = mediaBuf->range_length();
+
+ while (offset < size) {
+ sp<ABuffer> buffer = new ABuffer(kMaxPacketSize);
+ // CHECK_LE(mediaBuf->range_length() -2 + 14, buffer->capacity());
+
+ size_t remaining = size - offset;
+ bool lastPacket = (remaining + 14 <= buffer->capacity());
+ if (!lastPacket) {
+ remaining = buffer->capacity() - 14;
+ }
+
+ uint8_t *data = buffer->data();
+ data[0] = 0x80;
+ data[1] = (lastPacket ? 0x80 : 0x00) | PT; // M-bit
+ data[2] = (mSeqNo >> 8) & 0xff;
+ data[3] = mSeqNo & 0xff;
+ data[4] = rtpTime >> 24;
+ data[5] = (rtpTime >> 16) & 0xff;
+ data[6] = (rtpTime >> 8) & 0xff;
+ data[7] = rtpTime & 0xff;
+ data[8] = mSourceID >> 24;
+ data[9] = (mSourceID >> 16) & 0xff;
+ data[10] = (mSourceID >> 8) & 0xff;
+ data[11] = mSourceID & 0xff;
+
+ data[12] = (offset == 2) ? 0x04 : 0x00; // P=?, V=0
+ data[13] = 0x00; // PLEN = PEBIT = 0
+
+ memcpy(&data[14], &mediaData[offset], remaining);
+ offset += remaining;
+
+ buffer->setRange(0, remaining + 14);
+
+ send(buffer, false /* isRTCP */);
+
+ ++mSeqNo;
+ ++mNumRTPSent;
+ mNumRTPOctetsSent += buffer->size() - 12;
+ }
+
+ mLastRTPTime = rtpTime;
+ mLastNTPTime = GetNowNTP();
+}
+
+static size_t getFrameSize(bool isWide, unsigned FT) {
+ static const size_t kFrameSizeNB[8] = {
+ 95, 103, 118, 134, 148, 159, 204, 244
+ };
+ static const size_t kFrameSizeWB[9] = {
+ 132, 177, 253, 285, 317, 365, 397, 461, 477
+ };
+
+ size_t frameSize = isWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT];
+
+ // Round up bits to bytes and add 1 for the header byte.
+ frameSize = (frameSize + 7) / 8 + 1;
+
+ return frameSize;
+}
+
+void ARTPWriter::sendAMRData(MediaBuffer *mediaBuf) {
+ const uint8_t *mediaData =
+ (const uint8_t *)mediaBuf->data() + mediaBuf->range_offset();
+
+ size_t mediaLength = mediaBuf->range_length();
+
+ CHECK_GE(kMaxPacketSize, 12u + 1u + mediaLength);
+
+ const bool isWide = (mMode == AMR_WB);
+
+ int64_t timeUs;
+ CHECK(mediaBuf->meta_data()->findInt64(kKeyTime, &timeUs));
+ uint32_t rtpTime = mRTPTimeBase + (timeUs / (isWide ? 250 : 125));
+
+ // hexdump(mediaData, mediaLength);
+
+ Vector<uint8_t> tableOfContents;
+ size_t srcOffset = 0;
+ while (srcOffset < mediaLength) {
+ uint8_t toc = mediaData[srcOffset];
+
+ unsigned FT = (toc >> 3) & 0x0f;
+ CHECK((isWide && FT <= 8) || (!isWide && FT <= 7));
+
+ tableOfContents.push(toc);
+ srcOffset += getFrameSize(isWide, FT);
+ }
+ CHECK_EQ(srcOffset, mediaLength);
+
+ sp<ABuffer> buffer = new ABuffer(kMaxPacketSize);
+ CHECK_LE(mediaLength + 12 + 1, buffer->capacity());
+
+ // The data fits into a single packet
+ uint8_t *data = buffer->data();
+ data[0] = 0x80;
+ data[1] = PT;
+ if (mNumRTPSent == 0) {
+ // Signal start of talk-spurt.
+ data[1] |= 0x80; // M-bit
+ }
+ data[2] = (mSeqNo >> 8) & 0xff;
+ data[3] = mSeqNo & 0xff;
+ data[4] = rtpTime >> 24;
+ data[5] = (rtpTime >> 16) & 0xff;
+ data[6] = (rtpTime >> 8) & 0xff;
+ data[7] = rtpTime & 0xff;
+ data[8] = mSourceID >> 24;
+ data[9] = (mSourceID >> 16) & 0xff;
+ data[10] = (mSourceID >> 8) & 0xff;
+ data[11] = mSourceID & 0xff;
+
+ data[12] = 0xf0; // CMR=15, RR=0
+
+ size_t dstOffset = 13;
+
+ for (size_t i = 0; i < tableOfContents.size(); ++i) {
+ uint8_t toc = tableOfContents[i];
+
+ if (i + 1 < tableOfContents.size()) {
+ toc |= 0x80;
+ } else {
+ toc &= ~0x80;
+ }
+
+ data[dstOffset++] = toc;
+ }
+
+ srcOffset = 0;
+ for (size_t i = 0; i < tableOfContents.size(); ++i) {
+ uint8_t toc = tableOfContents[i];
+ unsigned FT = (toc >> 3) & 0x0f;
+ size_t frameSize = getFrameSize(isWide, FT);
+
+ ++srcOffset; // skip toc
+ memcpy(&data[dstOffset], &mediaData[srcOffset], frameSize - 1);
+ srcOffset += frameSize - 1;
+ dstOffset += frameSize - 1;
+ }
+
+ buffer->setRange(0, dstOffset);
+
+ send(buffer, false /* isRTCP */);
+
+ ++mSeqNo;
+ ++mNumRTPSent;
+ mNumRTPOctetsSent += buffer->size() - 12;
+
+ mLastRTPTime = rtpTime;
+ mLastNTPTime = GetNowNTP();
+}
+
+} // namespace android
+