blob: 3c3ad86af6ba4263976a41bf395a7c7b1df760f8 [file] [log] [blame]
Byeongjo Parkd157b792019-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 Sungyeon2c987f12019-03-17 22:04:14 +090045 mRTPConn(new ARTPConnection(ARTPConnection::kViLTEConnection)),
Byeongjo Parkd157b792019-01-24 20:56:37 +090046 mEOSTimeoutAudio(0),
Byeongjo Parkb0225aa2018-04-20 14:00:15 +090047 mEOSTimeoutVideo(0),
48 mLastCVOUpdated(-1) {
Byeongjo Parkd157b792019-01-24 20:56:37 +090049 ALOGD("RTPSource initialized with rtpParams=%s", rtpParams.string());
50}
51
52NuPlayer::RTPSource::~RTPSource() {
53 if (mLooper != NULL) {
54 mLooper->unregisterHandler(id());
55 mLooper->unregisterHandler(mRTPConn->id());
56 mLooper->stop();
57 }
58}
59
60status_t NuPlayer::RTPSource::getBufferingSettings(
61 BufferingSettings* buffering /* nonnull */) {
62 Mutex::Autolock _l(mBufferingSettingsLock);
63 *buffering = mBufferingSettings;
64 return OK;
65}
66
67status_t NuPlayer::RTPSource::setBufferingSettings(const BufferingSettings& buffering) {
68 Mutex::Autolock _l(mBufferingSettingsLock);
69 mBufferingSettings = buffering;
70 return OK;
71}
72
73void NuPlayer::RTPSource::prepareAsync() {
74 if (mLooper == NULL) {
75 mLooper = new ALooper;
76 mLooper->setName("rtp");
77 mLooper->start();
78
79 mLooper->registerHandler(this);
80 mLooper->registerHandler(mRTPConn);
81 }
82
Lajos Molnar19829332020-03-17 08:13:10 -070083 CHECK_EQ(mState, (int)DISCONNECTED);
84 mState = CONNECTING;
85
Byeongjo Parkd157b792019-01-24 20:56:37 +090086 setParameters(mRTPParams);
87
88 TrackInfo *info = NULL;
89 unsigned i;
90 for (i = 0; i < mTracks.size(); i++) {
91 info = &mTracks.editItemAt(i);
92
93 if (info == NULL)
94 break;
95
96 AString sdp;
97 ASessionDescription::SDPStringFactory(sdp, info->mLocalIp,
98 info->mIsAudio, info->mLocalPort, info->mPayloadType, info->mAS, info->mCodecName,
Byeongjo Parkb0225aa2018-04-20 14:00:15 +090099 NULL, info->mWidth, info->mHeight, info->mCVOExtMap);
Byeongjo Parkd157b792019-01-24 20:56:37 +0900100 ALOGD("RTPSource SDP =>\n%s", sdp.c_str());
101
102 sp<ASessionDescription> desc = new ASessionDescription;
103 bool isValidSdp = desc->setTo(sdp.c_str(), sdp.size());
104 ALOGV("RTPSource isValidSdp => %d", isValidSdp);
105
106 int sockRtp, sockRtcp;
107 ARTPConnection::MakeRTPSocketPair(&sockRtp, &sockRtcp, info->mLocalIp, info->mRemoteIp,
Byeongjo Park88068dc2019-03-29 15:04:10 +0900108 info->mLocalPort, info->mRemotePort, info->mSocketNetwork);
Byeongjo Parkd157b792019-01-24 20:56:37 +0900109
110 sp<AMessage> notify = new AMessage('accu', this);
111
112 ALOGV("RTPSource addStream. track-index=%d", i);
113 notify->setSize("trackIndex", i);
114 // index(i) should be started from 1. 0 is reserved for [root]
115 mRTPConn->addStream(sockRtp, sockRtcp, desc, i + 1, notify, false);
Kim Sungyeond2875e92018-03-20 16:51:41 +0900116 mRTPConn->setSelfID(info->mSelfID);
Byeongjo Park7143c072019-09-23 13:31:37 +0900117 mRTPConn->setJbTime((info->mJbTime <= 3000 && info->mJbTime >= 40) ? info->mJbTime : 300);
Lajos Molnar19829332020-03-17 08:13:10 -0700118 mRTPConn->setMinMaxBitrate(kMinVideoBitrate, info->mAS * 1000 /* kbps */);
Byeongjo Parkd157b792019-01-24 20:56:37 +0900119
120 info->mRTPSocket = sockRtp;
121 info->mRTCPSocket = sockRtcp;
122 info->mFirstSeqNumInSegment = 0;
123 info->mNewSegment = true;
124 info->mAllowedStaleAccessUnits = kMaxAllowedStaleAccessUnits;
125 info->mRTPAnchor = 0;
126 info->mNTPAnchorUs = -1;
127 info->mNormalPlayTimeRTP = 0;
128 info->mNormalPlayTimeUs = 0ll;
129
130 // index(i) should be started from 1. 0 is reserved for [root]
131 info->mPacketSource = new APacketSource(desc, i + 1);
132
133 int32_t timeScale;
134 sp<MetaData> format = getTrackFormat(i, &timeScale);
135 sp<AnotherPacketSource> source = new AnotherPacketSource(format);
136
137 if (info->mIsAudio) {
138 mAudioTrack = source;
139 } else {
140 mVideoTrack = source;
141 }
142
143 info->mSource = source;
144 }
145
Byeongjo Parkd157b792019-01-24 20:56:37 +0900146 if (mInPreparationPhase) {
147 mInPreparationPhase = false;
148 notifyPrepared();
149 }
150}
151
152void NuPlayer::RTPSource::start() {
153}
154
155void NuPlayer::RTPSource::pause() {
156 mState = PAUSED;
157}
158
159void NuPlayer::RTPSource::resume() {
160 mState = CONNECTING;
161}
162
163void NuPlayer::RTPSource::stop() {
164 if (mLooper == NULL) {
165 return;
166 }
167 sp<AMessage> msg = new AMessage(kWhatDisconnect, this);
168
169 sp<AMessage> dummy;
170 msg->postAndAwaitResponse(&dummy);
171}
172
173status_t NuPlayer::RTPSource::feedMoreTSData() {
174 Mutex::Autolock _l(mBufferingLock);
175 return mFinalResult;
176}
177
178sp<MetaData> NuPlayer::RTPSource::getFormatMeta(bool audio) {
179 sp<AnotherPacketSource> source = getSource(audio);
180
181 if (source == NULL) {
182 return NULL;
183 }
184
185 return source->getFormat();
186}
187
188bool NuPlayer::RTPSource::haveSufficientDataOnAllTracks() {
189 // We're going to buffer at least 2 secs worth data on all tracks before
190 // starting playback (both at startup and after a seek).
191
192 static const int64_t kMinDurationUs = 2000000ll;
193
194 int64_t mediaDurationUs = 0;
195 getDuration(&mediaDurationUs);
196 if ((mAudioTrack != NULL && mAudioTrack->isFinished(mediaDurationUs))
197 || (mVideoTrack != NULL && mVideoTrack->isFinished(mediaDurationUs))) {
198 return true;
199 }
200
201 status_t err;
202 int64_t durationUs;
203 if (mAudioTrack != NULL
204 && (durationUs = mAudioTrack->getBufferedDurationUs(&err))
205 < kMinDurationUs
206 && err == OK) {
207 ALOGV("audio track doesn't have enough data yet. (%.2f secs buffered)",
208 durationUs / 1E6);
209 return false;
210 }
211
212 if (mVideoTrack != NULL
213 && (durationUs = mVideoTrack->getBufferedDurationUs(&err))
214 < kMinDurationUs
215 && err == OK) {
216 ALOGV("video track doesn't have enough data yet. (%.2f secs buffered)",
217 durationUs / 1E6);
218 return false;
219 }
220
221 return true;
222}
223
224status_t NuPlayer::RTPSource::dequeueAccessUnit(
225 bool audio, sp<ABuffer> *accessUnit) {
226
227 sp<AnotherPacketSource> source = getSource(audio);
228
229 if (mState == PAUSED) {
230 ALOGV("-EWOULDBLOCK");
231 return -EWOULDBLOCK;
232 }
233
234 status_t finalResult;
235 if (!source->hasBufferAvailable(&finalResult)) {
236 if (finalResult == OK) {
237 int64_t mediaDurationUs = 0;
238 getDuration(&mediaDurationUs);
239 sp<AnotherPacketSource> otherSource = getSource(!audio);
240 status_t otherFinalResult;
241
242 // If other source already signaled EOS, this source should also signal EOS
243 if (otherSource != NULL &&
244 !otherSource->hasBufferAvailable(&otherFinalResult) &&
245 otherFinalResult == ERROR_END_OF_STREAM) {
246 source->signalEOS(ERROR_END_OF_STREAM);
247 return ERROR_END_OF_STREAM;
248 }
249
250 // If this source has detected near end, give it some time to retrieve more
251 // data before signaling EOS
252 if (source->isFinished(mediaDurationUs)) {
253 int64_t eosTimeout = audio ? mEOSTimeoutAudio : mEOSTimeoutVideo;
254 if (eosTimeout == 0) {
255 setEOSTimeout(audio, ALooper::GetNowUs());
256 } else if ((ALooper::GetNowUs() - eosTimeout) > kNearEOSTimeoutUs) {
257 setEOSTimeout(audio, 0);
258 source->signalEOS(ERROR_END_OF_STREAM);
259 return ERROR_END_OF_STREAM;
260 }
261 return -EWOULDBLOCK;
262 }
263
264 if (!(otherSource != NULL && otherSource->isFinished(mediaDurationUs))) {
265 // We should not enter buffering mode
266 // if any of the sources already have detected EOS.
267 // TODO: needs to be checked whether below line is needed or not.
268 // startBufferingIfNecessary();
269 }
270
271 return -EWOULDBLOCK;
272 }
273 return finalResult;
274 }
275
276 setEOSTimeout(audio, 0);
277
Byeongjo Parkb0225aa2018-04-20 14:00:15 +0900278 finalResult = source->dequeueAccessUnit(accessUnit);
279 if (finalResult != OK) {
280 return finalResult;
281 }
282
283 int32_t cvo;
284 if ((*accessUnit) != NULL && (*accessUnit)->meta()->findInt32("cvo", &cvo)) {
285 if (cvo != mLastCVOUpdated) {
286 sp<AMessage> msg = new AMessage();
287 msg->setInt32("payload-type", NuPlayer::RTPSource::RTP_CVO);
288 msg->setInt32("cvo", cvo);
289
290 sp<AMessage> notify = dupNotify();
291 notify->setInt32("what", kWhatIMSRxNotice);
292 notify->setMessage("message", msg);
293 notify->post();
294
295 ALOGV("notify cvo updated (%d)->(%d) to upper layer", mLastCVOUpdated, cvo);
296 mLastCVOUpdated = cvo;
297 }
298 }
299
300 return finalResult;
Byeongjo Parkd157b792019-01-24 20:56:37 +0900301}
302
303sp<AnotherPacketSource> NuPlayer::RTPSource::getSource(bool audio) {
304 return audio ? mAudioTrack : mVideoTrack;
305}
306
307void NuPlayer::RTPSource::setEOSTimeout(bool audio, int64_t timeout) {
308 if (audio) {
309 mEOSTimeoutAudio = timeout;
310 } else {
311 mEOSTimeoutVideo = timeout;
312 }
313}
314
315status_t NuPlayer::RTPSource::getDuration(int64_t *durationUs) {
316 *durationUs = 0ll;
317
318 int64_t audioDurationUs;
319 if (mAudioTrack != NULL
320 && mAudioTrack->getFormat()->findInt64(
321 kKeyDuration, &audioDurationUs)
322 && audioDurationUs > *durationUs) {
323 *durationUs = audioDurationUs;
324 }
325
326 int64_t videoDurationUs;
327 if (mVideoTrack != NULL
328 && mVideoTrack->getFormat()->findInt64(
329 kKeyDuration, &videoDurationUs)
330 && videoDurationUs > *durationUs) {
331 *durationUs = videoDurationUs;
332 }
333
334 return OK;
335}
336
337status_t NuPlayer::RTPSource::seekTo(int64_t seekTimeUs, MediaPlayerSeekMode mode) {
338 ALOGV("RTPSource::seekTo=%d, mode=%d", (int)seekTimeUs, mode);
339 return OK;
340}
341
342void NuPlayer::RTPSource::schedulePollBuffering() {
343 sp<AMessage> msg = new AMessage(kWhatPollBuffering, this);
Lajos Molnar19829332020-03-17 08:13:10 -0700344 msg->post(kBufferingPollIntervalUs); // 1 second intervals
Byeongjo Parkd157b792019-01-24 20:56:37 +0900345}
346
347void NuPlayer::RTPSource::onPollBuffering() {
348 schedulePollBuffering();
349}
350
351void NuPlayer::RTPSource::onMessageReceived(const sp<AMessage> &msg) {
352 ALOGV("onMessageReceived =%d", msg->what());
353
354 switch (msg->what()) {
355 case kWhatAccessUnitComplete:
356 {
357 if (mState == CONNECTING) {
358 mState = CONNECTED;
359 }
360
361 int32_t timeUpdate;
362 //"time-update" raised from ARTPConnection::parseSR()
363 if (msg->findInt32("time-update", &timeUpdate) && timeUpdate) {
364 size_t trackIndex;
365 CHECK(msg->findSize("trackIndex", &trackIndex));
366
367 uint32_t rtpTime;
368 uint64_t ntpTime;
369 CHECK(msg->findInt32("rtp-time", (int32_t *)&rtpTime));
370 CHECK(msg->findInt64("ntp-time", (int64_t *)&ntpTime));
371
372 onTimeUpdate(trackIndex, rtpTime, ntpTime);
373 break;
374 }
375
376 int32_t firstRTCP;
377 if (msg->findInt32("first-rtcp", &firstRTCP)) {
378 // There won't be an access unit here, it's just a notification
379 // that the data communication worked since we got the first
380 // rtcp packet.
381 ALOGV("first-rtcp");
382 break;
383 }
384
Kim Sungyeond3c6b322018-03-02 14:41:19 +0900385 int32_t IMSRxNotice;
Byeongjo Park73f6fc82018-08-23 16:19:58 +0900386 if (msg->findInt32("rtcp-event", &IMSRxNotice)) {
Kim Sungyeond3c6b322018-03-02 14:41:19 +0900387 int32_t payloadType, feedbackType;
388 CHECK(msg->findInt32("payload-type", &payloadType));
389 CHECK(msg->findInt32("feedback-type", &feedbackType));
390
391 sp<AMessage> notify = dupNotify();
392 notify->setInt32("what", kWhatIMSRxNotice);
393 notify->setMessage("message", msg);
394 notify->post();
395
396 ALOGV("IMSRxNotice \t\t payload : %d feedback : %d",
397 payloadType, feedbackType);
398 break;
399 }
400
Byeongjo Parkd157b792019-01-24 20:56:37 +0900401 size_t trackIndex;
402 CHECK(msg->findSize("trackIndex", &trackIndex));
403
404 sp<ABuffer> accessUnit;
405 if (msg->findBuffer("access-unit", &accessUnit) == false) {
406 break;
407 }
408
409 int32_t damaged;
410 if (accessUnit->meta()->findInt32("damaged", &damaged)
411 && damaged) {
412 ALOGD("dropping damaged access unit.");
413 break;
414 }
415
Lajos Molnar19829332020-03-17 08:13:10 -0700416 // Implicitly assert on valid trackIndex here, which we ensure by
417 // never removing tracks.
Byeongjo Parkd157b792019-01-24 20:56:37 +0900418 TrackInfo *info = &mTracks.editItemAt(trackIndex);
419
420 sp<AnotherPacketSource> source = info->mSource;
421 if (source != NULL) {
422 uint32_t rtpTime;
423 CHECK(accessUnit->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
424
425 /* AnotherPacketSource make an assertion if there is no ntp provided
426 RTPSource should provide ntpUs all the times.
427 if (!info->mNPTMappingValid) {
428 // This is a live stream, we didn't receive any normal
429 // playtime mapping. We won't map to npt time.
430 source->queueAccessUnit(accessUnit);
431 break;
432 }
433 */
434
435 int64_t nptUs =
436 ((double)rtpTime - (double)info->mRTPTime)
437 / info->mTimeScale
438 * 1000000ll
439 + info->mNormalPlaytimeUs;
440
441 accessUnit->meta()->setInt64("timeUs", nptUs);
442
443 source->queueAccessUnit(accessUnit);
444 }
445
446 break;
447 }
448 case kWhatDisconnect:
449 {
450 sp<AReplyToken> replyID;
451 CHECK(msg->senderAwaitsResponse(&replyID));
452
453 for (size_t i = 0; i < mTracks.size(); ++i) {
454 TrackInfo *info = &mTracks.editItemAt(i);
455
456 if (info->mIsAudio) {
457 mAudioTrack->signalEOS(ERROR_END_OF_STREAM);
458 mAudioTrack = NULL;
459 ALOGV("mAudioTrack disconnected");
460 } else {
461 mVideoTrack->signalEOS(ERROR_END_OF_STREAM);
462 mVideoTrack = NULL;
463 ALOGV("mVideoTrack disconnected");
464 }
465
466 mRTPConn->removeStream(info->mRTPSocket, info->mRTCPSocket);
467 close(info->mRTPSocket);
468 close(info->mRTCPSocket);
469 }
470
471 mTracks.clear();
472 mFirstAccessUnit = true;
473 mAllTracksHaveTime = false;
474 mNTPAnchorUs = -1;
475 mMediaAnchorUs = -1;
476 mLastMediaTimeUs = -1;
477 mNumAccessUnitsReceived = 0;
478 mReceivedFirstRTCPPacket = false;
479 mReceivedFirstRTPPacket = false;
480 mPausing = false;
481 mPauseGeneration = 0;
482
483 (new AMessage)->postReply(replyID);
484
485 break;
486 }
487 case kWhatPollBuffering:
488 break;
489 default:
490 TRESPASS();
491 }
492}
493
494void NuPlayer::RTPSource::onTimeUpdate(int32_t trackIndex, uint32_t rtpTime, uint64_t ntpTime) {
495 ALOGV("onTimeUpdate track %d, rtpTime = 0x%08x, ntpTime = %#016llx",
496 trackIndex, rtpTime, (long long)ntpTime);
497
Lajos Molnar19829332020-03-17 08:13:10 -0700498 // convert ntpTime in Q32 seconds to microseconds. Note: this will not lose precision
499 // because ntpTimeUs is at most 52 bits (double holds 53 bits)
Byeongjo Parkd157b792019-01-24 20:56:37 +0900500 int64_t ntpTimeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
501
502 TrackInfo *track = &mTracks.editItemAt(trackIndex);
503
504 track->mRTPAnchor = rtpTime;
505 track->mNTPAnchorUs = ntpTimeUs;
506
507 if (mNTPAnchorUs < 0) {
508 mNTPAnchorUs = ntpTimeUs;
509 mMediaAnchorUs = mLastMediaTimeUs;
510 }
511
512 if (!mAllTracksHaveTime) {
513 bool allTracksHaveTime = (mTracks.size() > 0);
514 for (size_t i = 0; i < mTracks.size(); ++i) {
515 TrackInfo *track = &mTracks.editItemAt(i);
516 if (track->mNTPAnchorUs < 0) {
517 allTracksHaveTime = false;
518 break;
519 }
520 }
521 if (allTracksHaveTime) {
522 mAllTracksHaveTime = true;
523 ALOGI("Time now established for all tracks.");
524 }
525 }
526 if (mAllTracksHaveTime && dataReceivedOnAllChannels()) {
527 // Time is now established, lets start timestamping immediately
528 for (size_t i = 0; i < mTracks.size(); ++i) {
529 TrackInfo *trackInfo = &mTracks.editItemAt(i);
530 while (!trackInfo->mPackets.empty()) {
531 sp<ABuffer> accessUnit = *trackInfo->mPackets.begin();
532 trackInfo->mPackets.erase(trackInfo->mPackets.begin());
533
534 if (addMediaTimestamp(i, trackInfo, accessUnit)) {
535 postQueueAccessUnit(i, accessUnit);
536 }
537 }
538 }
539 }
540}
541
542bool NuPlayer::RTPSource::addMediaTimestamp(
543 int32_t trackIndex, const TrackInfo *track,
544 const sp<ABuffer> &accessUnit) {
545
546 uint32_t rtpTime;
547 CHECK(accessUnit->meta()->findInt32(
548 "rtp-time", (int32_t *)&rtpTime));
549
550 int64_t relRtpTimeUs =
551 (((int64_t)rtpTime - (int64_t)track->mRTPAnchor) * 1000000ll)
552 / track->mTimeScale;
553
554 int64_t ntpTimeUs = track->mNTPAnchorUs + relRtpTimeUs;
555
556 int64_t mediaTimeUs = mMediaAnchorUs + ntpTimeUs - mNTPAnchorUs;
557
558 if (mediaTimeUs > mLastMediaTimeUs) {
559 mLastMediaTimeUs = mediaTimeUs;
560 }
561
562 if (mediaTimeUs < 0) {
563 ALOGV("dropping early accessUnit.");
564 return false;
565 }
566
567 ALOGV("track %d rtpTime=%u mediaTimeUs = %lld us (%.2f secs)",
568 trackIndex, rtpTime, (long long)mediaTimeUs, mediaTimeUs / 1E6);
569
570 accessUnit->meta()->setInt64("timeUs", mediaTimeUs);
571
572 return true;
573}
574
575bool NuPlayer::RTPSource::dataReceivedOnAllChannels() {
576 TrackInfo *track;
577 for (size_t i = 0; i < mTracks.size(); ++i) {
578 track = &mTracks.editItemAt(i);
579 if (track->mPackets.empty()) {
580 return false;
581 }
582 }
583 return true;
584}
585
586void NuPlayer::RTPSource::postQueueAccessUnit(
587 size_t trackIndex, const sp<ABuffer> &accessUnit) {
588 sp<AMessage> msg = new AMessage(kWhatAccessUnit, this);
589 msg->setInt32("what", kWhatAccessUnit);
590 msg->setSize("trackIndex", trackIndex);
591 msg->setBuffer("accessUnit", accessUnit);
592 msg->post();
593}
594
595void NuPlayer::RTPSource::postQueueEOS(size_t trackIndex, status_t finalResult) {
596 sp<AMessage> msg = new AMessage(kWhatEOS, this);
597 msg->setInt32("what", kWhatEOS);
598 msg->setSize("trackIndex", trackIndex);
599 msg->setInt32("finalResult", finalResult);
600 msg->post();
601}
602
603sp<MetaData> NuPlayer::RTPSource::getTrackFormat(size_t index, int32_t *timeScale) {
604 CHECK_GE(index, 0u);
605 CHECK_LT(index, mTracks.size());
606
607 const TrackInfo &info = mTracks.itemAt(index);
608
609 *timeScale = info.mTimeScale;
610
611 return info.mPacketSource->getFormat();
612}
613
614void NuPlayer::RTPSource::onConnected() {
615 ALOGV("onConnected");
616 mState = CONNECTED;
617}
618
619void NuPlayer::RTPSource::onDisconnected(const sp<AMessage> &msg) {
620 if (mState == DISCONNECTED) {
621 return;
622 }
623
624 status_t err;
625 CHECK(msg->findInt32("result", &err));
626 CHECK_NE(err, (status_t)OK);
627
628// mLooper->unregisterHandler(mHandler->id());
629// mHandler.clear();
630
631 if (mState == CONNECTING) {
632 // We're still in the preparation phase, signal that it
633 // failed.
634 notifyPrepared(err);
635 }
636
637 mState = DISCONNECTED;
638// setError(err);
639
640}
641
642status_t NuPlayer::RTPSource::setParameter(const String8 &key, const String8 &value) {
643 ALOGV("setParameter: key (%s) => value (%s)", key.string(), value.string());
644
645 bool isAudioKey = key.contains("audio");
646 TrackInfo *info = NULL;
647 for (unsigned i = 0; i < mTracks.size(); ++i) {
648 info = &mTracks.editItemAt(i);
649 if (info != NULL && info->mIsAudio == isAudioKey) {
650 ALOGV("setParameter: %s track (%d) found", isAudioKey ? "audio" : "video" , i);
651 break;
652 }
653 }
654
655 if (info == NULL) {
656 TrackInfo newTrackInfo;
657 newTrackInfo.mIsAudio = isAudioKey;
658 mTracks.push(newTrackInfo);
659 info = &mTracks.editTop();
Byeongjo Park7143c072019-09-23 13:31:37 +0900660 info->mJbTime = 300;
Byeongjo Parkd157b792019-01-24 20:56:37 +0900661 }
662
663 if (key == "rtp-param-mime-type") {
664 info->mMimeType = value;
665
666 const char *mime = value.string();
667 const char *delimiter = strchr(mime, '/');
Lajos Molnar19829332020-03-17 08:13:10 -0700668 info->mCodecName = delimiter ? (delimiter + 1) : "<none>";
Byeongjo Parkd157b792019-01-24 20:56:37 +0900669
670 ALOGV("rtp-param-mime-type: mMimeType (%s) => mCodecName (%s)",
Lajos Molnar19829332020-03-17 08:13:10 -0700671 info->mMimeType.string(), info->mCodecName.string());
Byeongjo Parkd157b792019-01-24 20:56:37 +0900672 } else if (key == "video-param-decoder-profile") {
673 info->mCodecProfile = atoi(value);
674 } else if (key == "video-param-decoder-level") {
675 info->mCodecLevel = atoi(value);
676 } else if (key == "video-param-width") {
677 info->mWidth = atoi(value);
678 } else if (key == "video-param-height") {
679 info->mHeight = atoi(value);
680 } else if (key == "rtp-param-local-ip") {
681 info->mLocalIp = value;
682 } else if (key == "rtp-param-local-port") {
683 info->mLocalPort = atoi(value);
684 } else if (key == "rtp-param-remote-ip") {
685 info->mRemoteIp = value;
686 } else if (key == "rtp-param-remote-port") {
687 info->mRemotePort = atoi(value);
688 } else if (key == "rtp-param-payload-type") {
689 info->mPayloadType = atoi(value);
690 } else if (key == "rtp-param-as") {
691 //AS means guaranteed bit rate that negotiated from sdp.
692 info->mAS = atoi(value);
693 } else if (key == "rtp-param-rtp-timeout") {
694 } else if (key == "rtp-param-rtcp-timeout") {
695 } else if (key == "rtp-param-time-scale") {
Kim Sungyeond2875e92018-03-20 16:51:41 +0900696 } else if (key == "rtp-param-self-id") {
697 info->mSelfID = atoi(value);
Byeongjo Parkb0225aa2018-04-20 14:00:15 +0900698 } else if (key == "rtp-param-ext-cvo-extmap") {
699 info->mCVOExtMap = atoi(value);
Byeongjo Park88068dc2019-03-29 15:04:10 +0900700 } else if (key == "rtp-param-set-socket-network") {
701 int64_t networkHandle = atoll(value);
702 setSocketNetwork(networkHandle);
Byeongjo Park7143c072019-09-23 13:31:37 +0900703 } else if (key == "rtp-param-jitter-buffer-time") {
704 info->mJbTime = atoi(value);
Byeongjo Parkd157b792019-01-24 20:56:37 +0900705 }
706
707 return OK;
708}
709
710status_t NuPlayer::RTPSource::setParameters(const String8 &params) {
711 ALOGV("setParameters: %s", params.string());
712 const char *cparams = params.string();
713 const char *key_start = cparams;
714 for (;;) {
715 const char *equal_pos = strchr(key_start, '=');
716 if (equal_pos == NULL) {
717 ALOGE("Parameters %s miss a value", cparams);
718 return BAD_VALUE;
719 }
720 String8 key(key_start, equal_pos - key_start);
721 TrimString(&key);
722 if (key.length() == 0) {
723 ALOGE("Parameters %s contains an empty key", cparams);
724 return BAD_VALUE;
725 }
726 const char *value_start = equal_pos + 1;
727 const char *semicolon_pos = strchr(value_start, ';');
728 String8 value;
729 if (semicolon_pos == NULL) {
730 value.setTo(value_start);
731 } else {
732 value.setTo(value_start, semicolon_pos - value_start);
733 }
734 if (setParameter(key, value) != OK) {
735 return BAD_VALUE;
736 }
737 if (semicolon_pos == NULL) {
738 break; // Reaches the end
739 }
740 key_start = semicolon_pos + 1;
741 }
742 return OK;
743}
744
Byeongjo Park88068dc2019-03-29 15:04:10 +0900745void NuPlayer::RTPSource::setSocketNetwork(int64_t networkHandle) {
746 ALOGV("setSocketNetwork: %llu", (unsigned long long)networkHandle);
747
748 TrackInfo *info = NULL;
749 for (size_t i = 0; i < mTracks.size(); ++i) {
750 info = &mTracks.editItemAt(i);
751
752 if (info == NULL)
753 break;
754
755 info->mSocketNetwork = networkHandle;
756 }
757}
758
Byeongjo Parkd157b792019-01-24 20:56:37 +0900759// Trim both leading and trailing whitespace from the given string.
760//static
761void NuPlayer::RTPSource::TrimString(String8 *s) {
762 size_t num_bytes = s->bytes();
763 const char *data = s->string();
764
765 size_t leading_space = 0;
766 while (leading_space < num_bytes && isspace(data[leading_space])) {
767 ++leading_space;
768 }
769
770 size_t i = num_bytes;
771 while (i > leading_space && isspace(data[i - 1])) {
772 --i;
773 }
774
775 s->setTo(String8(&data[leading_space], i - leading_space));
776}
777
778} // namespace android