sp<AnotherPacketSource> mSource;
bool mPayloadStarted;
+ uint64_t mPrevPTS;
+
ElementaryStreamQueue *mQueue;
status_t flush();
mPCR_PID(PCR_PID),
mExpectedContinuityCounter(-1),
mPayloadStarted(false),
+ mPrevPTS(0),
mQueue(NULL) {
switch (mStreamType) {
case STREAMTYPE_H264:
ElementaryStreamQueue::MPEG4_VIDEO);
break;
+ case STREAMTYPE_PCM_AUDIO:
+ mQueue = new ElementaryStreamQueue(
+ ElementaryStreamQueue::PCM_AUDIO);
+ break;
+
default:
break;
}
case STREAMTYPE_MPEG1_AUDIO:
case STREAMTYPE_MPEG2_AUDIO:
case STREAMTYPE_MPEG2_AUDIO_ADTS:
+ case STREAMTYPE_PCM_AUDIO:
return true;
default:
void ATSParser::Stream::onPayloadData(
unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS,
const uint8_t *data, size_t size) {
+#if 0
+ ALOGI("payload streamType 0x%02x, PTS = 0x%016llx, dPTS = %lld",
+ mStreamType,
+ PTS,
+ (int64_t)PTS - mPrevPTS);
+ mPrevPTS = PTS;
+#endif
+
ALOGV("onPayloadData mStreamType=0x%02x", mStreamType);
int64_t timeUs = 0ll; // no presentation timestamp available.
STREAMTYPE_MPEG2_AUDIO_ADTS = 0x0f,
STREAMTYPE_MPEG4_VIDEO = 0x10,
STREAMTYPE_H264 = 0x1b,
+ STREAMTYPE_PCM_AUDIO = 0x83,
};
protected:
#include "include/avc_utils.h"
+#include <netinet/in.h>
+
namespace android {
ElementaryStreamQueue::ElementaryStreamQueue(Mode mode, uint32_t flags)
break;
}
+ case PCM_AUDIO:
+ {
+ break;
+ }
+
default:
TRESPASS();
break;
return dequeueAccessUnitMPEGVideo();
case MPEG4_VIDEO:
return dequeueAccessUnitMPEG4Video();
+ case PCM_AUDIO:
+ return dequeueAccessUnitPCMAudio();
default:
CHECK_EQ((unsigned)mMode, (unsigned)MPEG_AUDIO);
return dequeueAccessUnitMPEGAudio();
}
}
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitPCMAudio() {
+ if (mBuffer->size() < 4) {
+ return NULL;
+ }
+
+ ABitReader bits(mBuffer->data(), 4);
+ CHECK_EQ(bits.getBits(8), 0xa0);
+ unsigned numAUs = bits.getBits(8);
+ bits.skipBits(8);
+ unsigned quantization_word_length = bits.getBits(2);
+ unsigned audio_sampling_frequency = bits.getBits(3);
+ unsigned num_channels = bits.getBits(3);
+
+ CHECK_EQ(audio_sampling_frequency, 2); // 48kHz
+ CHECK_EQ(num_channels, 1u); // stereo!
+
+ if (mFormat == NULL) {
+ mFormat = new MetaData;
+ mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+ mFormat->setInt32(kKeyChannelCount, 2);
+ mFormat->setInt32(kKeySampleRate, 48000);
+ }
+
+ static const size_t kFramesPerAU = 80;
+ size_t frameSize = 2 /* numChannels */ * sizeof(int16_t);
+
+ size_t payloadSize = numAUs * frameSize * kFramesPerAU;
+
+ if (mBuffer->size() < 4 + payloadSize) {
+ return NULL;
+ }
+
+ sp<ABuffer> accessUnit = new ABuffer(payloadSize);
+ memcpy(accessUnit->data(), mBuffer->data() + 4, payloadSize);
+
+ int64_t timeUs = fetchTimestamp(payloadSize + 4);
+ CHECK_GE(timeUs, 0ll);
+ accessUnit->meta()->setInt64("timeUs", timeUs);
+
+ int16_t *ptr = (int16_t *)accessUnit->data();
+ for (size_t i = 0; i < payloadSize / sizeof(int16_t); ++i) {
+ ptr[i] = ntohs(ptr[i]);
+ }
+
+ memmove(
+ mBuffer->data(),
+ mBuffer->data() + 4 + payloadSize,
+ mBuffer->size() - 4 - payloadSize);
+
+ mBuffer->setRange(0, mBuffer->size() - 4 - payloadSize);
+
+ return accessUnit;
+}
+
sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAAC() {
int64_t timeUs;
MPEG_AUDIO,
MPEG_VIDEO,
MPEG4_VIDEO,
+ PCM_AUDIO,
};
enum Flags {
sp<ABuffer> dequeueAccessUnitMPEGAudio();
sp<ABuffer> dequeueAccessUnitMPEGVideo();
sp<ABuffer> dequeueAccessUnitMPEG4Video();
+ sp<ABuffer> dequeueAccessUnitPCMAudio();
// consume a logical (compressed) access unit of size "size",
// returns its timestamp in us (or -1 if no time information).
sp<AMessage> mNotify;
bool mSawReceiveFailure, mSawSendFailure;
+ // for TCP / stream data
AString mOutBuffer;
- List<size_t> mOutBufferSizes;
+
+ // for UDP / datagrams
+ List<sp<ABuffer> > mOutDatagrams;
AString mInBuffer;
bool ANetworkSession::Session::wantsToWrite() {
return !mSawSendFailure
&& (mState == CONNECTING
- || ((mState == CONNECTED || mState == DATAGRAM)
- && !mOutBuffer.empty()));
+ || (mState == CONNECTED && !mOutBuffer.empty())
+ || (mState == DATAGRAM && !mOutDatagrams.empty()));
}
status_t ANetworkSession::Session::readMore() {
status_t ANetworkSession::Session::writeMore() {
if (mState == DATAGRAM) {
- CHECK(!mOutBufferSizes.empty());
+ CHECK(!mOutDatagrams.empty());
status_t err;
do {
- size_t size = *mOutBufferSizes.begin();
-
- CHECK_GE(mOutBuffer.size(), size);
+ const sp<ABuffer> &datagram = *mOutDatagrams.begin();
int n;
do {
- n = send(mSocket, mOutBuffer.c_str(), size, 0);
+ n = send(mSocket, datagram->data(), datagram->size(), 0);
} while (n < 0 && errno == EINTR);
err = OK;
if (n > 0) {
- mOutBufferSizes.erase(mOutBufferSizes.begin());
- mOutBuffer.erase(0, n);
+ mOutDatagrams.erase(mOutDatagrams.begin());
} else if (n < 0) {
err = -errno;
} else if (n == 0) {
err = -ECONNRESET;
}
- } while (err == OK && !mOutBufferSizes.empty());
+ } while (err == OK && !mOutDatagrams.empty());
if (err == -EAGAIN) {
err = OK;
status_t ANetworkSession::Session::sendRequest(const void *data, ssize_t size) {
CHECK(mState == CONNECTED || mState == DATAGRAM);
+ if (mState == DATAGRAM) {
+ CHECK_GE(size, 0);
+
+ sp<ABuffer> datagram = new ABuffer(size);
+ memcpy(datagram->data(), data, size);
+
+ mOutDatagrams.push_back(datagram);
+ return OK;
+ }
+
if (mState == CONNECTED && !mIsRTSPConnection) {
CHECK_LE(size, 65535);
(const char *)data,
(size >= 0) ? size : strlen((const char *)data));
- if (mState == DATAGRAM) {
- CHECK_GE(size, 0);
- mOutBufferSizes.push_back(size);
- }
-
return OK;
}
source/MediaPuller.cpp \
source/PlaybackSession.cpp \
source/RepeaterSource.cpp \
+ source/Sender.cpp \
source/TSPacketizer.cpp \
source/WifiDisplaySource.cpp \
timeUs += copyUs;
buffer->meta()->setInt64("timeUs", timeUs);
- if (copy == partialAudioAU->size() - 4) {
+ if (copy == partialAudioAU->capacity() - 4) {
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatAccessUnit);
notify->setBuffer("accessUnit", partialAudioAU);
#include "Converter.h"
#include "MediaPuller.h"
#include "RepeaterSource.h"
+#include "Sender.h"
#include "TSPacketizer.h"
#include "include/avc_utils.h"
namespace android {
-static size_t kMaxRTPPacketSize = 1500;
-static size_t kMaxNumTSPacketsPerRTPPacket = (kMaxRTPPacketSize - 12) / 188;
-
struct WifiDisplaySource::PlaybackSession::Track : public AHandler {
enum {
kWhatStopped,
void queueAccessUnit(const sp<ABuffer> &accessUnit);
sp<ABuffer> dequeueAccessUnit();
+ bool hasOutputBuffer(int64_t *timeUs) const;
+ void queueOutputBuffer(const sp<ABuffer> &accessUnit);
+ sp<ABuffer> dequeueOutputBuffer();
+ bool isSuspended() const;
+
+ size_t countQueuedOutputBuffers() const {
+ return mQueuedOutputBuffers.size();
+ }
+
void requestIDRFrame();
protected:
bool mIsAudio;
List<sp<ABuffer> > mQueuedAccessUnits;
sp<RepeaterSource> mRepeaterSource;
+ List<sp<ABuffer> > mQueuedOutputBuffers;
+ int64_t mLastOutputBufferQueuedTimeUs;
static bool IsAudioFormat(const sp<AMessage> &format);
mConverter(converter),
mStarted(false),
mPacketizerTrackIndex(-1),
- mIsAudio(IsAudioFormat(mConverter->getOutputFormat())) {
+ mIsAudio(IsAudioFormat(mConverter->getOutputFormat())),
+ mLastOutputBufferQueuedTimeUs(-1ll) {
}
WifiDisplaySource::PlaybackSession::Track::~Track() {
mConverter->requestIDRFrame();
}
+bool WifiDisplaySource::PlaybackSession::Track::hasOutputBuffer(
+ int64_t *timeUs) const {
+ *timeUs = 0ll;
+
+ if (mQueuedOutputBuffers.empty()) {
+ return false;
+ }
+
+ const sp<ABuffer> &outputBuffer = *mQueuedOutputBuffers.begin();
+
+ CHECK(outputBuffer->meta()->findInt64("timeUs", timeUs));
+
+ return true;
+}
+
+void WifiDisplaySource::PlaybackSession::Track::queueOutputBuffer(
+ const sp<ABuffer> &accessUnit) {
+ mQueuedOutputBuffers.push_back(accessUnit);
+
+ mLastOutputBufferQueuedTimeUs = ALooper::GetNowUs();
+}
+
+sp<ABuffer> WifiDisplaySource::PlaybackSession::Track::dequeueOutputBuffer() {
+ CHECK(!mQueuedOutputBuffers.empty());
+
+ sp<ABuffer> outputBuffer = *mQueuedOutputBuffers.begin();
+ mQueuedOutputBuffers.erase(mQueuedOutputBuffers.begin());
+
+ return outputBuffer;
+}
+
+bool WifiDisplaySource::PlaybackSession::Track::isSuspended() const {
+ if (!mQueuedOutputBuffers.empty()) {
+ return false;
+ }
+
+ if (mLastOutputBufferQueuedTimeUs < 0ll) {
+ // We've never seen an output buffer queued, but tracks start
+ // out live, not suspended.
+ return false;
+ }
+
+ // If we've not seen new output data for 60ms or more, we consider
+ // this track suspended for the time being.
+ return (ALooper::GetNowUs() - mLastOutputBufferQueuedTimeUs) > 60000ll;
+}
+
////////////////////////////////////////////////////////////////////////////////
WifiDisplaySource::PlaybackSession::PlaybackSession(
mWeAreDead(false),
mLastLifesignUs(),
mVideoTrackIndex(-1),
- mTSQueue(new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188)),
mPrevTimeUs(-1ll),
- mTransportMode(TRANSPORT_UDP),
- mAllTracksHavePacketizerIndex(false),
- mRTPChannel(0),
- mRTCPChannel(0),
- mRTPPort(0),
- mRTPSessionID(0),
- mRTCPSessionID(0),
-#if ENABLE_RETRANSMISSION
- mRTPRetransmissionSessionID(0),
- mRTCPRetransmissionSessionID(0),
-#endif
- mClientRTPPort(0),
- mClientRTCPPort(0),
- mRTPConnected(false),
- mRTCPConnected(false),
- mRTPSeqNo(0),
-#if ENABLE_RETRANSMISSION
- mRTPRetransmissionSeqNo(0),
-#endif
- mLastNTPTime(0),
- mLastRTPTime(0),
- mNumRTPSent(0),
- mNumRTPOctetsSent(0),
- mNumSRsSent(0),
- mSendSRPending(false)
-#if ENABLE_RETRANSMISSION
- ,mHistoryLength(0)
-#endif
-#if TRACK_BANDWIDTH
- ,mFirstPacketTimeUs(-1ll)
- ,mTotalBytesSent(0ll)
-#endif
-#if LOG_TRANSPORT_STREAM
- ,mLogFile(NULL)
-#endif
-{
- mTSQueue->setRange(0, 12);
-
-#if LOG_TRANSPORT_STREAM
- mLogFile = fopen("/system/etc/log.ts", "wb");
-#endif
+ mAllTracksHavePacketizerIndex(false) {
}
status_t WifiDisplaySource::PlaybackSession::init(
const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
- TransportMode transportMode,
+ Sender::TransportMode transportMode,
bool usePCMAudio) {
- mClientIP = clientIP;
-
status_t err = setupPacketizer(usePCMAudio);
if (err != OK) {
return err;
}
- mTransportMode = transportMode;
-
- if (transportMode == TRANSPORT_TCP_INTERLEAVED) {
- mRTPChannel = clientRtp;
- mRTCPChannel = clientRtcp;
- mRTPPort = 0;
- mRTPSessionID = 0;
- mRTCPSessionID = 0;
-
- updateLiveness();
- return OK;
- }
-
- mRTPChannel = 0;
- mRTCPChannel = 0;
-
- if (mTransportMode == TRANSPORT_TCP) {
- // XXX This is wrong, we need to allocate sockets here, we only
- // need to do this because the dongles are not establishing their
- // end until after PLAY instead of before SETUP.
- mRTPPort = 20000;
- mRTPSessionID = 0;
- mRTCPSessionID = 0;
- mClientRTPPort = clientRtp;
- mClientRTCPPort = clientRtcp;
-
- updateLiveness();
- return OK;
- }
-
- int serverRtp;
+ sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
+ mSender = new Sender(mNetSession, notify);
- sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
- sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id());
+ mSenderLooper = new ALooper;
+ mSenderLooper->setName("sender_looper");
-#if ENABLE_RETRANSMISSION
- sp<AMessage> rtpRetransmissionNotify =
- new AMessage(kWhatRTPRetransmissionNotify, id());
-
- sp<AMessage> rtcpRetransmissionNotify =
- new AMessage(kWhatRTCPRetransmissionNotify, id());
-#endif
-
- for (serverRtp = 15550;; serverRtp += 2) {
- int32_t rtpSession;
- if (mTransportMode == TRANSPORT_UDP) {
- err = mNetSession->createUDPSession(
- serverRtp, clientIP, clientRtp,
- rtpNotify, &rtpSession);
- } else {
- err = mNetSession->createTCPDatagramSession(
- serverRtp, clientIP, clientRtp,
- rtpNotify, &rtpSession);
- }
-
- if (err != OK) {
- ALOGI("failed to create RTP socket on port %d", serverRtp);
- continue;
- }
-
- int32_t rtcpSession = 0;
-
- if (clientRtcp >= 0) {
- if (mTransportMode == TRANSPORT_UDP) {
- err = mNetSession->createUDPSession(
- serverRtp + 1, clientIP, clientRtcp,
- rtcpNotify, &rtcpSession);
- } else {
- err = mNetSession->createTCPDatagramSession(
- serverRtp + 1, clientIP, clientRtcp,
- rtcpNotify, &rtcpSession);
- }
-
- if (err != OK) {
- ALOGI("failed to create RTCP socket on port %d", serverRtp + 1);
-
- mNetSession->destroySession(rtpSession);
- continue;
- }
- }
-
-#if ENABLE_RETRANSMISSION
- if (mTransportMode == TRANSPORT_UDP) {
- int32_t rtpRetransmissionSession;
-
- err = mNetSession->createUDPSession(
- serverRtp + kRetransmissionPortOffset,
- clientIP,
- clientRtp + kRetransmissionPortOffset,
- rtpRetransmissionNotify,
- &rtpRetransmissionSession);
-
- if (err != OK) {
- mNetSession->destroySession(rtcpSession);
- mNetSession->destroySession(rtpSession);
- continue;
- }
-
- CHECK_GE(clientRtcp, 0);
-
- int32_t rtcpRetransmissionSession;
- err = mNetSession->createUDPSession(
- serverRtp + 1 + kRetransmissionPortOffset,
- clientIP,
- clientRtp + 1 + kRetransmissionPortOffset,
- rtcpRetransmissionNotify,
- &rtcpRetransmissionSession);
-
- if (err != OK) {
- mNetSession->destroySession(rtpRetransmissionSession);
- mNetSession->destroySession(rtcpSession);
- mNetSession->destroySession(rtpSession);
- continue;
- }
-
- mRTPRetransmissionSessionID = rtpRetransmissionSession;
- mRTCPRetransmissionSessionID = rtcpRetransmissionSession;
-
- ALOGI("rtpRetransmissionSessionID = %d, "
- "rtcpRetransmissionSessionID = %d",
- rtpRetransmissionSession, rtcpRetransmissionSession);
- }
-#endif
+ mSenderLooper->start(
+ false /* runOnCallingThread */,
+ false /* canCallJava */,
+ PRIORITY_AUDIO);
- mRTPPort = serverRtp;
- mRTPSessionID = rtpSession;
- mRTCPSessionID = rtcpSession;
+ mSenderLooper->registerHandler(mSender);
- ALOGI("rtpSessionID = %d, rtcpSessionID = %d", rtpSession, rtcpSession);
- break;
- }
+ err = mSender->init(clientIP, clientRtp, clientRtcp, transportMode);
- if (mRTPPort == 0) {
- return UNKNOWN_ERROR;
+ if (err != OK) {
+ return err;
}
updateLiveness();
}
WifiDisplaySource::PlaybackSession::~PlaybackSession() {
-#if LOG_TRANSPORT_STREAM
- if (mLogFile != NULL) {
- fclose(mLogFile);
- mLogFile = NULL;
- }
-#endif
}
int32_t WifiDisplaySource::PlaybackSession::getRTPPort() const {
- return mRTPPort;
+ return mSender->getRTPPort();
}
int64_t WifiDisplaySource::PlaybackSession::getLastLifesignUs() const {
}
status_t WifiDisplaySource::PlaybackSession::onFinishPlay() {
- if (mTransportMode != TRANSPORT_TCP) {
- return onFinishPlay2();
- }
-
- sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
-
- status_t err = mNetSession->createTCPDatagramSession(
- mRTPPort, mClientIP.c_str(), mClientRTPPort,
- rtpNotify, &mRTPSessionID);
-
- if (err != OK) {
- return err;
- }
-
- if (mClientRTCPPort >= 0) {
- sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id());
-
- err = mNetSession->createTCPDatagramSession(
- mRTPPort + 1, mClientIP.c_str(), mClientRTCPPort,
- rtcpNotify, &mRTCPSessionID);
- }
-
- return err;
+ return mSender->finishInit();
}
status_t WifiDisplaySource::PlaybackSession::onFinishPlay2() {
- if (mRTCPSessionID != 0) {
- scheduleSendSR();
- }
+ mSender->scheduleSendSR();
for (size_t i = 0; i < mTracks.size(); ++i) {
CHECK_EQ((status_t)OK, mTracks.editValueAt(i)->start());
void WifiDisplaySource::PlaybackSession::onMessageReceived(
const sp<AMessage> &msg) {
switch (msg->what()) {
- case kWhatRTPNotify:
- case kWhatRTCPNotify:
-#if ENABLE_RETRANSMISSION
- case kWhatRTPRetransmissionNotify:
- case kWhatRTCPRetransmissionNotify:
-#endif
- {
- int32_t reason;
- CHECK(msg->findInt32("reason", &reason));
-
- switch (reason) {
- case ANetworkSession::kWhatError:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- int32_t err;
- CHECK(msg->findInt32("err", &err));
-
- int32_t errorOccuredDuringSend;
- CHECK(msg->findInt32("send", &errorOccuredDuringSend));
-
- AString detail;
- CHECK(msg->findString("detail", &detail));
-
- if ((msg->what() == kWhatRTPNotify
-#if ENABLE_RETRANSMISSION
- || msg->what() == kWhatRTPRetransmissionNotify
-#endif
- ) && !errorOccuredDuringSend) {
- // This is ok, we don't expect to receive anything on
- // the RTP socket.
- break;
- }
-
- ALOGE("An error occurred during %s in session %d "
- "(%d, '%s' (%s)).",
- errorOccuredDuringSend ? "send" : "receive",
- sessionID,
- err,
- detail.c_str(),
- strerror(-err));
-
- mNetSession->destroySession(sessionID);
-
- if (sessionID == mRTPSessionID) {
- mRTPSessionID = 0;
- } else if (sessionID == mRTCPSessionID) {
- mRTCPSessionID = 0;
- }
-#if ENABLE_RETRANSMISSION
- else if (sessionID == mRTPRetransmissionSessionID) {
- mRTPRetransmissionSessionID = 0;
- } else if (sessionID == mRTCPRetransmissionSessionID) {
- mRTCPRetransmissionSessionID = 0;
- }
-#endif
-
- notifySessionDead();
- break;
- }
-
- case ANetworkSession::kWhatDatagram:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- sp<ABuffer> data;
- CHECK(msg->findBuffer("data", &data));
-
- status_t err;
- if (msg->what() == kWhatRTCPNotify
-#if ENABLE_RETRANSMISSION
- || msg->what() == kWhatRTCPRetransmissionNotify
-#endif
- )
- {
- err = parseRTCP(data);
- }
- break;
- }
-
- case ANetworkSession::kWhatConnected:
- {
- CHECK_EQ(mTransportMode, TRANSPORT_TCP);
-
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- if (sessionID == mRTPSessionID) {
- CHECK(!mRTPConnected);
- mRTPConnected = true;
- ALOGI("RTP Session now connected.");
- } else if (sessionID == mRTCPSessionID) {
- CHECK(!mRTCPConnected);
- mRTCPConnected = true;
- ALOGI("RTCP Session now connected.");
- } else {
- TRESPASS();
- }
-
- if (mRTPConnected
- && (mClientRTCPPort < 0 || mRTCPConnected)) {
- onFinishPlay2();
- }
- break;
- }
-
- default:
- TRESPASS();
- }
- break;
- }
-
- case kWhatSendSR:
- {
- mSendSRPending = false;
-
- if (mRTCPSessionID == 0) {
- break;
- }
-
- onSendSR();
-
- scheduleSendSR();
- break;
- }
-
case kWhatConverterNotify:
{
if (mWeAreDead) {
break;
}
- status_t err = packetizeAccessUnit(trackIndex, accessUnit);
+ track->queueOutputBuffer(accessUnit);
- if (err != OK) {
- notifySessionDead();
- }
+ drainAccessUnits();
break;
} else if (what == Converter::kWhatEOS) {
CHECK_EQ(what, Converter::kWhatEOS);
break;
}
+ case kWhatSenderNotify:
+ {
+ int32_t what;
+ CHECK(msg->findInt32("what", &what));
+
+ if (what == Sender::kWhatInitDone) {
+ onFinishPlay2();
+ } else if (what == Sender::kWhatSessionDead) {
+ notifySessionDead();
+ } else {
+ TRESPASS();
+ }
+
+ break;
+ }
+
case kWhatFinishPlay:
{
onFinishPlay();
break;
}
+ mSenderLooper->unregisterHandler(mSender->id());
+ mSender.clear();
+ mSenderLooper.clear();
+
mPacketizer.clear();
-#if ENABLE_RETRANSMISSION
- if (mRTCPRetransmissionSessionID != 0) {
- mNetSession->destroySession(mRTCPRetransmissionSessionID);
- }
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatSessionDestroyed);
+ notify->post();
+ }
+ break;
+ }
- if (mRTPRetransmissionSessionID != 0) {
- mNetSession->destroySession(mRTPRetransmissionSessionID);
- }
-#endif
+ case kWhatPacketize:
+ {
+ size_t trackIndex;
+ CHECK(msg->findSize("trackIndex", &trackIndex));
- if (mRTCPSessionID != 0) {
- mNetSession->destroySession(mRTCPSessionID);
- }
+ sp<ABuffer> accessUnit;
+ CHECK(msg->findBuffer("accessUnit", &accessUnit));
- if (mRTPSessionID != 0) {
- mNetSession->destroySession(mRTPSessionID);
- }
+#if 0
+ if ((ssize_t)trackIndex == mVideoTrackIndex) {
+ int64_t nowUs = ALooper::GetNowUs();
+ static int64_t prevNowUs = 0ll;
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatSessionDestroyed);
- notify->post();
+ ALOGI("sending AU, dNowUs=%lld us", nowUs - prevNowUs);
+
+ prevNowUs = nowUs;
}
+#endif
+
break;
}
return 720;
}
-void WifiDisplaySource::PlaybackSession::scheduleSendSR() {
- if (mSendSRPending) {
- return;
- }
-
- mSendSRPending = true;
- (new AMessage(kWhatSendSR, id()))->post(kSendSRIntervalUs);
-}
-
-void WifiDisplaySource::PlaybackSession::addSR(const sp<ABuffer> &buffer) {
- uint8_t *data = buffer->data() + buffer->size();
-
- // TODO: Use macros/utility functions to clean up all the bitshifts below.
-
- data[0] = 0x80 | 0;
- data[1] = 200; // SR
- data[2] = 0;
- data[3] = 6;
- data[4] = kSourceID >> 24;
- data[5] = (kSourceID >> 16) & 0xff;
- data[6] = (kSourceID >> 8) & 0xff;
- data[7] = kSourceID & 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 WifiDisplaySource::PlaybackSession::addSDES(const sp<ABuffer> &buffer) {
- uint8_t *data = buffer->data() + buffer->size();
- data[0] = 0x80 | 1;
- data[1] = 202; // SDES
- data[4] = kSourceID >> 24;
- data[5] = (kSourceID >> 16) & 0xff;
- data[6] = (kSourceID >> 8) & 0xff;
- data[7] = kSourceID & 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 WifiDisplaySource::PlaybackSession::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 WifiDisplaySource::PlaybackSession::onSendSR() {
- sp<ABuffer> buffer = new ABuffer(1500);
- buffer->setRange(0, 0);
-
- addSR(buffer);
- addSDES(buffer);
-
- if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatBinaryData);
- notify->setInt32("channel", mRTCPChannel);
- notify->setBuffer("data", buffer);
- notify->post();
- } else {
- sendPacket(mRTCPSessionID, buffer->data(), buffer->size());
- }
-
- ++mNumSRsSent;
-}
-
-ssize_t WifiDisplaySource::PlaybackSession::appendTSData(
- const void *data, size_t size, bool timeDiscontinuity, bool flush) {
- CHECK_EQ(size, 188);
-
- CHECK_LE(mTSQueue->size() + size, mTSQueue->capacity());
-
- memcpy(mTSQueue->data() + mTSQueue->size(), data, size);
- mTSQueue->setRange(0, mTSQueue->size() + size);
-
- if (flush || mTSQueue->size() == mTSQueue->capacity()) {
- // flush
-
- int64_t nowUs = ALooper::GetNowUs();
-
-#if TRACK_BANDWIDTH
- if (mFirstPacketTimeUs < 0ll) {
- mFirstPacketTimeUs = nowUs;
- }
-#endif
-
- // 90kHz time scale
- uint32_t rtpTime = (nowUs * 9ll) / 100ll;
-
- uint8_t *rtp = mTSQueue->data();
- rtp[0] = 0x80;
- rtp[1] = 33 | (timeDiscontinuity ? (1 << 7) : 0); // M-bit
- rtp[2] = (mRTPSeqNo >> 8) & 0xff;
- rtp[3] = mRTPSeqNo & 0xff;
- rtp[4] = rtpTime >> 24;
- rtp[5] = (rtpTime >> 16) & 0xff;
- rtp[6] = (rtpTime >> 8) & 0xff;
- rtp[7] = rtpTime & 0xff;
- rtp[8] = kSourceID >> 24;
- rtp[9] = (kSourceID >> 16) & 0xff;
- rtp[10] = (kSourceID >> 8) & 0xff;
- rtp[11] = kSourceID & 0xff;
-
- ++mRTPSeqNo;
- ++mNumRTPSent;
- mNumRTPOctetsSent += mTSQueue->size() - 12;
-
- mLastRTPTime = rtpTime;
- mLastNTPTime = GetNowNTP();
-
- if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatBinaryData);
-
- sp<ABuffer> data = new ABuffer(mTSQueue->size());
- memcpy(data->data(), rtp, mTSQueue->size());
-
- notify->setInt32("channel", mRTPChannel);
- notify->setBuffer("data", data);
- notify->post();
- } else {
- sendPacket(mRTPSessionID, rtp, mTSQueue->size());
-
-#if TRACK_BANDWIDTH
- mTotalBytesSent += mTSQueue->size();
- int64_t delayUs = ALooper::GetNowUs() - mFirstPacketTimeUs;
-
- if (delayUs > 0ll) {
- ALOGI("approx. net bandwidth used: %.2f Mbit/sec",
- mTotalBytesSent * 8.0 / delayUs);
- }
-#endif
- }
-
-#if ENABLE_RETRANSMISSION
- mTSQueue->setInt32Data(mRTPSeqNo - 1);
-
- mHistory.push_back(mTSQueue);
- ++mHistoryLength;
-
- if (mHistoryLength > kMaxHistoryLength) {
- mTSQueue = *mHistory.begin();
- mHistory.erase(mHistory.begin());
-
- --mHistoryLength;
- } else {
- mTSQueue = new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188);
- }
-#endif
-
- mTSQueue->setRange(0, 12);
- }
-
- return size;
-}
-
-status_t WifiDisplaySource::PlaybackSession::parseRTCP(
- const sp<ABuffer> &buffer) {
- const uint8_t *data = buffer->data();
- size_t size = buffer->size();
-
- while (size > 0) {
- if (size < 8) {
- // Too short to be a valid RTCP header
- return ERROR_MALFORMED;
- }
-
- if ((data[0] >> 6) != 2) {
- // Unsupported version.
- return ERROR_UNSUPPORTED;
- }
-
- if (data[0] & 0x20) {
- // Padding present.
-
- size_t paddingLength = data[size - 1];
-
- if (paddingLength + 12 > size) {
- // If we removed this much padding we'd end up with something
- // that's too short to be a valid RTP header.
- return ERROR_MALFORMED;
- }
-
- size -= paddingLength;
- }
-
- size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4;
-
- if (size < headerLength) {
- // Only received a partial packet?
- return ERROR_MALFORMED;
- }
-
- switch (data[1]) {
- case 200:
- case 201: // RR
- case 202: // SDES
- case 203:
- case 204: // APP
- break;
-
-#if ENABLE_RETRANSMISSION
- case 205: // TSFB (transport layer specific feedback)
- parseTSFB(data, headerLength);
- break;
-#endif
-
- case 206: // PSFB (payload specific feedback)
- hexdump(data, headerLength);
- break;
-
- default:
- {
- ALOGW("Unknown RTCP packet type %u of size %d",
- (unsigned)data[1], headerLength);
- break;
- }
- }
-
- data += headerLength;
- size -= headerLength;
- }
-
- return OK;
-}
-
-#if ENABLE_RETRANSMISSION
-status_t WifiDisplaySource::PlaybackSession::parseTSFB(
- const uint8_t *data, size_t size) {
- if ((data[0] & 0x1f) != 1) {
- return ERROR_UNSUPPORTED; // We only support NACK for now.
- }
-
- uint32_t srcId = U32_AT(&data[8]);
- if (srcId != kSourceID) {
- return ERROR_MALFORMED;
- }
-
- for (size_t i = 12; i < size; i += 4) {
- uint16_t seqNo = U16_AT(&data[i]);
- uint16_t blp = U16_AT(&data[i + 2]);
-
- List<sp<ABuffer> >::iterator it = mHistory.begin();
- bool foundSeqNo = false;
- while (it != mHistory.end()) {
- const sp<ABuffer> &buffer = *it;
-
- uint16_t bufferSeqNo = buffer->int32Data() & 0xffff;
-
- bool retransmit = false;
- if (bufferSeqNo == seqNo) {
- retransmit = true;
- } else if (blp != 0) {
- for (size_t i = 0; i < 16; ++i) {
- if ((blp & (1 << i))
- && (bufferSeqNo == ((seqNo + i + 1) & 0xffff))) {
- blp &= ~(1 << i);
- retransmit = true;
- }
- }
- }
-
- if (retransmit) {
- ALOGI("retransmitting seqNo %d", bufferSeqNo);
-
- sp<ABuffer> retransRTP = new ABuffer(2 + buffer->size());
- uint8_t *rtp = retransRTP->data();
- memcpy(rtp, buffer->data(), 12);
- rtp[2] = (mRTPRetransmissionSeqNo >> 8) & 0xff;
- rtp[3] = mRTPRetransmissionSeqNo & 0xff;
- rtp[12] = (bufferSeqNo >> 8) & 0xff;
- rtp[13] = bufferSeqNo & 0xff;
- memcpy(&rtp[14], buffer->data() + 12, buffer->size() - 12);
-
- ++mRTPRetransmissionSeqNo;
-
- sendPacket(
- mRTPRetransmissionSessionID,
- retransRTP->data(), retransRTP->size());
-
- if (bufferSeqNo == seqNo) {
- foundSeqNo = true;
- }
-
- if (foundSeqNo && blp == 0) {
- break;
- }
- }
-
- ++it;
- }
-
- if (!foundSeqNo || blp != 0) {
- ALOGI("Some sequence numbers were no longer available for "
- "retransmission");
- }
- }
-
- return OK;
-}
-#endif
-
void WifiDisplaySource::PlaybackSession::requestIDRFrame() {
for (size_t i = 0; i < mTracks.size(); ++i) {
const sp<Track> &track = mTracks.valueAt(i);
}
}
-status_t WifiDisplaySource::PlaybackSession::sendPacket(
- int32_t sessionID, const void *data, size_t size) {
- return mNetSession->sendRequest(sessionID, data, size);
-}
-
bool WifiDisplaySource::PlaybackSession::allTracksHavePacketizerIndex() {
if (mAllTracksHavePacketizerIndex) {
return true;
}
status_t WifiDisplaySource::PlaybackSession::packetizeAccessUnit(
- size_t trackIndex, const sp<ABuffer> &accessUnit) {
+ size_t trackIndex, const sp<ABuffer> &accessUnit,
+ sp<ABuffer> *packets) {
const sp<Track> &track = mTracks.valueFor(trackIndex);
uint32_t flags = 0;
mPrevTimeUs = timeUs;
}
- sp<ABuffer> packets;
mPacketizer->packetize(
- track->packetizerTrackIndex(), accessUnit, &packets, flags,
+ track->packetizerTrackIndex(), accessUnit, packets, flags,
!isHDCPEncrypted ? NULL : HDCP_private_data,
- !isHDCPEncrypted ? 0 : sizeof(HDCP_private_data));
-
- for (size_t offset = 0;
- offset < packets->size(); offset += 188) {
- bool lastTSPacket = (offset + 188 >= packets->size());
-
- // We're only going to flush video, audio packets are
- // much more frequent and would waste all that space
- // available in a full sized UDP packet.
- bool flush =
- lastTSPacket
- && ((ssize_t)trackIndex == mVideoTrackIndex);
-
- appendTSData(
- packets->data() + offset,
- 188,
- true /* timeDiscontinuity */,
- flush);
- }
-
-#if LOG_TRANSPORT_STREAM
- if (mLogFile != NULL) {
- fwrite(packets->data(), 1, packets->size(), mLogFile);
- }
-#endif
+ !isHDCPEncrypted ? 0 : sizeof(HDCP_private_data),
+ track->isAudio() ? 2 : 0 /* numStuffingBytes */);
return OK;
}
sp<ABuffer> accessUnit = track->dequeueAccessUnit();
if (accessUnit != NULL) {
- status_t err = packetizeAccessUnit(trackIndex, accessUnit);
-
- if (err != OK) {
- return err;
- }
-
+ track->queueOutputBuffer(accessUnit);
gotMoreData = true;
}
}
mWeAreDead = true;
}
+void WifiDisplaySource::PlaybackSession::drainAccessUnits() {
+ ALOGV("audio/video has %d/%d buffers ready.",
+ mTracks.valueFor(1)->countQueuedOutputBuffers(),
+ mTracks.valueFor(0)->countQueuedOutputBuffers());
+
+ while (drainAccessUnit()) {
+ }
+}
+
+bool WifiDisplaySource::PlaybackSession::drainAccessUnit() {
+ ssize_t minTrackIndex = -1;
+ int64_t minTimeUs = -1ll;
+
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ const sp<Track> &track = mTracks.valueAt(i);
+
+ int64_t timeUs;
+ if (track->hasOutputBuffer(&timeUs)) {
+ if (minTrackIndex < 0 || timeUs < minTimeUs) {
+ minTrackIndex = mTracks.keyAt(i);
+ minTimeUs = timeUs;
+ }
+ } else if (!track->isSuspended()) {
+ // We still consider this track "live", so it should keep
+ // delivering output data whose time stamps we'll have to
+ // consider for proper interleaving.
+ return false;
+ }
+ }
+
+ if (minTrackIndex < 0) {
+ return false;
+ }
+
+ const sp<Track> &track = mTracks.valueFor(minTrackIndex);
+ sp<ABuffer> accessUnit = track->dequeueOutputBuffer();
+
+ sp<ABuffer> packets;
+ status_t err = packetizeAccessUnit(minTrackIndex, accessUnit, &packets);
+
+ if (err != OK) {
+ notifySessionDead();
+ }
+
+ if ((ssize_t)minTrackIndex == mVideoTrackIndex) {
+ packets->meta()->setInt32("isVideo", 1);
+ }
+ mSender->queuePackets(minTimeUs, packets);
+
+ return true;
+}
+
} // namespace android
#define PLAYBACK_SESSION_H_
+#include "Sender.h"
#include "WifiDisplaySource.h"
namespace android {
struct MediaSource;
struct TSPacketizer;
-#define LOG_TRANSPORT_STREAM 0
-#define ENABLE_RETRANSMISSION 0
-#define TRACK_BANDWIDTH 0
-
// Encapsulates the state of an RTP/RTCP session in the context of wifi
// display.
struct WifiDisplaySource::PlaybackSession : public AHandler {
const struct in_addr &interfaceAddr,
const sp<IHDCP> &hdcp);
- enum TransportMode {
- TRANSPORT_UDP,
- TRANSPORT_TCP_INTERLEAVED,
- TRANSPORT_TCP,
- };
status_t init(
const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
- TransportMode transportMode,
+ Sender::TransportMode transportMode,
bool usePCMAudio);
void destroyAsync();
struct Track;
enum {
- kWhatSendSR,
- kWhatRTPNotify,
- kWhatRTCPNotify,
-#if ENABLE_RETRANSMISSION
- kWhatRTPRetransmissionNotify,
- kWhatRTCPRetransmissionNotify,
-#endif
kWhatMediaPullerNotify,
kWhatConverterNotify,
kWhatTrackNotify,
+ kWhatSenderNotify,
kWhatUpdateSurface,
kWhatFinishPlay,
+ kWhatPacketize,
};
- static const int64_t kSendSRIntervalUs = 10000000ll;
- static const uint32_t kSourceID = 0xdeadbeef;
- static const size_t kMaxHistoryLength = 128;
-
-#if ENABLE_RETRANSMISSION
- static const size_t kRetransmissionPortOffset = 120;
-#endif
-
sp<ANetworkSession> mNetSession;
+ sp<Sender> mSender;
+ sp<ALooper> mSenderLooper;
sp<AMessage> mNotify;
in_addr mInterfaceAddr;
sp<IHDCP> mHDCP;
KeyedVector<size_t, sp<Track> > mTracks;
ssize_t mVideoTrackIndex;
- sp<ABuffer> mTSQueue;
int64_t mPrevTimeUs;
- TransportMode mTransportMode;
-
- AString mClientIP;
-
bool mAllTracksHavePacketizerIndex;
- // in TCP mode
- int32_t mRTPChannel;
- int32_t mRTCPChannel;
-
- // in UDP mode
- int32_t mRTPPort;
- int32_t mRTPSessionID;
- int32_t mRTCPSessionID;
-
-#if ENABLE_RETRANSMISSION
- int32_t mRTPRetransmissionSessionID;
- int32_t mRTCPRetransmissionSessionID;
-#endif
-
- int32_t mClientRTPPort;
- int32_t mClientRTCPPort;
- bool mRTPConnected;
- bool mRTCPConnected;
-
- uint32_t mRTPSeqNo;
-#if ENABLE_RETRANSMISSION
- uint32_t mRTPRetransmissionSeqNo;
-#endif
-
- uint64_t mLastNTPTime;
- uint32_t mLastRTPTime;
- uint32_t mNumRTPSent;
- uint32_t mNumRTPOctetsSent;
- uint32_t mNumSRsSent;
-
- bool mSendSRPending;
-
-#if ENABLE_RETRANSMISSION
- List<sp<ABuffer> > mHistory;
- size_t mHistoryLength;
-#endif
-
-#if TRACK_BANDWIDTH
- int64_t mFirstPacketTimeUs;
- uint64_t mTotalBytesSent;
-#endif
-
-#if LOG_TRANSPORT_STREAM
- FILE *mLogFile;
-#endif
-
- void onSendSR();
- void addSR(const sp<ABuffer> &buffer);
- void addSDES(const sp<ABuffer> &buffer);
- static uint64_t GetNowNTP();
-
status_t setupPacketizer(bool usePCMAudio);
status_t addSource(
ssize_t appendTSData(
const void *data, size_t size, bool timeDiscontinuity, bool flush);
- void scheduleSendSR();
-
- status_t parseRTCP(const sp<ABuffer> &buffer);
-
-#if ENABLE_RETRANSMISSION
- status_t parseTSFB(const uint8_t *data, size_t size);
-#endif
-
- status_t sendPacket(int32_t sessionID, const void *data, size_t size);
status_t onFinishPlay();
status_t onFinishPlay2();
bool allTracksHavePacketizerIndex();
status_t packetizeAccessUnit(
- size_t trackIndex, const sp<ABuffer> &accessUnit);
+ size_t trackIndex, const sp<ABuffer> &accessUnit,
+ sp<ABuffer> *packets);
status_t packetizeQueuedAccessUnits();
void notifySessionDead();
+ void drainAccessUnits();
+
+ // Returns true iff an access unit was successfully drained.
+ bool drainAccessUnit();
+
DISALLOW_EVIL_CONSTRUCTORS(PlaybackSession);
};
--- /dev/null
+/*
+ * Copyright 2012, 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 "Sender"
+#include <utils/Log.h>
+
+#include "Sender.h"
+
+#include "ANetworkSession.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/MediaErrors.h>
+#include <media/stagefright/Utils.h>
+
+#include <math.h>
+
+#define DEBUG_JITTER 0
+
+namespace android {
+
+////////////////////////////////////////////////////////////////////////////////
+
+#if DEBUG_JITTER
+struct TimeSeries {
+ TimeSeries();
+
+ void add(double val);
+
+ double mean() const;
+ double sdev() const;
+
+private:
+ enum {
+ kHistorySize = 20
+ };
+ double mValues[kHistorySize];
+
+ size_t mCount;
+ double mSum;
+};
+
+TimeSeries::TimeSeries()
+ : mCount(0),
+ mSum(0.0) {
+}
+
+void TimeSeries::add(double val) {
+ if (mCount < kHistorySize) {
+ mValues[mCount++] = val;
+ mSum += val;
+ } else {
+ mSum -= mValues[0];
+ memmove(&mValues[0], &mValues[1], (kHistorySize - 1) * sizeof(double));
+ mValues[kHistorySize - 1] = val;
+ mSum += val;
+ }
+}
+
+double TimeSeries::mean() const {
+ if (mCount < 1) {
+ return 0.0;
+ }
+
+ return mSum / mCount;
+}
+
+double TimeSeries::sdev() const {
+ if (mCount < 1) {
+ return 0.0;
+ }
+
+ double m = mean();
+
+ double sum = 0.0;
+ for (size_t i = 0; i < mCount; ++i) {
+ double tmp = mValues[i] - m;
+ tmp *= tmp;
+
+ sum += tmp;
+ }
+
+ return sqrt(sum / mCount);
+}
+#endif // DEBUG_JITTER
+
+////////////////////////////////////////////////////////////////////////////////
+
+static size_t kMaxRTPPacketSize = 1500;
+static size_t kMaxNumTSPacketsPerRTPPacket = (kMaxRTPPacketSize - 12) / 188;
+
+Sender::Sender(
+ const sp<ANetworkSession> &netSession,
+ const sp<AMessage> ¬ify)
+ : mNetSession(netSession),
+ mNotify(notify),
+ mTSQueue(new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188)),
+ mTransportMode(TRANSPORT_UDP),
+ mRTPChannel(0),
+ mRTCPChannel(0),
+ mRTPPort(0),
+ mRTPSessionID(0),
+ mRTCPSessionID(0),
+#if ENABLE_RETRANSMISSION
+ mRTPRetransmissionSessionID(0),
+ mRTCPRetransmissionSessionID(0),
+#endif
+ mClientRTPPort(0),
+ mClientRTCPPort(0),
+ mRTPConnected(false),
+ mRTCPConnected(false),
+ mFirstOutputBufferReadyTimeUs(-1ll),
+ mFirstOutputBufferSentTimeUs(-1ll),
+ mRTPSeqNo(0),
+#if ENABLE_RETRANSMISSION
+ mRTPRetransmissionSeqNo(0),
+#endif
+ mLastNTPTime(0),
+ mLastRTPTime(0),
+ mNumRTPSent(0),
+ mNumRTPOctetsSent(0),
+ mNumSRsSent(0),
+ mSendSRPending(false)
+#if ENABLE_RETRANSMISSION
+ ,mHistoryLength(0)
+#endif
+#if TRACK_BANDWIDTH
+ ,mFirstPacketTimeUs(-1ll)
+ ,mTotalBytesSent(0ll)
+#endif
+#if LOG_TRANSPORT_STREAM
+ ,mLogFile(NULL)
+#endif
+{
+ mTSQueue->setRange(0, 12);
+
+#if LOG_TRANSPORT_STREAM
+ mLogFile = fopen("/system/etc/log.ts", "wb");
+#endif
+}
+
+Sender::~Sender() {
+#if ENABLE_RETRANSMISSION
+ if (mRTCPRetransmissionSessionID != 0) {
+ mNetSession->destroySession(mRTCPRetransmissionSessionID);
+ }
+
+ if (mRTPRetransmissionSessionID != 0) {
+ mNetSession->destroySession(mRTPRetransmissionSessionID);
+ }
+#endif
+
+ if (mRTCPSessionID != 0) {
+ mNetSession->destroySession(mRTCPSessionID);
+ }
+
+ if (mRTPSessionID != 0) {
+ mNetSession->destroySession(mRTPSessionID);
+ }
+
+#if LOG_TRANSPORT_STREAM
+ if (mLogFile != NULL) {
+ fclose(mLogFile);
+ mLogFile = NULL;
+ }
+#endif
+}
+
+status_t Sender::init(
+ const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
+ TransportMode transportMode) {
+ mClientIP = clientIP;
+ mTransportMode = transportMode;
+
+ if (transportMode == TRANSPORT_TCP_INTERLEAVED) {
+ mRTPChannel = clientRtp;
+ mRTCPChannel = clientRtcp;
+ mRTPPort = 0;
+ mRTPSessionID = 0;
+ mRTCPSessionID = 0;
+ return OK;
+ }
+
+ mRTPChannel = 0;
+ mRTCPChannel = 0;
+
+ if (mTransportMode == TRANSPORT_TCP) {
+ // XXX This is wrong, we need to allocate sockets here, we only
+ // need to do this because the dongles are not establishing their
+ // end until after PLAY instead of before SETUP.
+ mRTPPort = 20000;
+ mRTPSessionID = 0;
+ mRTCPSessionID = 0;
+ mClientRTPPort = clientRtp;
+ mClientRTCPPort = clientRtcp;
+ return OK;
+ }
+
+ int serverRtp;
+
+ sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
+ sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id());
+
+#if ENABLE_RETRANSMISSION
+ sp<AMessage> rtpRetransmissionNotify =
+ new AMessage(kWhatRTPRetransmissionNotify, id());
+
+ sp<AMessage> rtcpRetransmissionNotify =
+ new AMessage(kWhatRTCPRetransmissionNotify, id());
+#endif
+
+ status_t err;
+ for (serverRtp = 15550;; serverRtp += 2) {
+ int32_t rtpSession;
+ if (mTransportMode == TRANSPORT_UDP) {
+ err = mNetSession->createUDPSession(
+ serverRtp, clientIP, clientRtp,
+ rtpNotify, &rtpSession);
+ } else {
+ err = mNetSession->createTCPDatagramSession(
+ serverRtp, clientIP, clientRtp,
+ rtpNotify, &rtpSession);
+ }
+
+ if (err != OK) {
+ ALOGI("failed to create RTP socket on port %d", serverRtp);
+ continue;
+ }
+
+ int32_t rtcpSession = 0;
+
+ if (clientRtcp >= 0) {
+ if (mTransportMode == TRANSPORT_UDP) {
+ err = mNetSession->createUDPSession(
+ serverRtp + 1, clientIP, clientRtcp,
+ rtcpNotify, &rtcpSession);
+ } else {
+ err = mNetSession->createTCPDatagramSession(
+ serverRtp + 1, clientIP, clientRtcp,
+ rtcpNotify, &rtcpSession);
+ }
+
+ if (err != OK) {
+ ALOGI("failed to create RTCP socket on port %d", serverRtp + 1);
+
+ mNetSession->destroySession(rtpSession);
+ continue;
+ }
+ }
+
+#if ENABLE_RETRANSMISSION
+ if (mTransportMode == TRANSPORT_UDP) {
+ int32_t rtpRetransmissionSession;
+
+ err = mNetSession->createUDPSession(
+ serverRtp + kRetransmissionPortOffset,
+ clientIP,
+ clientRtp + kRetransmissionPortOffset,
+ rtpRetransmissionNotify,
+ &rtpRetransmissionSession);
+
+ if (err != OK) {
+ mNetSession->destroySession(rtcpSession);
+ mNetSession->destroySession(rtpSession);
+ continue;
+ }
+
+ CHECK_GE(clientRtcp, 0);
+
+ int32_t rtcpRetransmissionSession;
+ err = mNetSession->createUDPSession(
+ serverRtp + 1 + kRetransmissionPortOffset,
+ clientIP,
+ clientRtp + 1 + kRetransmissionPortOffset,
+ rtcpRetransmissionNotify,
+ &rtcpRetransmissionSession);
+
+ if (err != OK) {
+ mNetSession->destroySession(rtpRetransmissionSession);
+ mNetSession->destroySession(rtcpSession);
+ mNetSession->destroySession(rtpSession);
+ continue;
+ }
+
+ mRTPRetransmissionSessionID = rtpRetransmissionSession;
+ mRTCPRetransmissionSessionID = rtcpRetransmissionSession;
+
+ ALOGI("rtpRetransmissionSessionID = %d, "
+ "rtcpRetransmissionSessionID = %d",
+ rtpRetransmissionSession, rtcpRetransmissionSession);
+ }
+#endif
+
+ mRTPPort = serverRtp;
+ mRTPSessionID = rtpSession;
+ mRTCPSessionID = rtcpSession;
+
+ ALOGI("rtpSessionID = %d, rtcpSessionID = %d", rtpSession, rtcpSession);
+ break;
+ }
+
+ if (mRTPPort == 0) {
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+status_t Sender::finishInit() {
+ if (mTransportMode != TRANSPORT_TCP) {
+ notifyInitDone();
+ return OK;
+ }
+
+ sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
+
+ status_t err = mNetSession->createTCPDatagramSession(
+ mRTPPort, mClientIP.c_str(), mClientRTPPort,
+ rtpNotify, &mRTPSessionID);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (mClientRTCPPort >= 0) {
+ sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id());
+
+ err = mNetSession->createTCPDatagramSession(
+ mRTPPort + 1, mClientIP.c_str(), mClientRTCPPort,
+ rtcpNotify, &mRTCPSessionID);
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+int32_t Sender::getRTPPort() const {
+ return mRTPPort;
+}
+
+void Sender::queuePackets(
+ int64_t timeUs, const sp<ABuffer> &packets) {
+ bool isVideo = false;
+
+ int32_t dummy;
+ if (packets->meta()->findInt32("isVideo", &dummy)) {
+ isVideo = true;
+ }
+
+ int64_t delayUs;
+ int64_t whenUs;
+
+ if (mFirstOutputBufferReadyTimeUs < 0ll) {
+ mFirstOutputBufferReadyTimeUs = timeUs;
+ mFirstOutputBufferSentTimeUs = whenUs = ALooper::GetNowUs();
+ delayUs = 0ll;
+ } else {
+ int64_t nowUs = ALooper::GetNowUs();
+
+ whenUs = (timeUs - mFirstOutputBufferReadyTimeUs)
+ + mFirstOutputBufferSentTimeUs;
+
+ delayUs = whenUs - nowUs;
+ }
+
+ sp<AMessage> msg = new AMessage(kWhatQueuePackets, id());
+ msg->setBuffer("packets", packets);
+
+ packets->meta()->setInt64("timeUs", timeUs);
+ packets->meta()->setInt64("whenUs", whenUs);
+ packets->meta()->setInt64("delayUs", delayUs);
+ msg->post(delayUs > 0 ? delayUs : 0);
+}
+
+void Sender::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatRTPNotify:
+ case kWhatRTCPNotify:
+#if ENABLE_RETRANSMISSION
+ case kWhatRTPRetransmissionNotify:
+ case kWhatRTCPRetransmissionNotify:
+#endif
+ {
+ int32_t reason;
+ CHECK(msg->findInt32("reason", &reason));
+
+ switch (reason) {
+ case ANetworkSession::kWhatError:
+ {
+ int32_t sessionID;
+ CHECK(msg->findInt32("sessionID", &sessionID));
+
+ int32_t err;
+ CHECK(msg->findInt32("err", &err));
+
+ int32_t errorOccuredDuringSend;
+ CHECK(msg->findInt32("send", &errorOccuredDuringSend));
+
+ AString detail;
+ CHECK(msg->findString("detail", &detail));
+
+ if ((msg->what() == kWhatRTPNotify
+#if ENABLE_RETRANSMISSION
+ || msg->what() == kWhatRTPRetransmissionNotify
+#endif
+ ) && !errorOccuredDuringSend) {
+ // This is ok, we don't expect to receive anything on
+ // the RTP socket.
+ break;
+ }
+
+ ALOGE("An error occurred during %s in session %d "
+ "(%d, '%s' (%s)).",
+ errorOccuredDuringSend ? "send" : "receive",
+ sessionID,
+ err,
+ detail.c_str(),
+ strerror(-err));
+
+ mNetSession->destroySession(sessionID);
+
+ if (sessionID == mRTPSessionID) {
+ mRTPSessionID = 0;
+ } else if (sessionID == mRTCPSessionID) {
+ mRTCPSessionID = 0;
+ }
+#if ENABLE_RETRANSMISSION
+ else if (sessionID == mRTPRetransmissionSessionID) {
+ mRTPRetransmissionSessionID = 0;
+ } else if (sessionID == mRTCPRetransmissionSessionID) {
+ mRTCPRetransmissionSessionID = 0;
+ }
+#endif
+
+ notifySessionDead();
+ break;
+ }
+
+ case ANetworkSession::kWhatDatagram:
+ {
+ int32_t sessionID;
+ CHECK(msg->findInt32("sessionID", &sessionID));
+
+ sp<ABuffer> data;
+ CHECK(msg->findBuffer("data", &data));
+
+ status_t err;
+ if (msg->what() == kWhatRTCPNotify
+#if ENABLE_RETRANSMISSION
+ || msg->what() == kWhatRTCPRetransmissionNotify
+#endif
+ )
+ {
+ err = parseRTCP(data);
+ }
+ break;
+ }
+
+ case ANetworkSession::kWhatConnected:
+ {
+ CHECK_EQ(mTransportMode, TRANSPORT_TCP);
+
+ int32_t sessionID;
+ CHECK(msg->findInt32("sessionID", &sessionID));
+
+ if (sessionID == mRTPSessionID) {
+ CHECK(!mRTPConnected);
+ mRTPConnected = true;
+ ALOGI("RTP Session now connected.");
+ } else if (sessionID == mRTCPSessionID) {
+ CHECK(!mRTCPConnected);
+ mRTCPConnected = true;
+ ALOGI("RTCP Session now connected.");
+ } else {
+ TRESPASS();
+ }
+
+ if (mRTPConnected
+ && (mClientRTCPPort < 0 || mRTCPConnected)) {
+ notifyInitDone();
+ }
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+ break;
+ }
+
+ case kWhatQueuePackets:
+ {
+ sp<ABuffer> packets;
+ CHECK(msg->findBuffer("packets", &packets));
+
+ onQueuePackets(packets);
+ break;
+ }
+
+ case kWhatSendSR:
+ {
+ mSendSRPending = false;
+
+ if (mRTCPSessionID == 0) {
+ break;
+ }
+
+ onSendSR();
+
+ scheduleSendSR();
+ break;
+ }
+ }
+}
+
+void Sender::onQueuePackets(const sp<ABuffer> &packets) {
+#if DEBUG_JITTER
+ int32_t dummy;
+ if (packets->meta()->findInt32("isVideo", &dummy)) {
+ static int64_t lastTimeUs = 0ll;
+ int64_t nowUs = ALooper::GetNowUs();
+
+ static TimeSeries series;
+ series.add((double)(nowUs - lastTimeUs));
+
+ ALOGI("deltaTimeUs = %lld us, mean %.2f, sdev %.2f",
+ nowUs - lastTimeUs, series.mean(), series.sdev());
+
+ lastTimeUs = nowUs;
+ }
+#endif
+
+ int64_t startTimeUs = ALooper::GetNowUs();
+
+ for (size_t offset = 0;
+ offset < packets->size(); offset += 188) {
+ bool lastTSPacket = (offset + 188 >= packets->size());
+
+ appendTSData(
+ packets->data() + offset,
+ 188,
+ true /* timeDiscontinuity */,
+ lastTSPacket /* flush */);
+ }
+
+#if 0
+ int64_t netTimeUs = ALooper::GetNowUs() - startTimeUs;
+
+ int64_t whenUs;
+ CHECK(packets->meta()->findInt64("whenUs", &whenUs));
+
+ int64_t delayUs;
+ CHECK(packets->meta()->findInt64("delayUs", &delayUs));
+
+ bool isVideo = false;
+ int32_t dummy;
+ if (packets->meta()->findInt32("isVideo", &dummy)) {
+ isVideo = true;
+ }
+
+ int64_t nowUs = ALooper::GetNowUs();
+
+ if (nowUs - whenUs > 2000) {
+ ALOGI("[%s] delayUs = %lld us, delta = %lld us",
+ isVideo ? "video" : "audio", delayUs, nowUs - netTimeUs - whenUs);
+ }
+#endif
+
+#if LOG_TRANSPORT_STREAM
+ if (mLogFile != NULL) {
+ fwrite(packets->data(), 1, packets->size(), mLogFile);
+ }
+#endif
+}
+
+ssize_t Sender::appendTSData(
+ const void *data, size_t size, bool timeDiscontinuity, bool flush) {
+ CHECK_EQ(size, 188);
+
+ CHECK_LE(mTSQueue->size() + size, mTSQueue->capacity());
+
+ memcpy(mTSQueue->data() + mTSQueue->size(), data, size);
+ mTSQueue->setRange(0, mTSQueue->size() + size);
+
+ if (flush || mTSQueue->size() == mTSQueue->capacity()) {
+ // flush
+
+ int64_t nowUs = ALooper::GetNowUs();
+
+#if TRACK_BANDWIDTH
+ if (mFirstPacketTimeUs < 0ll) {
+ mFirstPacketTimeUs = nowUs;
+ }
+#endif
+
+ // 90kHz time scale
+ uint32_t rtpTime = (nowUs * 9ll) / 100ll;
+
+ uint8_t *rtp = mTSQueue->data();
+ rtp[0] = 0x80;
+ rtp[1] = 33 | (timeDiscontinuity ? (1 << 7) : 0); // M-bit
+ rtp[2] = (mRTPSeqNo >> 8) & 0xff;
+ rtp[3] = mRTPSeqNo & 0xff;
+ rtp[4] = rtpTime >> 24;
+ rtp[5] = (rtpTime >> 16) & 0xff;
+ rtp[6] = (rtpTime >> 8) & 0xff;
+ rtp[7] = rtpTime & 0xff;
+ rtp[8] = kSourceID >> 24;
+ rtp[9] = (kSourceID >> 16) & 0xff;
+ rtp[10] = (kSourceID >> 8) & 0xff;
+ rtp[11] = kSourceID & 0xff;
+
+ ++mRTPSeqNo;
+ ++mNumRTPSent;
+ mNumRTPOctetsSent += mTSQueue->size() - 12;
+
+ mLastRTPTime = rtpTime;
+ mLastNTPTime = GetNowNTP();
+
+ if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatBinaryData);
+
+ sp<ABuffer> data = new ABuffer(mTSQueue->size());
+ memcpy(data->data(), rtp, mTSQueue->size());
+
+ notify->setInt32("channel", mRTPChannel);
+ notify->setBuffer("data", data);
+ notify->post();
+ } else {
+ sendPacket(mRTPSessionID, rtp, mTSQueue->size());
+
+#if TRACK_BANDWIDTH
+ mTotalBytesSent += mTSQueue->size();
+ int64_t delayUs = ALooper::GetNowUs() - mFirstPacketTimeUs;
+
+ if (delayUs > 0ll) {
+ ALOGI("approx. net bandwidth used: %.2f Mbit/sec",
+ mTotalBytesSent * 8.0 / delayUs);
+ }
+#endif
+ }
+
+#if ENABLE_RETRANSMISSION
+ mTSQueue->setInt32Data(mRTPSeqNo - 1);
+
+ mHistory.push_back(mTSQueue);
+ ++mHistoryLength;
+
+ if (mHistoryLength > kMaxHistoryLength) {
+ mTSQueue = *mHistory.begin();
+ mHistory.erase(mHistory.begin());
+
+ --mHistoryLength;
+ } else {
+ mTSQueue = new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188);
+ }
+#endif
+
+ mTSQueue->setRange(0, 12);
+ }
+
+ return size;
+}
+
+void Sender::scheduleSendSR() {
+ if (mSendSRPending || mRTCPSessionID == 0) {
+ return;
+ }
+
+ mSendSRPending = true;
+ (new AMessage(kWhatSendSR, id()))->post(kSendSRIntervalUs);
+}
+
+void Sender::addSR(const sp<ABuffer> &buffer) {
+ uint8_t *data = buffer->data() + buffer->size();
+
+ // TODO: Use macros/utility functions to clean up all the bitshifts below.
+
+ data[0] = 0x80 | 0;
+ data[1] = 200; // SR
+ data[2] = 0;
+ data[3] = 6;
+ data[4] = kSourceID >> 24;
+ data[5] = (kSourceID >> 16) & 0xff;
+ data[6] = (kSourceID >> 8) & 0xff;
+ data[7] = kSourceID & 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 Sender::addSDES(const sp<ABuffer> &buffer) {
+ uint8_t *data = buffer->data() + buffer->size();
+ data[0] = 0x80 | 1;
+ data[1] = 202; // SDES
+ data[4] = kSourceID >> 24;
+ data[5] = (kSourceID >> 16) & 0xff;
+ data[6] = (kSourceID >> 8) & 0xff;
+ data[7] = kSourceID & 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 Sender::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 Sender::onSendSR() {
+ sp<ABuffer> buffer = new ABuffer(1500);
+ buffer->setRange(0, 0);
+
+ addSR(buffer);
+ addSDES(buffer);
+
+ if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatBinaryData);
+ notify->setInt32("channel", mRTCPChannel);
+ notify->setBuffer("data", buffer);
+ notify->post();
+ } else {
+ sendPacket(mRTCPSessionID, buffer->data(), buffer->size());
+ }
+
+ ++mNumSRsSent;
+}
+
+#if ENABLE_RETRANSMISSION
+status_t Sender::parseTSFB(
+ const uint8_t *data, size_t size) {
+ if ((data[0] & 0x1f) != 1) {
+ return ERROR_UNSUPPORTED; // We only support NACK for now.
+ }
+
+ uint32_t srcId = U32_AT(&data[8]);
+ if (srcId != kSourceID) {
+ return ERROR_MALFORMED;
+ }
+
+ for (size_t i = 12; i < size; i += 4) {
+ uint16_t seqNo = U16_AT(&data[i]);
+ uint16_t blp = U16_AT(&data[i + 2]);
+
+ List<sp<ABuffer> >::iterator it = mHistory.begin();
+ bool foundSeqNo = false;
+ while (it != mHistory.end()) {
+ const sp<ABuffer> &buffer = *it;
+
+ uint16_t bufferSeqNo = buffer->int32Data() & 0xffff;
+
+ bool retransmit = false;
+ if (bufferSeqNo == seqNo) {
+ retransmit = true;
+ } else if (blp != 0) {
+ for (size_t i = 0; i < 16; ++i) {
+ if ((blp & (1 << i))
+ && (bufferSeqNo == ((seqNo + i + 1) & 0xffff))) {
+ blp &= ~(1 << i);
+ retransmit = true;
+ }
+ }
+ }
+
+ if (retransmit) {
+ ALOGI("retransmitting seqNo %d", bufferSeqNo);
+
+ sp<ABuffer> retransRTP = new ABuffer(2 + buffer->size());
+ uint8_t *rtp = retransRTP->data();
+ memcpy(rtp, buffer->data(), 12);
+ rtp[2] = (mRTPRetransmissionSeqNo >> 8) & 0xff;
+ rtp[3] = mRTPRetransmissionSeqNo & 0xff;
+ rtp[12] = (bufferSeqNo >> 8) & 0xff;
+ rtp[13] = bufferSeqNo & 0xff;
+ memcpy(&rtp[14], buffer->data() + 12, buffer->size() - 12);
+
+ ++mRTPRetransmissionSeqNo;
+
+ sendPacket(
+ mRTPRetransmissionSessionID,
+ retransRTP->data(), retransRTP->size());
+
+ if (bufferSeqNo == seqNo) {
+ foundSeqNo = true;
+ }
+
+ if (foundSeqNo && blp == 0) {
+ break;
+ }
+ }
+
+ ++it;
+ }
+
+ if (!foundSeqNo || blp != 0) {
+ ALOGI("Some sequence numbers were no longer available for "
+ "retransmission");
+ }
+ }
+
+ return OK;
+}
+#endif
+
+status_t Sender::parseRTCP(
+ const sp<ABuffer> &buffer) {
+ const uint8_t *data = buffer->data();
+ size_t size = buffer->size();
+
+ while (size > 0) {
+ if (size < 8) {
+ // Too short to be a valid RTCP header
+ return ERROR_MALFORMED;
+ }
+
+ if ((data[0] >> 6) != 2) {
+ // Unsupported version.
+ return ERROR_UNSUPPORTED;
+ }
+
+ if (data[0] & 0x20) {
+ // Padding present.
+
+ size_t paddingLength = data[size - 1];
+
+ if (paddingLength + 12 > size) {
+ // If we removed this much padding we'd end up with something
+ // that's too short to be a valid RTP header.
+ return ERROR_MALFORMED;
+ }
+
+ size -= paddingLength;
+ }
+
+ size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4;
+
+ if (size < headerLength) {
+ // Only received a partial packet?
+ return ERROR_MALFORMED;
+ }
+
+ switch (data[1]) {
+ case 200:
+ case 201: // RR
+ case 202: // SDES
+ case 203:
+ case 204: // APP
+ break;
+
+#if ENABLE_RETRANSMISSION
+ case 205: // TSFB (transport layer specific feedback)
+ parseTSFB(data, headerLength);
+ break;
+#endif
+
+ case 206: // PSFB (payload specific feedback)
+ hexdump(data, headerLength);
+ break;
+
+ default:
+ {
+ ALOGW("Unknown RTCP packet type %u of size %d",
+ (unsigned)data[1], headerLength);
+ break;
+ }
+ }
+
+ data += headerLength;
+ size -= headerLength;
+ }
+
+ return OK;
+}
+
+status_t Sender::sendPacket(
+ int32_t sessionID, const void *data, size_t size) {
+ return mNetSession->sendRequest(sessionID, data, size);
+}
+
+void Sender::notifyInitDone() {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatInitDone);
+ notify->post();
+}
+
+void Sender::notifySessionDead() {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatSessionDead);
+ notify->post();
+}
+
+} // namespace android
+
--- /dev/null
+/*
+ * Copyright 2012, 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.
+ */
+
+#ifndef SENDER_H_
+
+#define SENDER_H_
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+#define LOG_TRANSPORT_STREAM 0
+#define ENABLE_RETRANSMISSION 0
+#define TRACK_BANDWIDTH 0
+
+struct ABuffer;
+struct ANetworkSession;
+
+struct Sender : public AHandler {
+ Sender(const sp<ANetworkSession> &netSession, const sp<AMessage> ¬ify);
+
+ enum {
+ kWhatInitDone,
+ kWhatSessionDead,
+ kWhatBinaryData,
+ };
+
+ enum TransportMode {
+ TRANSPORT_UDP,
+ TRANSPORT_TCP_INTERLEAVED,
+ TRANSPORT_TCP,
+ };
+ status_t init(
+ const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
+ TransportMode transportMode);
+
+ status_t finishInit();
+
+ int32_t getRTPPort() const;
+
+ void queuePackets(int64_t timeUs, const sp<ABuffer> &packets);
+ void scheduleSendSR();
+
+protected:
+ virtual ~Sender();
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+ enum {
+ kWhatQueuePackets,
+ kWhatSendSR,
+ kWhatRTPNotify,
+ kWhatRTCPNotify,
+#if ENABLE_RETRANSMISSION
+ kWhatRTPRetransmissionNotify,
+ kWhatRTCPRetransmissionNotify
+#endif
+ };
+
+ static const int64_t kSendSRIntervalUs = 10000000ll;
+
+ static const uint32_t kSourceID = 0xdeadbeef;
+ static const size_t kMaxHistoryLength = 128;
+
+#if ENABLE_RETRANSMISSION
+ static const size_t kRetransmissionPortOffset = 120;
+#endif
+
+ sp<ANetworkSession> mNetSession;
+ sp<AMessage> mNotify;
+
+ sp<ABuffer> mTSQueue;
+
+ TransportMode mTransportMode;
+ AString mClientIP;
+
+ // in TCP mode
+ int32_t mRTPChannel;
+ int32_t mRTCPChannel;
+
+ // in UDP mode
+ int32_t mRTPPort;
+ int32_t mRTPSessionID;
+ int32_t mRTCPSessionID;
+
+#if ENABLE_RETRANSMISSION
+ int32_t mRTPRetransmissionSessionID;
+ int32_t mRTCPRetransmissionSessionID;
+#endif
+
+ int32_t mClientRTPPort;
+ int32_t mClientRTCPPort;
+ bool mRTPConnected;
+ bool mRTCPConnected;
+
+
+ int64_t mFirstOutputBufferReadyTimeUs;
+ int64_t mFirstOutputBufferSentTimeUs;
+
+ uint32_t mRTPSeqNo;
+#if ENABLE_RETRANSMISSION
+ uint32_t mRTPRetransmissionSeqNo;
+#endif
+
+ uint64_t mLastNTPTime;
+ uint32_t mLastRTPTime;
+ uint32_t mNumRTPSent;
+ uint32_t mNumRTPOctetsSent;
+ uint32_t mNumSRsSent;
+
+ bool mSendSRPending;
+
+#if ENABLE_RETRANSMISSION
+ List<sp<ABuffer> > mHistory;
+ size_t mHistoryLength;
+#endif
+
+#if TRACK_BANDWIDTH
+ int64_t mFirstPacketTimeUs;
+ uint64_t mTotalBytesSent;
+#endif
+
+ void onSendSR();
+ void addSR(const sp<ABuffer> &buffer);
+ void addSDES(const sp<ABuffer> &buffer);
+ static uint64_t GetNowNTP();
+
+#if LOG_TRANSPORT_STREAM
+ FILE *mLogFile;
+#endif
+
+ ssize_t appendTSData(
+ const void *data, size_t size, bool timeDiscontinuity, bool flush);
+
+ void onQueuePackets(const sp<ABuffer> &packets);
+
+#if ENABLE_RETRANSMISSION
+ status_t parseTSFB(const uint8_t *data, size_t size);
+#endif
+
+ status_t parseRTCP(const sp<ABuffer> &buffer);
+
+ status_t sendPacket(int32_t sessionID, const void *data, size_t size);
+
+ void notifyInitDone();
+ void notifySessionDead();
+
+ DISALLOW_EVIL_CONSTRUCTORS(Sender);
+};
+
+} // namespace android
+
+#endif // SENDER_H_
bool isH264() const;
bool isAAC() const;
bool lacksADTSHeader() const;
+ bool isPCMAudio() const;
sp<ABuffer> prependCSD(const sp<ABuffer> &accessUnit) const;
sp<ABuffer> prependADTSHeader(const sp<ABuffer> &accessUnit) const;
+ size_t countDescriptors() const;
+ sp<ABuffer> descriptorAt(size_t index) const;
+
+ void finalize();
+
protected:
virtual ~Track();
AString mMIME;
Vector<sp<ABuffer> > mCSD;
+ Vector<sp<ABuffer> > mDescriptors;
+
bool mAudioLacksATDSHeaders;
+ bool mFinalized;
DISALLOW_EVIL_CONSTRUCTORS(Track);
};
mStreamType(streamType),
mStreamID(streamID),
mContinuityCounter(0),
- mAudioLacksATDSHeaders(false) {
+ mAudioLacksATDSHeaders(false),
+ mFinalized(false) {
CHECK(format->findString("mime", &mMIME));
if (!strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)
return !strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_AUDIO_AAC);
}
+bool TSPacketizer::Track::isPCMAudio() const {
+ return !strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_AUDIO_RAW);
+}
+
bool TSPacketizer::Track::lacksADTSHeader() const {
return mAudioLacksATDSHeaders;
}
return dup;
}
+size_t TSPacketizer::Track::countDescriptors() const {
+ return mDescriptors.size();
+}
+
+sp<ABuffer> TSPacketizer::Track::descriptorAt(size_t index) const {
+ CHECK_LT(index, mDescriptors.size());
+ return mDescriptors.itemAt(index);
+}
+
+void TSPacketizer::Track::finalize() {
+ if (mFinalized) {
+ return;
+ }
+
+ if (isH264()) {
+ {
+ // AVC video descriptor (40)
+
+ sp<ABuffer> descriptor = new ABuffer(6);
+ uint8_t *data = descriptor->data();
+ data[0] = 40; // descriptor_tag
+ data[1] = 4; // descriptor_length
+
+ CHECK_EQ(mCSD.size(), 1u);
+ const sp<ABuffer> &sps = mCSD.itemAt(0);
+ CHECK(!memcmp("\x00\x00\x00\x01", sps->data(), 4));
+ CHECK_GE(sps->size(), 7u);
+ // profile_idc, constraint_set*, level_idc
+ memcpy(&data[2], sps->data() + 4, 3);
+
+ // AVC_still_present=0, AVC_24_hour_picture_flag=0, reserved
+ data[5] = 0x3f;
+
+ mDescriptors.push_back(descriptor);
+ }
+
+ {
+ // AVC timing and HRD descriptor (42)
+
+ sp<ABuffer> descriptor = new ABuffer(4);
+ uint8_t *data = descriptor->data();
+ data[0] = 42; // descriptor_tag
+ data[1] = 2; // descriptor_length
+
+ // hrd_management_valid_flag = 0
+ // reserved = 111111b
+ // picture_and_timing_info_present = 0
+
+ data[2] = 0x7e;
+
+ // fixed_frame_rate_flag = 0
+ // temporal_poc_flag = 0
+ // picture_to_display_conversion_flag = 0
+ // reserved = 11111b
+ data[3] = 0x1f;
+
+ mDescriptors.push_back(descriptor);
+ }
+ } else if (isPCMAudio()) {
+ // LPCM audio stream descriptor (0x83)
+
+ int32_t channelCount;
+ CHECK(mFormat->findInt32("channel-count", &channelCount));
+ CHECK_EQ(channelCount, 2);
+
+ int32_t sampleRate;
+ CHECK(mFormat->findInt32("sample-rate", &sampleRate));
+ CHECK(sampleRate == 44100 || sampleRate == 48000);
+
+ sp<ABuffer> descriptor = new ABuffer(4);
+ uint8_t *data = descriptor->data();
+ data[0] = 0x83; // descriptor_tag
+ data[1] = 2; // descriptor_length
+
+ unsigned sampling_frequency = (sampleRate == 44100) ? 1 : 2;
+
+ data[2] = (sampling_frequency << 5)
+ | (3 /* reserved */ << 1)
+ | 0 /* emphasis_flag */;
+
+ data[3] =
+ (1 /* number_of_channels = stereo */ << 5)
+ | 0xf /* reserved */;
+
+ mDescriptors.push_back(descriptor);
+ }
+
+ mFinalized = true;
+}
+
////////////////////////////////////////////////////////////////////////////////
TSPacketizer::TSPacketizer()
const sp<ABuffer> &_accessUnit,
sp<ABuffer> *packets,
uint32_t flags,
- const uint8_t *PES_private_data, size_t PES_private_data_len) {
+ const uint8_t *PES_private_data, size_t PES_private_data_len,
+ size_t numStuffingBytes) {
sp<ABuffer> accessUnit = _accessUnit;
int64_t timeUs;
// reserved = b1
// the first fragment of "buffer" follows
- size_t PES_packet_length = accessUnit->size() + 8;
+ size_t PES_packet_length = accessUnit->size() + 8 + numStuffingBytes;
if (PES_private_data_len > 0) {
PES_packet_length += PES_private_data_len + 1;
}
*ptr++ = 0x10 | mPATContinuityCounter;
*ptr++ = 0x00;
- const uint8_t *crcDataStart = ptr;
+ uint8_t *crcDataStart = ptr;
*ptr++ = 0x00;
*ptr++ = 0xb0;
*ptr++ = 0x0d;
mPMTContinuityCounter = 0;
}
- size_t section_length = 5 * mTracks.size() + 4 + 9;
-
ptr = packetDataStart;
*ptr++ = 0x47;
*ptr++ = 0x40 | (kPID_PMT >> 8);
crcDataStart = ptr;
*ptr++ = 0x02;
- *ptr++ = 0xb0 | (section_length >> 8);
- *ptr++ = section_length & 0xff;
+
+ *ptr++ = 0x00; // section_length to be filled in below.
+ *ptr++ = 0x00;
+
*ptr++ = 0x00;
*ptr++ = 0x01;
*ptr++ = 0xc3;
for (size_t i = 0; i < mTracks.size(); ++i) {
const sp<Track> &track = mTracks.itemAt(i);
+ // Make sure all the decriptors have been added.
+ track->finalize();
+
*ptr++ = track->streamType();
*ptr++ = 0xe0 | (track->PID() >> 8);
*ptr++ = track->PID() & 0xff;
- *ptr++ = 0xf0;
- *ptr++ = 0x00;
+
+ size_t ES_info_length = 0;
+ for (size_t i = 0; i < track->countDescriptors(); ++i) {
+ ES_info_length += track->descriptorAt(i)->size();
+ }
+ CHECK_LE(ES_info_length, 0xfff);
+
+ *ptr++ = 0xf0 | (ES_info_length >> 8);
+ *ptr++ = (ES_info_length & 0xff);
+
+ for (size_t i = 0; i < track->countDescriptors(); ++i) {
+ const sp<ABuffer> &descriptor = track->descriptorAt(i);
+ memcpy(ptr, descriptor->data(), descriptor->size());
+ ptr += descriptor->size();
+ }
}
- CHECK_EQ(ptr - crcDataStart, 12 + mTracks.size() * 5);
+ size_t section_length = ptr - (crcDataStart + 3) + 4 /* CRC */;
+
+ crcDataStart[1] = 0xb0 | (section_length >> 8);
+ crcDataStart[2] = section_length & 0xff;
+
crc = htonl(crc32(crcDataStart, ptr - crcDataStart));
memcpy(ptr, &crc, 4);
ptr += 4;
*ptr++ = 0x84;
*ptr++ = (PES_private_data_len > 0) ? 0x81 : 0x80;
- *ptr++ = (PES_private_data_len > 0)
- ? (1 + PES_private_data_len + 0x05) : 0x05;
+ size_t headerLength = 0x05 + numStuffingBytes;
+ if (PES_private_data_len > 0) {
+ headerLength += 1 + PES_private_data_len;
+ }
+
+ *ptr++ = headerLength;
*ptr++ = 0x20 | (((PTS >> 30) & 7) << 1) | 1;
*ptr++ = (PTS >> 22) & 0xff;
ptr += PES_private_data_len;
}
+ for (size_t i = 0; i < numStuffingBytes; ++i) {
+ *ptr++ = 0xff;
+ }
+
// 18 bytes of TS/PES header leave 188 - 18 = 170 bytes for the payload
size_t sizeLeft = packetDataStart + 188 - ptr;
size_t trackIndex, const sp<ABuffer> &accessUnit,
sp<ABuffer> *packets,
uint32_t flags,
- const uint8_t *PES_private_data, size_t PES_private_data_len);
+ const uint8_t *PES_private_data, size_t PES_private_data_len,
+ size_t numStuffingBytes = 0);
// XXX to be removed once encoder config option takes care of this for
// encrypted mode.
#include "PlaybackSession.h"
#include "Parameters.h"
#include "ParsedMessage.h"
+#include "Sender.h"
#include <binder/IServiceManager.h>
#include <gui/ISurfaceTexture.h>
return ERROR_MALFORMED;
}
- PlaybackSession::TransportMode transportMode =
- PlaybackSession::TRANSPORT_UDP;
+ Sender::TransportMode transportMode = Sender::TRANSPORT_UDP;
int clientRtp, clientRtcp;
if (transport.startsWith("RTP/AVP/TCP;")) {
transport.c_str(), "interleaved", &interleaved)
&& sscanf(interleaved.c_str(), "%d-%d",
&clientRtp, &clientRtcp) == 2) {
- transportMode = PlaybackSession::TRANSPORT_TCP_INTERLEAVED;
+ transportMode = Sender::TRANSPORT_TCP_INTERLEAVED;
} else {
bool badRequest = false;
return ERROR_MALFORMED;
}
- transportMode = PlaybackSession::TRANSPORT_TCP;
+ transportMode = Sender::TRANSPORT_TCP;
}
} else if (transport.startsWith("RTP/AVP;unicast;")
|| transport.startsWith("RTP/AVP/UDP;unicast;")) {
AString response = "RTSP/1.0 200 OK\r\n";
AppendCommonResponse(&response, cseq, playbackSessionID);
- if (transportMode == PlaybackSession::TRANSPORT_TCP_INTERLEAVED) {
+ if (transportMode == Sender::TRANSPORT_TCP_INTERLEAVED) {
response.append(
StringPrintf(
"Transport: RTP/AVP/TCP;interleaved=%d-%d;",
int32_t serverRtp = playbackSession->getRTPPort();
AString transportString = "UDP";
- if (transportMode == PlaybackSession::TRANSPORT_TCP) {
+ if (transportMode == Sender::TRANSPORT_TCP) {
transportString = "TCP";
}