blob: b43df38972b1851c7638884dd6508aef6dfce1a0 [file] [log] [blame]
Byeongjo Park870c9d62019-01-24 20:56:37 +09001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "RTPSource"
19#include <utils/Log.h>
20
21#include "RTPSource.h"
22
23
24
25
26#include <media/stagefright/MediaDefs.h>
27#include <media/stagefright/MetaData.h>
28#include <string.h>
29
30namespace android {
31
32const int64_t kNearEOSTimeoutUs = 2000000ll; // 2 secs
33static int32_t kMaxAllowedStaleAccessUnits = 20;
34
35NuPlayer::RTPSource::RTPSource(
36 const sp<AMessage> &notify,
37 const String8& rtpParams)
38 : Source(notify),
39 mRTPParams(rtpParams),
40 mFlags(0),
41 mState(DISCONNECTED),
42 mFinalResult(OK),
43 mBuffering(false),
44 mInPreparationPhase(true),
Kim Sungyeona2c250d2019-03-17 22:04:14 +090045 mRTPConn(new ARTPConnection(ARTPConnection::kViLTEConnection)),
Byeongjo Park870c9d62019-01-24 20:56:37 +090046 mEOSTimeoutAudio(0),
Byeongjo Park8f935262018-04-20 14:00:15 +090047 mEOSTimeoutVideo(0),
Byeongjo Park1a470dd2020-06-05 13:14:19 +090048 mFirstAccessUnit(true),
49 mAllTracksHaveTime(false),
50 mNTPAnchorUs(-1),
51 mMediaAnchorUs(-1),
52 mLastMediaTimeUs(-1),
53 mNumAccessUnitsReceived(0),
54 mLastCVOUpdated(-1),
55 mReceivedFirstRTCPPacket(false),
56 mReceivedFirstRTPPacket(false),
57 mPausing(false),
58 mPauseGeneration(0) {
59 ALOGD("RTPSource initialized with rtpParams=%s", rtpParams.string());
Byeongjo Park870c9d62019-01-24 20:56:37 +090060}
61
62NuPlayer::RTPSource::~RTPSource() {
63 if (mLooper != NULL) {
64 mLooper->unregisterHandler(id());
65 mLooper->unregisterHandler(mRTPConn->id());
66 mLooper->stop();
67 }
68}
69
70status_t NuPlayer::RTPSource::getBufferingSettings(
71 BufferingSettings* buffering /* nonnull */) {
72 Mutex::Autolock _l(mBufferingSettingsLock);
73 *buffering = mBufferingSettings;
74 return OK;
75}
76
77status_t NuPlayer::RTPSource::setBufferingSettings(const BufferingSettings& buffering) {
78 Mutex::Autolock _l(mBufferingSettingsLock);
79 mBufferingSettings = buffering;
80 return OK;
81}
82
83void NuPlayer::RTPSource::prepareAsync() {
84 if (mLooper == NULL) {
85 mLooper = new ALooper;
86 mLooper->setName("rtp");
87 mLooper->start();
88
89 mLooper->registerHandler(this);
90 mLooper->registerHandler(mRTPConn);
91 }
92
Lajos Molnar638df3a2020-03-17 08:13:10 -070093 CHECK_EQ(mState, (int)DISCONNECTED);
94 mState = CONNECTING;
95
Byeongjo Park870c9d62019-01-24 20:56:37 +090096 setParameters(mRTPParams);
97
98 TrackInfo *info = NULL;
99 unsigned i;
100 for (i = 0; i < mTracks.size(); i++) {
101 info = &mTracks.editItemAt(i);
102
103 if (info == NULL)
104 break;
105
106 AString sdp;
107 ASessionDescription::SDPStringFactory(sdp, info->mLocalIp,
108 info->mIsAudio, info->mLocalPort, info->mPayloadType, info->mAS, info->mCodecName,
Byeongjo Park8f935262018-04-20 14:00:15 +0900109 NULL, info->mWidth, info->mHeight, info->mCVOExtMap);
Byeongjo Park870c9d62019-01-24 20:56:37 +0900110 ALOGD("RTPSource SDP =>\n%s", sdp.c_str());
111
112 sp<ASessionDescription> desc = new ASessionDescription;
113 bool isValidSdp = desc->setTo(sdp.c_str(), sdp.size());
114 ALOGV("RTPSource isValidSdp => %d", isValidSdp);
115
116 int sockRtp, sockRtcp;
117 ARTPConnection::MakeRTPSocketPair(&sockRtp, &sockRtcp, info->mLocalIp, info->mRemoteIp,
Byeongjo Parkd1587172019-03-29 15:04:10 +0900118 info->mLocalPort, info->mRemotePort, info->mSocketNetwork);
Byeongjo Park870c9d62019-01-24 20:56:37 +0900119
120 sp<AMessage> notify = new AMessage('accu', this);
121
122 ALOGV("RTPSource addStream. track-index=%d", i);
123 notify->setSize("trackIndex", i);
124 // index(i) should be started from 1. 0 is reserved for [root]
125 mRTPConn->addStream(sockRtp, sockRtcp, desc, i + 1, notify, false);
Kim Sungyeonfc88be82018-03-20 16:51:41 +0900126 mRTPConn->setSelfID(info->mSelfID);
Lajos Molnard9b59572020-08-17 16:53:02 -0700127 mRTPConn->setJbTime(
128 (info->mJbTimeMs <= 3000 && info->mJbTimeMs >= 40) ? info->mJbTimeMs : 300);
Byeongjo Park870c9d62019-01-24 20:56:37 +0900129
130 info->mRTPSocket = sockRtp;
131 info->mRTCPSocket = sockRtcp;
132 info->mFirstSeqNumInSegment = 0;
133 info->mNewSegment = true;
134 info->mAllowedStaleAccessUnits = kMaxAllowedStaleAccessUnits;
135 info->mRTPAnchor = 0;
136 info->mNTPAnchorUs = -1;
137 info->mNormalPlayTimeRTP = 0;
138 info->mNormalPlayTimeUs = 0ll;
139
140 // index(i) should be started from 1. 0 is reserved for [root]
141 info->mPacketSource = new APacketSource(desc, i + 1);
142
143 int32_t timeScale;
144 sp<MetaData> format = getTrackFormat(i, &timeScale);
145 sp<AnotherPacketSource> source = new AnotherPacketSource(format);
146
147 if (info->mIsAudio) {
148 mAudioTrack = source;
Byeongjo Parkc0a4d1a2019-10-16 16:38:24 +0900149 info->mTimeScale = 16000;
Byeongjo Park870c9d62019-01-24 20:56:37 +0900150 } else {
151 mVideoTrack = source;
Byeongjo Parkc0a4d1a2019-10-16 16:38:24 +0900152 info->mTimeScale = 90000;
Byeongjo Park870c9d62019-01-24 20:56:37 +0900153 }
154
155 info->mSource = source;
Byeongjo Parkc0a4d1a2019-10-16 16:38:24 +0900156 info->mRTPTime = 0;
157 info->mNormalPlaytimeUs = 0;
158 info->mNPTMappingValid = false;
Byeongjo Park870c9d62019-01-24 20:56:37 +0900159 }
160
Byeongjo Park870c9d62019-01-24 20:56:37 +0900161 if (mInPreparationPhase) {
162 mInPreparationPhase = false;
163 notifyPrepared();
164 }
165}
166
167void NuPlayer::RTPSource::start() {
168}
169
170void NuPlayer::RTPSource::pause() {
171 mState = PAUSED;
172}
173
174void NuPlayer::RTPSource::resume() {
175 mState = CONNECTING;
176}
177
178void NuPlayer::RTPSource::stop() {
179 if (mLooper == NULL) {
180 return;
181 }
182 sp<AMessage> msg = new AMessage(kWhatDisconnect, this);
183
184 sp<AMessage> dummy;
185 msg->postAndAwaitResponse(&dummy);
186}
187
188status_t NuPlayer::RTPSource::feedMoreTSData() {
189 Mutex::Autolock _l(mBufferingLock);
190 return mFinalResult;
191}
192
193sp<MetaData> NuPlayer::RTPSource::getFormatMeta(bool audio) {
194 sp<AnotherPacketSource> source = getSource(audio);
195
196 if (source == NULL) {
197 return NULL;
198 }
199
200 return source->getFormat();
201}
202
203bool NuPlayer::RTPSource::haveSufficientDataOnAllTracks() {
204 // We're going to buffer at least 2 secs worth data on all tracks before
205 // starting playback (both at startup and after a seek).
206
207 static const int64_t kMinDurationUs = 2000000ll;
208
209 int64_t mediaDurationUs = 0;
210 getDuration(&mediaDurationUs);
211 if ((mAudioTrack != NULL && mAudioTrack->isFinished(mediaDurationUs))
212 || (mVideoTrack != NULL && mVideoTrack->isFinished(mediaDurationUs))) {
213 return true;
214 }
215
216 status_t err;
217 int64_t durationUs;
218 if (mAudioTrack != NULL
219 && (durationUs = mAudioTrack->getBufferedDurationUs(&err))
220 < kMinDurationUs
221 && err == OK) {
222 ALOGV("audio track doesn't have enough data yet. (%.2f secs buffered)",
223 durationUs / 1E6);
224 return false;
225 }
226
227 if (mVideoTrack != NULL
228 && (durationUs = mVideoTrack->getBufferedDurationUs(&err))
229 < kMinDurationUs
230 && err == OK) {
231 ALOGV("video track doesn't have enough data yet. (%.2f secs buffered)",
232 durationUs / 1E6);
233 return false;
234 }
235
236 return true;
237}
238
239status_t NuPlayer::RTPSource::dequeueAccessUnit(
240 bool audio, sp<ABuffer> *accessUnit) {
241
242 sp<AnotherPacketSource> source = getSource(audio);
243
244 if (mState == PAUSED) {
245 ALOGV("-EWOULDBLOCK");
246 return -EWOULDBLOCK;
247 }
248
249 status_t finalResult;
250 if (!source->hasBufferAvailable(&finalResult)) {
251 if (finalResult == OK) {
252 int64_t mediaDurationUs = 0;
253 getDuration(&mediaDurationUs);
254 sp<AnotherPacketSource> otherSource = getSource(!audio);
255 status_t otherFinalResult;
256
257 // If other source already signaled EOS, this source should also signal EOS
258 if (otherSource != NULL &&
259 !otherSource->hasBufferAvailable(&otherFinalResult) &&
260 otherFinalResult == ERROR_END_OF_STREAM) {
261 source->signalEOS(ERROR_END_OF_STREAM);
262 return ERROR_END_OF_STREAM;
263 }
264
265 // If this source has detected near end, give it some time to retrieve more
266 // data before signaling EOS
267 if (source->isFinished(mediaDurationUs)) {
268 int64_t eosTimeout = audio ? mEOSTimeoutAudio : mEOSTimeoutVideo;
269 if (eosTimeout == 0) {
270 setEOSTimeout(audio, ALooper::GetNowUs());
271 } else if ((ALooper::GetNowUs() - eosTimeout) > kNearEOSTimeoutUs) {
272 setEOSTimeout(audio, 0);
273 source->signalEOS(ERROR_END_OF_STREAM);
274 return ERROR_END_OF_STREAM;
275 }
276 return -EWOULDBLOCK;
277 }
278
279 if (!(otherSource != NULL && otherSource->isFinished(mediaDurationUs))) {
280 // We should not enter buffering mode
281 // if any of the sources already have detected EOS.
282 // TODO: needs to be checked whether below line is needed or not.
283 // startBufferingIfNecessary();
284 }
285
286 return -EWOULDBLOCK;
287 }
288 return finalResult;
289 }
290
291 setEOSTimeout(audio, 0);
292
Byeongjo Park8f935262018-04-20 14:00:15 +0900293 finalResult = source->dequeueAccessUnit(accessUnit);
294 if (finalResult != OK) {
295 return finalResult;
296 }
297
298 int32_t cvo;
Byeongjo Park48386612019-05-20 13:15:04 +0900299 if ((*accessUnit) != NULL && (*accessUnit)->meta()->findInt32("cvo", &cvo) &&
300 cvo != mLastCVOUpdated) {
301 sp<AMessage> msg = new AMessage();
Kim Sungyeon89d5bc62020-03-17 21:39:33 +0900302 msg->setInt32("payload-type", ARTPSource::RTP_CVO);
Byeongjo Park48386612019-05-20 13:15:04 +0900303 msg->setInt32("cvo", cvo);
Byeongjo Park8f935262018-04-20 14:00:15 +0900304
Byeongjo Park48386612019-05-20 13:15:04 +0900305 sp<AMessage> notify = dupNotify();
306 notify->setInt32("what", kWhatIMSRxNotice);
307 notify->setMessage("message", msg);
308 notify->post();
Byeongjo Park8f935262018-04-20 14:00:15 +0900309
Byeongjo Park48386612019-05-20 13:15:04 +0900310 ALOGV("notify cvo updated (%d)->(%d) to upper layer", mLastCVOUpdated, cvo);
311 mLastCVOUpdated = cvo;
Byeongjo Park8f935262018-04-20 14:00:15 +0900312 }
313
314 return finalResult;
Byeongjo Park870c9d62019-01-24 20:56:37 +0900315}
316
317sp<AnotherPacketSource> NuPlayer::RTPSource::getSource(bool audio) {
318 return audio ? mAudioTrack : mVideoTrack;
319}
320
321void NuPlayer::RTPSource::setEOSTimeout(bool audio, int64_t timeout) {
322 if (audio) {
323 mEOSTimeoutAudio = timeout;
324 } else {
325 mEOSTimeoutVideo = timeout;
326 }
327}
328
329status_t NuPlayer::RTPSource::getDuration(int64_t *durationUs) {
330 *durationUs = 0ll;
331
332 int64_t audioDurationUs;
333 if (mAudioTrack != NULL
334 && mAudioTrack->getFormat()->findInt64(
335 kKeyDuration, &audioDurationUs)
336 && audioDurationUs > *durationUs) {
337 *durationUs = audioDurationUs;
338 }
339
340 int64_t videoDurationUs;
341 if (mVideoTrack != NULL
342 && mVideoTrack->getFormat()->findInt64(
343 kKeyDuration, &videoDurationUs)
344 && videoDurationUs > *durationUs) {
345 *durationUs = videoDurationUs;
346 }
347
348 return OK;
349}
350
351status_t NuPlayer::RTPSource::seekTo(int64_t seekTimeUs, MediaPlayerSeekMode mode) {
352 ALOGV("RTPSource::seekTo=%d, mode=%d", (int)seekTimeUs, mode);
353 return OK;
354}
355
356void NuPlayer::RTPSource::schedulePollBuffering() {
357 sp<AMessage> msg = new AMessage(kWhatPollBuffering, this);
Lajos Molnar638df3a2020-03-17 08:13:10 -0700358 msg->post(kBufferingPollIntervalUs); // 1 second intervals
Byeongjo Park870c9d62019-01-24 20:56:37 +0900359}
360
361void NuPlayer::RTPSource::onPollBuffering() {
362 schedulePollBuffering();
363}
364
Byeongjo Parkc0a4d1a2019-10-16 16:38:24 +0900365bool NuPlayer::RTPSource::isRealTime() const {
366 ALOGD("RTPSource::isRealTime=%d", true);
367 return true;
368}
369
Byeongjo Park870c9d62019-01-24 20:56:37 +0900370void NuPlayer::RTPSource::onMessageReceived(const sp<AMessage> &msg) {
371 ALOGV("onMessageReceived =%d", msg->what());
372
373 switch (msg->what()) {
374 case kWhatAccessUnitComplete:
375 {
376 if (mState == CONNECTING) {
377 mState = CONNECTED;
378 }
379
380 int32_t timeUpdate;
381 //"time-update" raised from ARTPConnection::parseSR()
382 if (msg->findInt32("time-update", &timeUpdate) && timeUpdate) {
383 size_t trackIndex;
384 CHECK(msg->findSize("trackIndex", &trackIndex));
385
386 uint32_t rtpTime;
387 uint64_t ntpTime;
388 CHECK(msg->findInt32("rtp-time", (int32_t *)&rtpTime));
389 CHECK(msg->findInt64("ntp-time", (int64_t *)&ntpTime));
390
391 onTimeUpdate(trackIndex, rtpTime, ntpTime);
392 break;
393 }
394
395 int32_t firstRTCP;
396 if (msg->findInt32("first-rtcp", &firstRTCP)) {
397 // There won't be an access unit here, it's just a notification
398 // that the data communication worked since we got the first
399 // rtcp packet.
400 ALOGV("first-rtcp");
401 break;
402 }
403
Kim Sungyeone40c5df2018-03-02 14:41:19 +0900404 int32_t IMSRxNotice;
Byeongjo Parka5407762018-08-23 16:19:58 +0900405 if (msg->findInt32("rtcp-event", &IMSRxNotice)) {
Kim Sungyeone40c5df2018-03-02 14:41:19 +0900406 int32_t payloadType, feedbackType;
407 CHECK(msg->findInt32("payload-type", &payloadType));
408 CHECK(msg->findInt32("feedback-type", &feedbackType));
409
410 sp<AMessage> notify = dupNotify();
411 notify->setInt32("what", kWhatIMSRxNotice);
412 notify->setMessage("message", msg);
413 notify->post();
414
415 ALOGV("IMSRxNotice \t\t payload : %d feedback : %d",
416 payloadType, feedbackType);
417 break;
418 }
419
Byeongjo Park870c9d62019-01-24 20:56:37 +0900420 size_t trackIndex;
421 CHECK(msg->findSize("trackIndex", &trackIndex));
422
423 sp<ABuffer> accessUnit;
424 if (msg->findBuffer("access-unit", &accessUnit) == false) {
425 break;
426 }
427
428 int32_t damaged;
429 if (accessUnit->meta()->findInt32("damaged", &damaged)
430 && damaged) {
431 ALOGD("dropping damaged access unit.");
432 break;
433 }
434
Lajos Molnar638df3a2020-03-17 08:13:10 -0700435 // Implicitly assert on valid trackIndex here, which we ensure by
436 // never removing tracks.
Byeongjo Park870c9d62019-01-24 20:56:37 +0900437 TrackInfo *info = &mTracks.editItemAt(trackIndex);
438
439 sp<AnotherPacketSource> source = info->mSource;
440 if (source != NULL) {
441 uint32_t rtpTime;
442 CHECK(accessUnit->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
443
444 /* AnotherPacketSource make an assertion if there is no ntp provided
445 RTPSource should provide ntpUs all the times.
446 if (!info->mNPTMappingValid) {
447 // This is a live stream, we didn't receive any normal
448 // playtime mapping. We won't map to npt time.
449 source->queueAccessUnit(accessUnit);
450 break;
451 }
Byeongjo Park870c9d62019-01-24 20:56:37 +0900452
453 int64_t nptUs =
454 ((double)rtpTime - (double)info->mRTPTime)
455 / info->mTimeScale
456 * 1000000ll
457 + info->mNormalPlaytimeUs;
458
Byeongjo Parkc0a4d1a2019-10-16 16:38:24 +0900459 */
460 accessUnit->meta()->setInt64("timeUs", ALooper::GetNowUs());
Byeongjo Park870c9d62019-01-24 20:56:37 +0900461
462 source->queueAccessUnit(accessUnit);
463 }
464
465 break;
466 }
467 case kWhatDisconnect:
468 {
469 sp<AReplyToken> replyID;
470 CHECK(msg->senderAwaitsResponse(&replyID));
471
472 for (size_t i = 0; i < mTracks.size(); ++i) {
473 TrackInfo *info = &mTracks.editItemAt(i);
474
475 if (info->mIsAudio) {
476 mAudioTrack->signalEOS(ERROR_END_OF_STREAM);
477 mAudioTrack = NULL;
478 ALOGV("mAudioTrack disconnected");
479 } else {
480 mVideoTrack->signalEOS(ERROR_END_OF_STREAM);
481 mVideoTrack = NULL;
482 ALOGV("mVideoTrack disconnected");
483 }
484
485 mRTPConn->removeStream(info->mRTPSocket, info->mRTCPSocket);
486 close(info->mRTPSocket);
487 close(info->mRTCPSocket);
488 }
489
490 mTracks.clear();
491 mFirstAccessUnit = true;
492 mAllTracksHaveTime = false;
493 mNTPAnchorUs = -1;
494 mMediaAnchorUs = -1;
495 mLastMediaTimeUs = -1;
496 mNumAccessUnitsReceived = 0;
497 mReceivedFirstRTCPPacket = false;
498 mReceivedFirstRTPPacket = false;
499 mPausing = false;
500 mPauseGeneration = 0;
501
502 (new AMessage)->postReply(replyID);
503
504 break;
505 }
506 case kWhatPollBuffering:
507 break;
508 default:
509 TRESPASS();
510 }
511}
512
Kim Sungyeona6d72e52019-07-18 17:48:32 +0900513void NuPlayer::RTPSource::setTargetBitrate(int32_t bitrate) {
514 mRTPConn->setTargetBitrate(bitrate);
515}
516
Byeongjo Park870c9d62019-01-24 20:56:37 +0900517void NuPlayer::RTPSource::onTimeUpdate(int32_t trackIndex, uint32_t rtpTime, uint64_t ntpTime) {
518 ALOGV("onTimeUpdate track %d, rtpTime = 0x%08x, ntpTime = %#016llx",
519 trackIndex, rtpTime, (long long)ntpTime);
520
Lajos Molnar638df3a2020-03-17 08:13:10 -0700521 // convert ntpTime in Q32 seconds to microseconds. Note: this will not lose precision
522 // because ntpTimeUs is at most 52 bits (double holds 53 bits)
Byeongjo Park870c9d62019-01-24 20:56:37 +0900523 int64_t ntpTimeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
524
525 TrackInfo *track = &mTracks.editItemAt(trackIndex);
526
527 track->mRTPAnchor = rtpTime;
528 track->mNTPAnchorUs = ntpTimeUs;
529
530 if (mNTPAnchorUs < 0) {
531 mNTPAnchorUs = ntpTimeUs;
532 mMediaAnchorUs = mLastMediaTimeUs;
533 }
534
535 if (!mAllTracksHaveTime) {
536 bool allTracksHaveTime = (mTracks.size() > 0);
537 for (size_t i = 0; i < mTracks.size(); ++i) {
538 TrackInfo *track = &mTracks.editItemAt(i);
539 if (track->mNTPAnchorUs < 0) {
540 allTracksHaveTime = false;
541 break;
542 }
543 }
544 if (allTracksHaveTime) {
545 mAllTracksHaveTime = true;
546 ALOGI("Time now established for all tracks.");
547 }
548 }
549 if (mAllTracksHaveTime && dataReceivedOnAllChannels()) {
550 // Time is now established, lets start timestamping immediately
551 for (size_t i = 0; i < mTracks.size(); ++i) {
552 TrackInfo *trackInfo = &mTracks.editItemAt(i);
553 while (!trackInfo->mPackets.empty()) {
554 sp<ABuffer> accessUnit = *trackInfo->mPackets.begin();
555 trackInfo->mPackets.erase(trackInfo->mPackets.begin());
556
557 if (addMediaTimestamp(i, trackInfo, accessUnit)) {
558 postQueueAccessUnit(i, accessUnit);
559 }
560 }
561 }
562 }
563}
564
565bool NuPlayer::RTPSource::addMediaTimestamp(
566 int32_t trackIndex, const TrackInfo *track,
567 const sp<ABuffer> &accessUnit) {
568
569 uint32_t rtpTime;
570 CHECK(accessUnit->meta()->findInt32(
571 "rtp-time", (int32_t *)&rtpTime));
572
573 int64_t relRtpTimeUs =
574 (((int64_t)rtpTime - (int64_t)track->mRTPAnchor) * 1000000ll)
575 / track->mTimeScale;
576
577 int64_t ntpTimeUs = track->mNTPAnchorUs + relRtpTimeUs;
578
579 int64_t mediaTimeUs = mMediaAnchorUs + ntpTimeUs - mNTPAnchorUs;
580
581 if (mediaTimeUs > mLastMediaTimeUs) {
582 mLastMediaTimeUs = mediaTimeUs;
583 }
584
585 if (mediaTimeUs < 0) {
586 ALOGV("dropping early accessUnit.");
587 return false;
588 }
589
590 ALOGV("track %d rtpTime=%u mediaTimeUs = %lld us (%.2f secs)",
591 trackIndex, rtpTime, (long long)mediaTimeUs, mediaTimeUs / 1E6);
592
593 accessUnit->meta()->setInt64("timeUs", mediaTimeUs);
594
595 return true;
596}
597
598bool NuPlayer::RTPSource::dataReceivedOnAllChannels() {
599 TrackInfo *track;
600 for (size_t i = 0; i < mTracks.size(); ++i) {
601 track = &mTracks.editItemAt(i);
602 if (track->mPackets.empty()) {
603 return false;
604 }
605 }
606 return true;
607}
608
609void NuPlayer::RTPSource::postQueueAccessUnit(
610 size_t trackIndex, const sp<ABuffer> &accessUnit) {
611 sp<AMessage> msg = new AMessage(kWhatAccessUnit, this);
612 msg->setInt32("what", kWhatAccessUnit);
613 msg->setSize("trackIndex", trackIndex);
614 msg->setBuffer("accessUnit", accessUnit);
615 msg->post();
616}
617
618void NuPlayer::RTPSource::postQueueEOS(size_t trackIndex, status_t finalResult) {
619 sp<AMessage> msg = new AMessage(kWhatEOS, this);
620 msg->setInt32("what", kWhatEOS);
621 msg->setSize("trackIndex", trackIndex);
622 msg->setInt32("finalResult", finalResult);
623 msg->post();
624}
625
626sp<MetaData> NuPlayer::RTPSource::getTrackFormat(size_t index, int32_t *timeScale) {
627 CHECK_GE(index, 0u);
628 CHECK_LT(index, mTracks.size());
629
630 const TrackInfo &info = mTracks.itemAt(index);
631
632 *timeScale = info.mTimeScale;
633
634 return info.mPacketSource->getFormat();
635}
636
637void NuPlayer::RTPSource::onConnected() {
638 ALOGV("onConnected");
639 mState = CONNECTED;
640}
641
642void NuPlayer::RTPSource::onDisconnected(const sp<AMessage> &msg) {
643 if (mState == DISCONNECTED) {
644 return;
645 }
646
647 status_t err;
648 CHECK(msg->findInt32("result", &err));
649 CHECK_NE(err, (status_t)OK);
650
651// mLooper->unregisterHandler(mHandler->id());
652// mHandler.clear();
653
654 if (mState == CONNECTING) {
655 // We're still in the preparation phase, signal that it
656 // failed.
657 notifyPrepared(err);
658 }
659
660 mState = DISCONNECTED;
661// setError(err);
662
663}
664
665status_t NuPlayer::RTPSource::setParameter(const String8 &key, const String8 &value) {
666 ALOGV("setParameter: key (%s) => value (%s)", key.string(), value.string());
667
668 bool isAudioKey = key.contains("audio");
669 TrackInfo *info = NULL;
670 for (unsigned i = 0; i < mTracks.size(); ++i) {
671 info = &mTracks.editItemAt(i);
672 if (info != NULL && info->mIsAudio == isAudioKey) {
673 ALOGV("setParameter: %s track (%d) found", isAudioKey ? "audio" : "video" , i);
674 break;
675 }
676 }
677
678 if (info == NULL) {
679 TrackInfo newTrackInfo;
680 newTrackInfo.mIsAudio = isAudioKey;
681 mTracks.push(newTrackInfo);
682 info = &mTracks.editTop();
Lajos Molnard9b59572020-08-17 16:53:02 -0700683 info->mJbTimeMs = 300;
Byeongjo Park870c9d62019-01-24 20:56:37 +0900684 }
685
686 if (key == "rtp-param-mime-type") {
687 info->mMimeType = value;
688
689 const char *mime = value.string();
690 const char *delimiter = strchr(mime, '/');
Lajos Molnar638df3a2020-03-17 08:13:10 -0700691 info->mCodecName = delimiter ? (delimiter + 1) : "<none>";
Byeongjo Park870c9d62019-01-24 20:56:37 +0900692
693 ALOGV("rtp-param-mime-type: mMimeType (%s) => mCodecName (%s)",
Lajos Molnar638df3a2020-03-17 08:13:10 -0700694 info->mMimeType.string(), info->mCodecName.string());
Byeongjo Park870c9d62019-01-24 20:56:37 +0900695 } else if (key == "video-param-decoder-profile") {
696 info->mCodecProfile = atoi(value);
697 } else if (key == "video-param-decoder-level") {
698 info->mCodecLevel = atoi(value);
699 } else if (key == "video-param-width") {
700 info->mWidth = atoi(value);
701 } else if (key == "video-param-height") {
702 info->mHeight = atoi(value);
703 } else if (key == "rtp-param-local-ip") {
704 info->mLocalIp = value;
705 } else if (key == "rtp-param-local-port") {
706 info->mLocalPort = atoi(value);
707 } else if (key == "rtp-param-remote-ip") {
708 info->mRemoteIp = value;
709 } else if (key == "rtp-param-remote-port") {
710 info->mRemotePort = atoi(value);
711 } else if (key == "rtp-param-payload-type") {
712 info->mPayloadType = atoi(value);
713 } else if (key == "rtp-param-as") {
714 //AS means guaranteed bit rate that negotiated from sdp.
715 info->mAS = atoi(value);
716 } else if (key == "rtp-param-rtp-timeout") {
717 } else if (key == "rtp-param-rtcp-timeout") {
718 } else if (key == "rtp-param-time-scale") {
Kim Sungyeonfc88be82018-03-20 16:51:41 +0900719 } else if (key == "rtp-param-self-id") {
720 info->mSelfID = atoi(value);
Byeongjo Park8f935262018-04-20 14:00:15 +0900721 } else if (key == "rtp-param-ext-cvo-extmap") {
722 info->mCVOExtMap = atoi(value);
Byeongjo Parkd1587172019-03-29 15:04:10 +0900723 } else if (key == "rtp-param-set-socket-network") {
724 int64_t networkHandle = atoll(value);
725 setSocketNetwork(networkHandle);
Byeongjo Park75adcb72019-09-23 13:31:37 +0900726 } else if (key == "rtp-param-jitter-buffer-time") {
Lajos Molnard9b59572020-08-17 16:53:02 -0700727 info->mJbTimeMs = atoi(value);
Byeongjo Park870c9d62019-01-24 20:56:37 +0900728 }
729
730 return OK;
731}
732
733status_t NuPlayer::RTPSource::setParameters(const String8 &params) {
734 ALOGV("setParameters: %s", params.string());
735 const char *cparams = params.string();
736 const char *key_start = cparams;
737 for (;;) {
738 const char *equal_pos = strchr(key_start, '=');
739 if (equal_pos == NULL) {
740 ALOGE("Parameters %s miss a value", cparams);
741 return BAD_VALUE;
742 }
743 String8 key(key_start, equal_pos - key_start);
744 TrimString(&key);
745 if (key.length() == 0) {
746 ALOGE("Parameters %s contains an empty key", cparams);
747 return BAD_VALUE;
748 }
749 const char *value_start = equal_pos + 1;
750 const char *semicolon_pos = strchr(value_start, ';');
751 String8 value;
752 if (semicolon_pos == NULL) {
753 value.setTo(value_start);
754 } else {
755 value.setTo(value_start, semicolon_pos - value_start);
756 }
757 if (setParameter(key, value) != OK) {
758 return BAD_VALUE;
759 }
760 if (semicolon_pos == NULL) {
761 break; // Reaches the end
762 }
763 key_start = semicolon_pos + 1;
764 }
765 return OK;
766}
767
Byeongjo Parkd1587172019-03-29 15:04:10 +0900768void NuPlayer::RTPSource::setSocketNetwork(int64_t networkHandle) {
769 ALOGV("setSocketNetwork: %llu", (unsigned long long)networkHandle);
770
771 TrackInfo *info = NULL;
772 for (size_t i = 0; i < mTracks.size(); ++i) {
773 info = &mTracks.editItemAt(i);
774
775 if (info == NULL)
776 break;
777
778 info->mSocketNetwork = networkHandle;
779 }
780}
781
Byeongjo Park870c9d62019-01-24 20:56:37 +0900782// Trim both leading and trailing whitespace from the given string.
783//static
784void NuPlayer::RTPSource::TrimString(String8 *s) {
785 size_t num_bytes = s->bytes();
786 const char *data = s->string();
787
788 size_t leading_space = 0;
789 while (leading_space < num_bytes && isspace(data[leading_space])) {
790 ++leading_space;
791 }
792
793 size_t i = num_bytes;
794 while (i > leading_space && isspace(data[i - 1])) {
795 --i;
796 }
797
798 s->setTo(String8(&data[leading_space], i - leading_space));
799}
800
801} // namespace android