/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //#define LOG_NDEBUG 0 #define LOG_TAG "ARTPWriter" #include #include "ARTPWriter.h" #include #include #include #include #include #include #include #include #include #include #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(dup(fd)), mLooper(new ALooper), mReflector(new AHandlerReflector(this)) { CHECK_GE(fd, 0); mLooper->setName("rtp writer"); 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.18.246"); #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 &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; } status_t ARTPWriter::stop() { Mutex::Autolock autoLock(mLock); if (!(mFlags & kFlagStarted)) { return OK; } (new AMessage(kWhatStop, mReflector->id()))->post(); while (mFlags & kFlagStarted) { mCondition.wait(mLock); } return OK; } status_t ARTPWriter::pause() { return OK; } 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 &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 &msg) { MediaBuffer *mediaBuf; status_t err = mSource->read(&mediaBuf); if (err != OK) { ALOGI("reached EOS."); Mutex::Autolock autoLock(mLock); mFlags |= kFlagEOS; return; } if (mediaBuf->range_length() > 0) { ALOGV("read buffer of size %zu", 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 &msg) { sp 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 &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 &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 &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 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"); } ALOGI("%s", sdp.c_str()); } 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 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 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 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 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 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