| /* |
| * 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 <utils/Log.h> |
| |
| #include <media/stagefright/rtsp/ARTPWriter.h> |
| |
| #include <media/stagefright/MediaSource.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/MetaData.h> |
| #include <utils/ByteOrder.h> |
| |
| #include <fcntl.h> |
| #include <strings.h> |
| |
| #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 H265_NALU_MASK 0x3F |
| #define H265_NALU_VPS 0x20 |
| #define H265_NALU_SPS 0x21 |
| #define H265_NALU_PPS 0x22 |
| |
| #define IPV4_HEADER_SIZE 20 |
| #define IPV6_HEADER_SIZE 40 |
| #define UDP_HEADER_SIZE 8 |
| #define TCPIPV4_HEADER_SIZE (IPV4_HEADER_SIZE + UDP_HEADER_SIZE) |
| #define TCPIPV6_HEADER_SIZE (IPV6_HEADER_SIZE + UDP_HEADER_SIZE) |
| #define TCPIP_HEADER_SIZE TCPIPV4_HEADER_SIZE |
| #define RTP_HEADER_SIZE 12 |
| #define RTP_HEADER_EXT_SIZE 8 |
| #define RTP_FU_HEADER_SIZE 2 |
| #define RTP_PAYLOAD_ROOM_SIZE 100 // ROOM size for IPv6 header, ESP and etc. |
| |
| |
| namespace android { |
| |
| // static const size_t kMaxPacketSize = 65507; // maximum payload in UDP over IP |
| static const size_t kMaxPacketSize = 1280; |
| static char kCNAME[255] = "someone@somewhere"; |
| |
| static const size_t kTrafficRecorderMaxEntries = 128; |
| static const size_t kTrafficRecorderMaxTimeSpanMs = 2000; |
| |
| 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<ARTPWriter>(this)), |
| mTrafficRec(new TrafficRecorder<uint32_t /* Time */, Bytes>( |
| kTrafficRecorderMaxEntries, kTrafficRecorderMaxTimeSpanMs)) { |
| CHECK_GE(fd, 0); |
| mIsIPv6 = false; |
| |
| mLooper->setName("rtp writer"); |
| mLooper->registerHandler(mReflector); |
| mLooper->start(); |
| |
| 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; |
| |
| #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); |
| mVPSBuf = NULL; |
| 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, uint32_t seqNo) |
| : mFlags(0), |
| mFd(dup(fd)), |
| mLooper(new ALooper), |
| mReflector(new AHandlerReflector<ARTPWriter>(this)), |
| mTrafficRec(new TrafficRecorder<uint32_t /* Time */, Bytes>( |
| kTrafficRecorderMaxEntries, kTrafficRecorderMaxTimeSpanMs)) { |
| CHECK_GE(fd, 0); |
| mIsIPv6 = false; |
| |
| mLooper->setName("rtp writer"); |
| mLooper->registerHandler(mReflector); |
| mLooper->start(); |
| |
| makeSocketPairAndBind(localIp, localPort, remoteIp , remotePort); |
| mVPSBuf = NULL; |
| mSPSBuf = NULL; |
| mPPSBuf = NULL; |
| |
| initState(); |
| mSeqNo = seqNo; // Must use explicit # of seq for RTP continuity |
| |
| #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 (mVPSBuf != NULL) { |
| mVPSBuf->release(); |
| mVPSBuf = NULL; |
| } |
| |
| if (mSPSBuf != NULL) { |
| mSPSBuf->release(); |
| mSPSBuf = NULL; |
| } |
| |
| if (mPPSBuf != NULL) { |
| mPPSBuf->release(); |
| mPPSBuf = NULL; |
| } |
| |
| #if LOG_TO_FILES |
| close(mRTCPFd); |
| mRTCPFd = -1; |
| |
| close(mRTPFd); |
| mRTPFd = -1; |
| #endif |
| |
| close(mRTPSocket); |
| mRTPSocket = -1; |
| |
| close(mRTCPSocket); |
| mRTCPSocket = -1; |
| |
| close(mFd); |
| mFd = -1; |
| } |
| |
| void ARTPWriter::initState() { |
| if (mSourceID == 0) |
| mSourceID = rand(); |
| mPayloadType = 0; |
| if (mSeqNo == 0) |
| mSeqNo = UniformRand(65536); |
| mRTPTimeBase = 0; |
| mNumRTPSent = 0; |
| mNumRTPOctetsSent = 0; |
| |
| mOpponentID = 0; |
| mBitrate = 192000; |
| |
| mNumSRsSent = 0; |
| mRTPCVOExtMap = -1; |
| mRTPCVODegrees = 0; |
| mRTPSockNetwork = 0; |
| |
| mMode = INVALID; |
| mClockRate = 16000; |
| } |
| |
| 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; |
| initState(); |
| |
| const char *mime; |
| CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime)); |
| |
| int32_t selfID = 0; |
| if (params->findInt32(kKeySelfID, &selfID)) |
| mSourceID = selfID; |
| |
| int32_t payloadType = 0; |
| if (params->findInt32(kKeyPayloadType, &payloadType)) |
| mPayloadType = payloadType; |
| |
| int32_t rtpExtMap = 0; |
| if (params->findInt32(kKeyRtpExtMap, &rtpExtMap)) |
| mRTPCVOExtMap = rtpExtMap; |
| |
| int32_t rtpCVODegrees = 0; |
| if (params->findInt32(kKeyRtpCvoDegrees, &rtpCVODegrees)) |
| mRTPCVODegrees = rtpCVODegrees; |
| |
| int32_t dscp = 0; |
| if (params->findInt32(kKeyRtpDscp, &dscp)) |
| updateSocketDscp(dscp); |
| |
| int64_t sockNetwork = 0; |
| if (params->findInt64(kKeySocketNetwork, &sockNetwork)) |
| updateSocketNetwork(sockNetwork); |
| |
| if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { |
| // rfc6184: RTP Payload Format for H.264 Video |
| // The clock rate in the "a=rtpmap" line MUST be 90000. |
| mMode = H264; |
| mClockRate = 90000; |
| } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) { |
| // rfc7798: RTP Payload Format for High Efficiency Video Coding (HEVC) |
| // The clock rate in the "a=rtpmap" line MUST be 90000. |
| mMode = H265; |
| mClockRate = 90000; |
| } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_H263)) { |
| mMode = H263; |
| // rfc4629: RTP Payload Format for ITU-T Rec. H.263 Video |
| // The clock rate in the "a=rtpmap" line MUST be 90000. |
| mClockRate = 90000; |
| } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) { |
| mMode = AMR_NB; |
| // rfc4867: RTP Payload Format ... (AMR) and (AMR-WB) |
| // The RTP clock rate in "a=rtpmap" MUST be 8000 for AMR and 16000 for AMR-WB |
| mClockRate = 8000; |
| } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) { |
| mMode = AMR_WB; |
| mClockRate = 16000; |
| } else { |
| TRESPASS(); |
| } |
| |
| (new AMessage(kWhatStart, mReflector))->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))->post(); |
| |
| while (mFlags & kFlagStarted) { |
| mCondition.wait(mLock); |
| } |
| return OK; |
| } |
| |
| status_t ARTPWriter::pause() { |
| return OK; |
| } |
| |
| static void StripStartcode(MediaBufferBase *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); |
| } |
| } |
| |
| static const uint8_t SPCSize = 4; // Start Prefix Code Size |
| static const uint8_t startPrefixCode[SPCSize] = {0, 0, 0, 1}; |
| static const uint8_t spcKMPidx[SPCSize] = {0, 0, 2, 0}; |
| static void SpsPpsParser(MediaBufferBase *buffer, |
| MediaBufferBase **spsBuffer, MediaBufferBase **ppsBuffer) { |
| |
| while (buffer->range_length() > 0) { |
| const uint8_t *NALPtr = (const uint8_t *)buffer->data() + buffer->range_offset(); |
| uint8_t nalType = (*NALPtr) & H264_NALU_MASK; |
| |
| MediaBufferBase **targetPtr = NULL; |
| if (nalType == H264_NALU_SPS) { |
| targetPtr = spsBuffer; |
| } else if (nalType == H264_NALU_PPS) { |
| targetPtr = ppsBuffer; |
| } else { |
| return; |
| } |
| ALOGV("SPS(7) or PPS(8) found. Type %d", nalType); |
| |
| uint32_t bufferSize = buffer->range_length(); |
| MediaBufferBase *&target = *targetPtr; |
| uint32_t i = 0, j = 0; |
| bool isBoundFound = false; |
| for (i = 0; i < bufferSize; i++) { |
| while (j > 0 && NALPtr[i] != startPrefixCode[j]) { |
| j = spcKMPidx[j - 1]; |
| } |
| if (NALPtr[i] == startPrefixCode[j]) { |
| j++; |
| if (j == SPCSize) { |
| isBoundFound = true; |
| break; |
| } |
| } |
| } |
| |
| uint32_t targetSize; |
| if (target != NULL) { |
| target->release(); |
| } |
| // note that targetSize is never 0 as the first byte is never part |
| // of a start prefix |
| if (isBoundFound) { |
| targetSize = i - SPCSize + 1; |
| target = MediaBufferBase::Create(targetSize); |
| memcpy(target->data(), |
| (const uint8_t *)buffer->data() + buffer->range_offset(), |
| targetSize); |
| buffer->set_range(buffer->range_offset() + targetSize + SPCSize, |
| buffer->range_length() - targetSize - SPCSize); |
| } else { |
| targetSize = bufferSize; |
| target = MediaBufferBase::Create(targetSize); |
| memcpy(target->data(), |
| (const uint8_t *)buffer->data() + buffer->range_offset(), |
| targetSize); |
| buffer->set_range(buffer->range_offset() + bufferSize, 0); |
| return; |
| } |
| } |
| } |
| |
| static void VpsSpsPpsParser(MediaBufferBase *buffer, |
| MediaBufferBase **vpsBuffer, MediaBufferBase **spsBuffer, MediaBufferBase **ppsBuffer) { |
| |
| while (buffer->range_length() > 0) { |
| const uint8_t *NALPtr = (const uint8_t *)buffer->data() + buffer->range_offset(); |
| uint8_t nalType = ((*NALPtr) >> 1) & H265_NALU_MASK; |
| |
| MediaBufferBase **targetPtr = NULL; |
| if (nalType == H265_NALU_VPS) { |
| targetPtr = vpsBuffer; |
| } else if (nalType == H265_NALU_SPS) { |
| targetPtr = spsBuffer; |
| } else if (nalType == H265_NALU_PPS) { |
| targetPtr = ppsBuffer; |
| } else { |
| return; |
| } |
| ALOGV("VPS(32) SPS(33) or PPS(34) found. Type %d", nalType); |
| |
| uint32_t bufferSize = buffer->range_length(); |
| MediaBufferBase *&target = *targetPtr; |
| uint32_t i = 0, j = 0; |
| bool isBoundFound = false; |
| for (i = 0; i < bufferSize; i++) { |
| while (j > 0 && NALPtr[i] != startPrefixCode[j]) { |
| j = spcKMPidx[j - 1]; |
| } |
| if (NALPtr[i] == startPrefixCode[j]) { |
| j++; |
| if (j == SPCSize) { |
| isBoundFound = true; |
| break; |
| } |
| } |
| } |
| |
| uint32_t targetSize; |
| if (target != NULL) { |
| target->release(); |
| } |
| // note that targetSize is never 0 as the first byte is never part |
| // of a start prefix |
| if (isBoundFound) { |
| targetSize = i - SPCSize + 1; |
| target = MediaBufferBase::Create(targetSize); |
| memcpy(target->data(), |
| (const uint8_t *)buffer->data() + buffer->range_offset(), |
| targetSize); |
| buffer->set_range(buffer->range_offset() + targetSize + SPCSize, |
| buffer->range_length() - targetSize - SPCSize); |
| } else { |
| targetSize = bufferSize; |
| target = MediaBufferBase::Create(targetSize); |
| memcpy(target->data(), |
| (const uint8_t *)buffer->data() + buffer->range_offset(), |
| targetSize); |
| buffer->set_range(buffer->range_offset() + bufferSize, 0); |
| return; |
| } |
| } |
| } |
| |
| void ARTPWriter::onMessageReceived(const sp<AMessage> &msg) { |
| switch (msg->what()) { |
| case kWhatStart: |
| { |
| sp<MetaData> meta = new MetaData(); |
| meta->setInt64(kKeyTime, 10ll); |
| CHECK_EQ(mSource->start(meta.get()), (status_t)OK); |
| |
| #if 0 |
| if (mMode == H264) { |
| MediaBufferBase *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))->post(); |
| (new AMessage(kWhatSendSR, mReflector))->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::setTMMBNInfo(uint32_t opponentID, uint32_t bitrate) { |
| mOpponentID = opponentID; |
| mBitrate = bitrate; |
| |
| sp<ABuffer> buffer = new ABuffer(65536); |
| buffer->setRange(0, 0); |
| |
| addTMMBN(buffer); |
| |
| send(buffer, true /* isRTCP */); |
| } |
| |
| void ARTPWriter::onRead(const sp<AMessage> &msg) { |
| MediaBufferBase *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); |
| SpsPpsParser(mediaBuf, &mSPSBuf, &mPPSBuf); |
| if (mediaBuf->range_length() > 0) { |
| sendAVCData(mediaBuf); |
| } |
| } else if (mMode == H265) { |
| StripStartcode(mediaBuf); |
| VpsSpsPpsParser(mediaBuf, &mVPSBuf, &mSPSBuf, &mPPSBuf); |
| if (mediaBuf->range_length() > 0) { |
| sendHEVCData(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) { |
| int sizeSockSt; |
| struct sockaddr *remAddr; |
| |
| if (mIsIPv6) { |
| sizeSockSt = sizeof(struct sockaddr_in6); |
| if (isRTCP) |
| remAddr = (struct sockaddr *)&mRTCPAddr6; |
| else |
| remAddr = (struct sockaddr *)&mRTPAddr6; |
| } else { |
| sizeSockSt = sizeof(struct sockaddr_in); |
| if (isRTCP) |
| remAddr = (struct sockaddr *)&mRTCPAddr; |
| else |
| remAddr = (struct sockaddr *)&mRTPAddr; |
| } |
| |
| // Unseal code if moderator is needed (prevent overflow of instant bandwidth) |
| // Set limit bits per period through the moderator. |
| // ex) 6KByte/10ms = 48KBit/10ms = 4.8MBit/s instant limit |
| // ModerateInstantTraffic(10, 6 * 1024); |
| |
| ssize_t n = sendto(isRTCP ? mRTCPSocket : mRTPSocket, |
| buffer->data(), buffer->size(), 0, remAddr, sizeSockSt); |
| |
| if (n != (ssize_t)buffer->size()) { |
| ALOGW("packets can not be sent. ret=%d, buf=%d", (int)n, (int)buffer->size()); |
| } else { |
| // Record current traffic & Print bits while last 1sec (1000ms) |
| mTrafficRec->writeBytes(buffer->size() + |
| (mIsIPv6 ? TCPIPV6_HEADER_SIZE : TCPIPV4_HEADER_SIZE)); |
| mTrafficRec->printAccuBitsForLastPeriod(1000, 1000); |
| } |
| |
| #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; |
| |
| uint64_t ntpTime = GetNowNTP(); |
| data[8] = ntpTime >> (64 - 8); |
| data[9] = (ntpTime >> (64 - 16)) & 0xff; |
| data[10] = (ntpTime >> (64 - 24)) & 0xff; |
| data[11] = (ntpTime >> 32) & 0xff; |
| data[12] = (ntpTime >> 24) & 0xff; |
| data[13] = (ntpTime >> 16) & 0xff; |
| data[14] = (ntpTime >> 8) & 0xff; |
| data[15] = ntpTime & 0xff; |
| |
| // A current rtpTime can be calculated from ALooper::GetNowUs(). |
| // This is expecting a timestamp of raw frame from a media source is |
| // on the same time context across components in android media framework |
| // which can be queried by ALooper::GetNowUs(). |
| // In other words, ALooper::GetNowUs() is on the same timeline as the time |
| // of kKeyTime in a MediaBufferBase |
| uint32_t rtpTime = getRtpTime(ALooper::GetNowUs()); |
| data[16] = (rtpTime >> 24) & 0xff; |
| data[17] = (rtpTime >> 16) & 0xff; |
| data[18] = (rtpTime >> 8) & 0xff; |
| data[19] = rtpTime & 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 |
| |
| 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; |
| [[fallthrough]]; |
| case 2: |
| data[offset++] = 0; |
| [[fallthrough]]; |
| 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); |
| } |
| |
| void ARTPWriter::addTMMBN(const sp<ABuffer> &buffer) { |
| if (buffer->size() + 20 > buffer->capacity()) { |
| ALOGW("RTCP buffer too small to accommodate SR."); |
| return; |
| } |
| if (mOpponentID == 0) |
| return; |
| |
| uint8_t *data = buffer->data() + buffer->size(); |
| |
| data[0] = 0x80 | 4; // TMMBN |
| data[1] = 205; // TSFB |
| data[2] = 0; |
| data[3] = 4; // total (4+1) * sizeof(int32_t) = 20 bytes |
| data[4] = mSourceID >> 24; |
| data[5] = (mSourceID >> 16) & 0xff; |
| data[6] = (mSourceID >> 8) & 0xff; |
| data[7] = mSourceID & 0xff; |
| |
| *(int32_t*)(&data[8]) = 0; // 4 bytes blank |
| |
| data[12] = mOpponentID >> 24; |
| data[13] = (mOpponentID >> 16) & 0xff; |
| data[14] = (mOpponentID >> 8) & 0xff; |
| data[15] = mOpponentID & 0xff; |
| |
| // Find the first bit '1' from left & right side of the value. |
| int32_t leftEnd = 31 - __builtin_clz(mBitrate); |
| int32_t rightEnd = ffs(mBitrate) - 1; |
| |
| // Mantissa have only 17bit space by RTCP specification. |
| if ((leftEnd - rightEnd) > 16) { |
| rightEnd = leftEnd - 16; |
| } |
| int32_t mantissa = mBitrate >> rightEnd; |
| |
| data[16] = ((rightEnd << 2) & 0xfc) | ((mantissa & 0x18000) >> 15); |
| data[17] = (mantissa & 0x07f80) >> 7; |
| data[18] = (mantissa & 0x0007f) << 1; |
| data[19] = 40; // 40 bytes overhead; |
| |
| buffer->setRange(buffer->offset(), buffer->size() + 20); |
| |
| ALOGI("UE -> Op Noti Tx bitrate : %d ", mantissa << rightEnd); |
| } |
| |
| // static |
| uint64_t ARTPWriter::GetNowNTP() { |
| uint64_t nowUs = systemTime(SYSTEM_TIME_REALTIME) / 1000ll; |
| |
| 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; |
| } |
| |
| uint32_t ARTPWriter::getRtpTime(int64_t timeUs) { |
| int32_t clockPerMs = mClockRate / 1000; |
| int64_t rtpTime = mRTPTimeBase + (timeUs * clockPerMs / 1000LL); |
| |
| return (uint32_t)rtpTime; |
| } |
| |
| 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(AStringPrintf("%d", mIsIPv6 ? ntohs(mRTPAddr6.sin6_port) : 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(AStringPrintf("/%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(MediaBufferBase *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 = |
| AStringPrintf("%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::sendSPSPPSIfIFrame(MediaBufferBase *mediaBuf, int64_t timeUs) { |
| CHECK(mediaBuf->range_length() > 0); |
| 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::sendVPSSPSPPSIfIFrame(MediaBufferBase *mediaBuf, int64_t timeUs) { |
| CHECK(mediaBuf->range_length() > 0); |
| const uint8_t *mediaData = |
| (const uint8_t *)mediaBuf->data() + mediaBuf->range_offset(); |
| |
| int nalType = ((mediaData[0] >> 1) & H265_NALU_MASK); |
| if (!(nalType >= 16 && nalType <= 21) /*H265_NALU_IFRAME*/) { |
| return; |
| } |
| |
| if (mVPSBuf != NULL) { |
| mVPSBuf->meta_data().setInt64(kKeyTime, timeUs); |
| mVPSBuf->meta_data().setInt32(kKeyVps, 1); |
| sendHEVCData(mVPSBuf); |
| } |
| |
| if (mSPSBuf != NULL) { |
| mSPSBuf->meta_data().setInt64(kKeyTime, timeUs); |
| mSPSBuf->meta_data().setInt32(kKeySps, 1); |
| sendHEVCData(mSPSBuf); |
| } |
| |
| if (mPPSBuf != NULL) { |
| mPPSBuf->meta_data().setInt64(kKeyTime, timeUs); |
| mPPSBuf->meta_data().setInt32(kKeyPps, 1); |
| sendHEVCData(mPPSBuf); |
| } |
| } |
| |
| void ARTPWriter::sendHEVCData(MediaBufferBase *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)); |
| |
| sendVPSSPSPPSIfIFrame(mediaBuf, timeUs); |
| |
| uint32_t rtpTime = getRtpTime(timeUs); |
| |
| CHECK(mediaBuf->range_length() > 0); |
| const uint8_t *mediaData = |
| (const uint8_t *)mediaBuf->data() + mediaBuf->range_offset(); |
| |
| int32_t isNonVCL = 0; |
| if (mediaBuf->meta_data().findInt32(kKeyVps, &isNonVCL) || |
| mediaBuf->meta_data().findInt32(kKeySps, &isNonVCL) || |
| mediaBuf->meta_data().findInt32(kKeyPps, &isNonVCL)) { |
| isNonVCL = 1; |
| } |
| |
| sp<ABuffer> buffer = new ABuffer(kMaxPacketSize); |
| if (mediaBuf->range_length() + TCPIP_HEADER_SIZE + RTP_HEADER_SIZE + RTP_HEADER_EXT_SIZE |
| + RTP_PAYLOAD_ROOM_SIZE <= buffer->capacity()) { |
| // The data fits into a single packet |
| uint8_t *data = buffer->data(); |
| data[0] = 0x80; |
| if (mRTPCVOExtMap > 0) { |
| data[0] |= 0x10; |
| } |
| if (isNonVCL) { |
| data[1] = mPayloadType; // Marker bit should not be set in case of Non-VCL |
| } else { |
| data[1] = (1 << 7) | mPayloadType; // 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; |
| |
| int rtpExtIndex = 0; |
| if (mRTPCVOExtMap > 0) { |
| /* |
| 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| | 0xBE | 0xDE | length=3 | |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| | ID | L=0 | data | ID | L=1 | data... |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| ...data | 0 (pad) | 0 (pad) | ID | L=3 | |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| | data | |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| |
| |
| In the one-byte header form of extensions, the 16-bit value required |
| by the RTP specification for a header extension, labeled in the RTP |
| specification as "defined by profile", takes the fixed bit pattern |
| 0xBEDE (the first version of this specification was written on the |
| feast day of the Venerable Bede). |
| */ |
| data[12] = 0xBE; |
| data[13] = 0xDE; |
| // put a length of RTP Extension. |
| data[14] = 0x00; |
| data[15] = 0x01; |
| // put extmap of RTP assigned for CVO. |
| data[16] = (mRTPCVOExtMap << 4) | 0x0; |
| // put image degrees as per CVO specification. |
| data[17] = mRTPCVODegrees; |
| data[18] = 0x0; |
| data[19] = 0x0; |
| rtpExtIndex = 8; |
| } |
| |
| memcpy(&data[12 + rtpExtIndex], |
| mediaData, mediaBuf->range_length()); |
| |
| buffer->setRange(0, mediaBuf->range_length() + (12 + rtpExtIndex)); |
| |
| send(buffer, false /* isRTCP */); |
| |
| ++mSeqNo; |
| ++mNumRTPSent; |
| mNumRTPOctetsSent += buffer->size() - (12 + rtpExtIndex); |
| } else { |
| // FU-A |
| |
| unsigned nalType = (mediaData[0] >> 1) & H265_NALU_MASK; |
| ALOGV("H265 nalType 0x%x, data[0]=0x%x", nalType, mediaData[0]); |
| size_t offset = 2; //H265 payload header is 16 bit. |
| |
| bool firstPacket = true; |
| while (offset < mediaBuf->range_length()) { |
| size_t size = mediaBuf->range_length() - offset; |
| bool lastPacket = true; |
| if (size + TCPIP_HEADER_SIZE + RTP_HEADER_SIZE + RTP_HEADER_EXT_SIZE + |
| RTP_FU_HEADER_SIZE + RTP_PAYLOAD_ROOM_SIZE > buffer->capacity()) { |
| lastPacket = false; |
| size = buffer->capacity() - TCPIP_HEADER_SIZE - RTP_HEADER_SIZE - |
| RTP_HEADER_EXT_SIZE - RTP_FU_HEADER_SIZE - RTP_PAYLOAD_ROOM_SIZE; |
| } |
| |
| uint8_t *data = buffer->data(); |
| data[0] = 0x80; |
| if (lastPacket && mRTPCVOExtMap > 0) { |
| data[0] |= 0x10; |
| } |
| data[1] = (lastPacket ? (1 << 7) : 0x00) | mPayloadType; // 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; |
| |
| int rtpExtIndex = 0; |
| if (lastPacket && mRTPCVOExtMap > 0) { |
| data[12] = 0xBE; |
| data[13] = 0xDE; |
| data[14] = 0x00; |
| data[15] = 0x01; |
| data[16] = (mRTPCVOExtMap << 4) | 0x0; |
| data[17] = mRTPCVODegrees; |
| data[18] = 0x0; |
| data[19] = 0x0; |
| rtpExtIndex = 8; |
| } |
| |
| /* H265 payload header is 16 bit |
| 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| |F| Type | Layer ID | TID | |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| */ |
| ALOGV("H265 payload header 0x%x %x", mediaData[0], mediaData[1]); |
| // excludes Type from 1st byte of H265 payload header. |
| data[12 + rtpExtIndex] = mediaData[0] & 0x81; |
| // fills Type as FU (49 == 0x31) |
| data[12 + rtpExtIndex] = data[12 + rtpExtIndex] | (0x31 << 1); |
| data[13 + rtpExtIndex] = mediaData[1]; |
| |
| ALOGV("H265 FU header 0x%x %x", data[12 + rtpExtIndex], data[13 + rtpExtIndex]); |
| |
| CHECK(!firstPacket || !lastPacket); |
| /* |
| FU INDICATOR HDR |
| 0 1 2 3 4 5 6 7 |
| +-+-+-+-+-+-+-+-+ |
| |S|E| Type | |
| +-+-+-+-+-+-+-+-+ |
| */ |
| |
| data[14 + rtpExtIndex] = |
| (firstPacket ? 0x80 : 0x00) |
| | (lastPacket ? 0x40 : 0x00) |
| | (nalType & H265_NALU_MASK); |
| ALOGV("H265 FU indicator 0x%x", data[14]); |
| |
| memcpy(&data[15 + rtpExtIndex], &mediaData[offset], size); |
| |
| buffer->setRange(0, 15 + rtpExtIndex + size); |
| |
| send(buffer, false /* isRTCP */); |
| |
| ++mSeqNo; |
| ++mNumRTPSent; |
| mNumRTPOctetsSent += buffer->size() - (12 + rtpExtIndex); |
| |
| firstPacket = false; |
| offset += size; |
| } |
| } |
| } |
| |
| void ARTPWriter::sendAVCData(MediaBufferBase *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)); |
| |
| sendSPSPPSIfIFrame(mediaBuf, timeUs); |
| |
| uint32_t rtpTime = getRtpTime(timeUs); |
| |
| CHECK(mediaBuf->range_length() > 0); |
| 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; |
| } |
| |
| mTrafficRec->updateClock(ALooper::GetNowUs() / 1000); |
| sp<ABuffer> buffer = new ABuffer(kMaxPacketSize); |
| if (mediaBuf->range_length() + TCPIP_HEADER_SIZE + RTP_HEADER_SIZE + RTP_HEADER_EXT_SIZE |
| + RTP_PAYLOAD_ROOM_SIZE <= buffer->capacity()) { |
| // The data fits into a single packet |
| uint8_t *data = buffer->data(); |
| data[0] = 0x80; |
| if (mRTPCVOExtMap > 0) { |
| data[0] |= 0x10; |
| } |
| if (isSpsPps) { |
| data[1] = mPayloadType; // Marker bit should not be set in case of sps/pps |
| } else { |
| data[1] = (1 << 7) | mPayloadType; |
| } |
| 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; |
| |
| int rtpExtIndex = 0; |
| if (mRTPCVOExtMap > 0) { |
| /* |
| 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| | 0xBE | 0xDE | length=3 | |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| | ID | L=0 | data | ID | L=1 | data... |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| ...data | 0 (pad) | 0 (pad) | ID | L=3 | |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| | data | |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| |
| |
| In the one-byte header form of extensions, the 16-bit value required |
| by the RTP specification for a header extension, labeled in the RTP |
| specification as "defined by profile", takes the fixed bit pattern |
| 0xBEDE (the first version of this specification was written on the |
| feast day of the Venerable Bede). |
| */ |
| data[12] = 0xBE; |
| data[13] = 0xDE; |
| // put a length of RTP Extension. |
| data[14] = 0x00; |
| data[15] = 0x01; |
| // put extmap of RTP assigned for CVO. |
| data[16] = (mRTPCVOExtMap << 4) | 0x0; |
| // put image degrees as per CVO specification. |
| data[17] = mRTPCVODegrees; |
| data[18] = 0x0; |
| data[19] = 0x0; |
| rtpExtIndex = 8; |
| } |
| |
| memcpy(&data[12 + rtpExtIndex], |
| mediaData, mediaBuf->range_length()); |
| |
| buffer->setRange(0, mediaBuf->range_length() + (12 + rtpExtIndex)); |
| |
| send(buffer, false /* isRTCP */); |
| |
| ++mSeqNo; |
| ++mNumRTPSent; |
| mNumRTPOctetsSent += buffer->size() - (12 + rtpExtIndex); |
| } else { |
| // FU-A |
| |
| unsigned nalType = mediaData[0] & H264_NALU_MASK; |
| ALOGV("H264 nalType 0x%x, data[0]=0x%x", 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 + TCPIP_HEADER_SIZE + RTP_HEADER_SIZE + RTP_HEADER_EXT_SIZE + |
| RTP_FU_HEADER_SIZE + RTP_PAYLOAD_ROOM_SIZE > buffer->capacity()) { |
| lastPacket = false; |
| size = buffer->capacity() - TCPIP_HEADER_SIZE - RTP_HEADER_SIZE - |
| RTP_HEADER_EXT_SIZE - RTP_FU_HEADER_SIZE - RTP_PAYLOAD_ROOM_SIZE; |
| } |
| |
| uint8_t *data = buffer->data(); |
| data[0] = 0x80; |
| if (lastPacket && mRTPCVOExtMap > 0) { |
| data[0] |= 0x10; |
| } |
| data[1] = (lastPacket ? (1 << 7) : 0x00) | mPayloadType; // 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; |
| |
| int rtpExtIndex = 0; |
| if (lastPacket && mRTPCVOExtMap > 0) { |
| data[12] = 0xBE; |
| data[13] = 0xDE; |
| data[14] = 0x00; |
| data[15] = 0x01; |
| data[16] = (mRTPCVOExtMap << 4) | 0x0; |
| data[17] = mRTPCVODegrees; |
| data[18] = 0x0; |
| data[19] = 0x0; |
| rtpExtIndex = 8; |
| } |
| |
| /* H264 payload header is 8 bit |
| 0 1 2 3 4 5 6 7 |
| +-+-+-+-+-+-+-+-+ |
| |F|NRI| Type | |
| +-+-+-+-+-+-+-+-+ |
| */ |
| ALOGV("H264 payload header 0x%x", mediaData[0]); |
| // excludes Type from 1st byte of H264 payload header. |
| data[12 + rtpExtIndex] = mediaData[0] & 0xe0; |
| // fills Type as FU (28 == 0x1C) |
| data[12 + rtpExtIndex] = data[12 + rtpExtIndex] | 0x1C; |
| |
| CHECK(!firstPacket || !lastPacket); |
| /* |
| FU header |
| 0 1 2 3 4 5 6 7 |
| +-+-+-+-+-+-+-+-+ |
| |S|E|R| Type | |
| +-+-+-+-+-+-+-+-+ |
| */ |
| |
| data[13 + rtpExtIndex] = |
| (firstPacket ? 0x80 : 0x00) |
| | (lastPacket ? 0x40 : 0x00) |
| | (nalType & H264_NALU_MASK); |
| ALOGV("H264 FU header 0x%x", data[13]); |
| |
| memcpy(&data[14 + rtpExtIndex], &mediaData[offset], size); |
| |
| buffer->setRange(0, 14 + rtpExtIndex + size); |
| |
| send(buffer, false /* isRTCP */); |
| |
| ++mSeqNo; |
| ++mNumRTPSent; |
| mNumRTPOctetsSent += buffer->size() - (12 + rtpExtIndex); |
| |
| firstPacket = false; |
| offset += size; |
| } |
| } |
| } |
| |
| void ARTPWriter::sendH263Data(MediaBufferBase *mediaBuf) { |
| CHECK_GE(kMaxPacketSize, 12u + 2u); |
| |
| int64_t timeUs; |
| CHECK(mediaBuf->meta_data().findInt64(kKeyTime, &timeUs)); |
| |
| uint32_t rtpTime = getRtpTime(timeUs); |
| |
| 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) | mPayloadType; // 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; |
| } |
| } |
| |
| void ARTPWriter::updateCVODegrees(int32_t cvoDegrees) { |
| Mutex::Autolock autoLock(mLock); |
| mRTPCVODegrees = cvoDegrees; |
| } |
| |
| void ARTPWriter::updatePayloadType(int32_t payloadType) { |
| Mutex::Autolock autoLock(mLock); |
| mPayloadType = payloadType; |
| } |
| |
| void ARTPWriter::updateSocketDscp(int32_t dscp) { |
| mRtpLayer3Dscp = dscp << 2; |
| |
| /* mRtpLayer3Dscp will be mapped to WMM(Wifi) as per operator's requirement */ |
| if (setsockopt(mRTPSocket, IPPROTO_IP, IP_TOS, |
| (int *)&mRtpLayer3Dscp, sizeof(mRtpLayer3Dscp)) < 0) { |
| ALOGE("failed to set dscp on rtpsock. err=%s", strerror(errno)); |
| } else { |
| ALOGD("successfully set dscp on rtpsock. opt=%d", mRtpLayer3Dscp); |
| setsockopt(mRTCPSocket, IPPROTO_IP, IP_TOS, |
| (int *)&mRtpLayer3Dscp, sizeof(mRtpLayer3Dscp)); |
| ALOGD("successfully set dscp on rtcpsock. opt=%d", mRtpLayer3Dscp); |
| } |
| } |
| |
| void ARTPWriter::updateSocketNetwork(int64_t socketNetwork) { |
| mRTPSockNetwork = (net_handle_t)socketNetwork; |
| ALOGI("trying to bind rtp socket(%d) to network(%llu).", |
| mRTPSocket, (unsigned long long)mRTPSockNetwork); |
| |
| int result = android_setsocknetwork(mRTPSockNetwork, mRTPSocket); |
| if (result != 0) { |
| ALOGW("failed(%d) to bind rtp socket(%d) to network(%llu)", |
| result, mRTPSocket, (unsigned long long)mRTPSockNetwork); |
| } |
| result = android_setsocknetwork(mRTPSockNetwork, mRTCPSocket); |
| if (result != 0) { |
| ALOGW("failed(%d) to bind rtcp socket(%d) to network(%llu)", |
| result, mRTCPSocket, (unsigned long long)mRTPSockNetwork); |
| } |
| ALOGI("done. bind rtp socket(%d) to network(%llu)", |
| mRTPSocket, (unsigned long long)mRTPSockNetwork); |
| } |
| |
| uint32_t ARTPWriter::getSequenceNum() { |
| return mSeqNo; |
| } |
| |
| uint64_t ARTPWriter::getAccumulativeBytes() { |
| return mTrafficRec->readBytesForTotal(); |
| } |
| |
| 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(MediaBufferBase *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 = getRtpTime(timeUs); |
| |
| // 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] = mPayloadType; |
| 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; |
| } |
| |
| void ARTPWriter::makeSocketPairAndBind(String8& localIp, int localPort, |
| String8& remoteIp, int remotePort) { |
| static char kSomeone[16] = "someone@"; |
| int nameLength = strlen(kSomeone); |
| memcpy(kCNAME, kSomeone, nameLength); |
| memcpy(kCNAME + nameLength, localIp.c_str(), localIp.length() + 1); |
| |
| if (localIp.contains(":")) |
| mIsIPv6 = true; |
| else |
| mIsIPv6 = false; |
| |
| mRTPSocket = socket(mIsIPv6 ? AF_INET6 : AF_INET, SOCK_DGRAM, 0); |
| CHECK_GE(mRTPSocket, 0); |
| mRTCPSocket = socket(mIsIPv6 ? AF_INET6 : AF_INET, SOCK_DGRAM, 0); |
| CHECK_GE(mRTCPSocket, 0); |
| |
| int sockopt = 1; |
| setsockopt(mRTPSocket, SOL_SOCKET, SO_REUSEADDR, (int *)&sockopt, sizeof(sockopt)); |
| setsockopt(mRTCPSocket, SOL_SOCKET, SO_REUSEADDR, (int *)&sockopt, sizeof(sockopt)); |
| |
| if (mIsIPv6) { |
| memset(&mLocalAddr6, 0, sizeof(mLocalAddr6)); |
| memset(&mRTPAddr6, 0, sizeof(mRTPAddr6)); |
| memset(&mRTCPAddr6, 0, sizeof(mRTCPAddr6)); |
| |
| mLocalAddr6.sin6_family = AF_INET6; |
| inet_pton(AF_INET6, localIp.string(), &mLocalAddr6.sin6_addr); |
| mLocalAddr6.sin6_port = htons((uint16_t)localPort); |
| |
| mRTPAddr6.sin6_family = AF_INET6; |
| inet_pton(AF_INET6, remoteIp.string(), &mRTPAddr6.sin6_addr); |
| mRTPAddr6.sin6_port = htons((uint16_t)remotePort); |
| |
| mRTCPAddr6 = mRTPAddr6; |
| mRTCPAddr6.sin6_port = htons((uint16_t)(remotePort + 1)); |
| } else { |
| memset(&mLocalAddr, 0, sizeof(mLocalAddr)); |
| memset(&mRTPAddr, 0, sizeof(mRTPAddr)); |
| memset(&mRTCPAddr, 0, sizeof(mRTCPAddr)); |
| |
| mLocalAddr.sin_family = AF_INET; |
| mLocalAddr.sin_addr.s_addr = inet_addr(localIp.string()); |
| mLocalAddr.sin_port = htons((uint16_t)localPort); |
| |
| mRTPAddr.sin_family = AF_INET; |
| mRTPAddr.sin_addr.s_addr = inet_addr(remoteIp.string()); |
| mRTPAddr.sin_port = htons((uint16_t)remotePort); |
| |
| mRTCPAddr = mRTPAddr; |
| mRTCPAddr.sin_port = htons((uint16_t)(remotePort + 1)); |
| } |
| |
| struct sockaddr *localAddr = mIsIPv6 ? |
| (struct sockaddr*)&mLocalAddr6 : (struct sockaddr*)&mLocalAddr; |
| |
| int sizeSockSt = mIsIPv6 ? sizeof(mLocalAddr6) : sizeof(mLocalAddr); |
| |
| if (bind(mRTPSocket, localAddr, sizeSockSt) == -1) { |
| ALOGE("failed to bind rtp %s:%d err=%s", localIp.string(), localPort, strerror(errno)); |
| } else { |
| ALOGD("succeed to bind rtp %s:%d", localIp.string(), localPort); |
| } |
| |
| if (mIsIPv6) |
| mLocalAddr6.sin6_port = htons((uint16_t)(localPort + 1)); |
| else |
| mLocalAddr.sin_port = htons((uint16_t)(localPort + 1)); |
| |
| if (bind(mRTCPSocket, localAddr, sizeSockSt) == -1) { |
| ALOGE("failed to bind rtcp %s:%d err=%s", localIp.string(), localPort + 1, strerror(errno)); |
| } else { |
| ALOGD("succeed to bind rtcp %s:%d", localIp.string(), localPort + 1); |
| } |
| } |
| |
| // TODO : Develop more advanced moderator based on AS & TMMBR value |
| void ARTPWriter::ModerateInstantTraffic(uint32_t samplePeriod, uint32_t limitBytes) { |
| unsigned int bytes = mTrafficRec->readBytesForLastPeriod(samplePeriod); |
| if (bytes > limitBytes) { |
| ALOGI("Nuclear moderator. #seq = %d \t\t %d bits / 10ms", |
| mSeqNo, bytes * 8); |
| usleep(4000); |
| mTrafficRec->updateClock(ALooper::GetNowUs() / 1000); |
| } |
| } |
| |
| } // namespace android |