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