1/* 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 "AnotherPacketSource" 19 20#include "AnotherPacketSource.h" 21 22#include <media/stagefright/foundation/ABuffer.h> 23#include <media/stagefright/foundation/ADebug.h> 24#include <media/stagefright/foundation/AMessage.h> 25#include <media/stagefright/foundation/AString.h> 26#include <media/stagefright/foundation/hexdump.h> 27#include <media/stagefright/foundation/avc_utils.h> 28#include <media/stagefright/MediaBuffer.h> 29#include <media/stagefright/MediaDefs.h> 30#include <media/stagefright/MetaData.h> 31#include <media/stagefright/Utils.h> 32#include <utils/Vector.h> 33 34#include <inttypes.h> 35 36namespace android { 37 38const int64_t kNearEOSMarkUs = 2000000ll; // 2 secs 39 40AnotherPacketSource::AnotherPacketSource(const sp<MetaData> &meta) 41 : mIsAudio(false), 42 mIsVideo(false), 43 mEnabled(true), 44 mFormat(NULL), 45 mLastQueuedTimeUs(0), 46 mEstimatedBufferDurationUs(-1), 47 mEOSResult(OK), 48 mLatestEnqueuedMeta(NULL), 49 mLatestDequeuedMeta(NULL) { 50 setFormat(meta); 51 52 mDiscontinuitySegments.push_back(DiscontinuitySegment()); 53} 54 55void AnotherPacketSource::setFormat(const sp<MetaData> &meta) { 56 if (mFormat != NULL) { 57 // Only allowed to be set once. Requires explicit clear to reset. 58 return; 59 } 60 61 mIsAudio = false; 62 mIsVideo = false; 63 64 if (meta == NULL) { 65 return; 66 } 67 68 mFormat = meta; 69 const char *mime; 70 CHECK(meta->findCString(kKeyMIMEType, &mime)); 71 72 if (!strncasecmp("audio/", mime, 6)) { 73 mIsAudio = true; 74 } else if (!strncasecmp("video/", mime, 6)) { 75 mIsVideo = true; 76 } else { 77 CHECK(!strncasecmp("text/", mime, 5) || !strncasecmp("application/", mime, 12)); 78 } 79} 80 81AnotherPacketSource::~AnotherPacketSource() { 82} 83 84status_t AnotherPacketSource::start(MetaData * /* params */) { 85 return OK; 86} 87 88status_t AnotherPacketSource::stop() { 89 return OK; 90} 91 92sp<MetaData> AnotherPacketSource::getFormat() { 93 Mutex::Autolock autoLock(mLock); 94 if (mFormat != NULL) { 95 return mFormat; 96 } 97 98 List<sp<ABuffer> >::iterator it = mBuffers.begin(); 99 while (it != mBuffers.end()) { 100 sp<ABuffer> buffer = *it; 101 int32_t discontinuity; 102 if (!buffer->meta()->findInt32("discontinuity", &discontinuity)) { 103 sp<RefBase> object; 104 if (buffer->meta()->findObject("format", &object)) { 105 setFormat(static_cast<MetaData*>(object.get())); 106 return mFormat; 107 } 108 } 109 110 ++it; 111 } 112 return NULL; 113} 114 115status_t AnotherPacketSource::dequeueAccessUnit(sp<ABuffer> *buffer) { 116 buffer->clear(); 117 118 Mutex::Autolock autoLock(mLock); 119 while (mEOSResult == OK && mBuffers.empty()) { 120 mCondition.wait(mLock); 121 } 122 123 if (!mBuffers.empty()) { 124 *buffer = *mBuffers.begin(); 125 mBuffers.erase(mBuffers.begin()); 126 127 int32_t discontinuity; 128 if ((*buffer)->meta()->findInt32("discontinuity", &discontinuity)) { 129 if (wasFormatChange(discontinuity)) { 130 mFormat.clear(); 131 } 132 133 mDiscontinuitySegments.erase(mDiscontinuitySegments.begin()); 134 // CHECK(!mDiscontinuitySegments.empty()); 135 return INFO_DISCONTINUITY; 136 } 137 138 // CHECK(!mDiscontinuitySegments.empty()); 139 DiscontinuitySegment &seg = *mDiscontinuitySegments.begin(); 140 141 int64_t timeUs; 142 mLatestDequeuedMeta = (*buffer)->meta()->dup(); 143 CHECK(mLatestDequeuedMeta->findInt64("timeUs", &timeUs)); 144 if (timeUs > seg.mMaxDequeTimeUs) { 145 seg.mMaxDequeTimeUs = timeUs; 146 } 147 148 sp<RefBase> object; 149 if ((*buffer)->meta()->findObject("format", &object)) { 150 setFormat(static_cast<MetaData*>(object.get())); 151 } 152 153 return OK; 154 } 155 156 return mEOSResult; 157} 158 159void AnotherPacketSource::requeueAccessUnit(const sp<ABuffer> &buffer) { 160 // TODO: update corresponding book keeping info. 161 Mutex::Autolock autoLock(mLock); 162 mBuffers.push_front(buffer); 163} 164 165status_t AnotherPacketSource::read( 166 MediaBufferBase **out, const ReadOptions *) { 167 *out = NULL; 168 169 Mutex::Autolock autoLock(mLock); 170 while (mEOSResult == OK && mBuffers.empty()) { 171 mCondition.wait(mLock); 172 } 173 174 if (!mBuffers.empty()) { 175 176 const sp<ABuffer> buffer = *mBuffers.begin(); 177 mBuffers.erase(mBuffers.begin()); 178 179 int32_t discontinuity; 180 if (buffer->meta()->findInt32("discontinuity", &discontinuity)) { 181 if (wasFormatChange(discontinuity)) { 182 mFormat.clear(); 183 } 184 185 mDiscontinuitySegments.erase(mDiscontinuitySegments.begin()); 186 // CHECK(!mDiscontinuitySegments.empty()); 187 return INFO_DISCONTINUITY; 188 } 189 190 mLatestDequeuedMeta = buffer->meta()->dup(); 191 192 sp<RefBase> object; 193 if (buffer->meta()->findObject("format", &object)) { 194 setFormat(static_cast<MetaData*>(object.get())); 195 } 196 197 int64_t timeUs; 198 CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); 199 // CHECK(!mDiscontinuitySegments.empty()); 200 DiscontinuitySegment &seg = *mDiscontinuitySegments.begin(); 201 if (timeUs > seg.mMaxDequeTimeUs) { 202 seg.mMaxDequeTimeUs = timeUs; 203 } 204 205 MediaBufferBase *mediaBuffer = new MediaBuffer(buffer); 206 MetaDataBase &bufmeta = mediaBuffer->meta_data(); 207 208 bufmeta.setInt64(kKeyTime, timeUs); 209 210 int32_t isSync; 211 if (buffer->meta()->findInt32("isSync", &isSync)) { 212 bufmeta.setInt32(kKeyIsSyncFrame, isSync); 213 } 214 215 sp<ABuffer> sei; 216 if (buffer->meta()->findBuffer("sei", &sei) && sei != NULL) { 217 bufmeta.setData(kKeySEI, 0, sei->data(), sei->size()); 218 } 219 220 sp<ABuffer> mpegUserData; 221 if (buffer->meta()->findBuffer("mpeg-user-data", &mpegUserData) && mpegUserData != NULL) { 222 bufmeta.setData( 223 kKeyMpegUserData, 0, mpegUserData->data(), mpegUserData->size()); 224 } 225 226 int32_t cryptoMode; 227 if (buffer->meta()->findInt32("cryptoMode", &cryptoMode)) { 228 int32_t cryptoKey; 229 sp<ABuffer> clearBytesBuffer, encBytesBuffer; 230 231 CHECK(buffer->meta()->findInt32("cryptoKey", &cryptoKey)); 232 CHECK(buffer->meta()->findBuffer("clearBytes", &clearBytesBuffer) 233 && clearBytesBuffer != NULL); 234 CHECK(buffer->meta()->findBuffer("encBytes", &encBytesBuffer) 235 && encBytesBuffer != NULL); 236 237 bufmeta.setInt32(kKeyCryptoMode, cryptoMode); 238 239 uint8_t array[16] = {0}; 240 bufmeta.setData(kKeyCryptoIV, 0, array, 16); 241 242 array[0] = (uint8_t) (cryptoKey & 0xff); 243 bufmeta.setData(kKeyCryptoKey, 0, array, 16); 244 245 bufmeta.setData(kKeyPlainSizes, 0, 246 clearBytesBuffer->data(), clearBytesBuffer->size()); 247 248 bufmeta.setData(kKeyEncryptedSizes, 0, 249 encBytesBuffer->data(), encBytesBuffer->size()); 250 } 251 252 253 *out = mediaBuffer; 254 return OK; 255 } 256 257 return mEOSResult; 258} 259 260bool AnotherPacketSource::wasFormatChange( 261 int32_t discontinuityType) const { 262 if (mIsAudio) { 263 return (discontinuityType & ATSParser::DISCONTINUITY_AUDIO_FORMAT) != 0; 264 } 265 266 if (mIsVideo) { 267 return (discontinuityType & ATSParser::DISCONTINUITY_VIDEO_FORMAT) != 0; 268 } 269 270 return false; 271} 272 273void AnotherPacketSource::queueAccessUnit(const sp<ABuffer> &buffer) { 274 int32_t damaged; 275 if (buffer->meta()->findInt32("damaged", &damaged) && damaged) { 276 // LOG(VERBOSE) << "discarding damaged AU"; 277 return; 278 } 279 280 Mutex::Autolock autoLock(mLock); 281 mBuffers.push_back(buffer); 282 mCondition.signal(); 283 284 int32_t discontinuity; 285 if (buffer->meta()->findInt32("discontinuity", &discontinuity)){ 286 ALOGV("queueing a discontinuity with queueAccessUnit"); 287 288 mLastQueuedTimeUs = 0ll; 289 mEOSResult = OK; 290 mLatestEnqueuedMeta = NULL; 291 292 mDiscontinuitySegments.push_back(DiscontinuitySegment()); 293 return; 294 } 295 296 int64_t lastQueuedTimeUs; 297 CHECK(buffer->meta()->findInt64("timeUs", &lastQueuedTimeUs)); 298 mLastQueuedTimeUs = lastQueuedTimeUs; 299 ALOGV("queueAccessUnit timeUs=%" PRIi64 " us (%.2f secs)", 300 mLastQueuedTimeUs, mLastQueuedTimeUs / 1E6); 301 302 // CHECK(!mDiscontinuitySegments.empty()); 303 DiscontinuitySegment &tailSeg = *(--mDiscontinuitySegments.end()); 304 if (lastQueuedTimeUs > tailSeg.mMaxEnqueTimeUs) { 305 tailSeg.mMaxEnqueTimeUs = lastQueuedTimeUs; 306 } 307 if (tailSeg.mMaxDequeTimeUs == -1) { 308 tailSeg.mMaxDequeTimeUs = lastQueuedTimeUs; 309 } 310 311 if (mLatestEnqueuedMeta == NULL) { 312 mLatestEnqueuedMeta = buffer->meta()->dup(); 313 } else { 314 int64_t latestTimeUs = 0; 315 int64_t frameDeltaUs = 0; 316 CHECK(mLatestEnqueuedMeta->findInt64("timeUs", &latestTimeUs)); 317 if (lastQueuedTimeUs > latestTimeUs) { 318 mLatestEnqueuedMeta = buffer->meta()->dup(); 319 frameDeltaUs = lastQueuedTimeUs - latestTimeUs; 320 mLatestEnqueuedMeta->setInt64("durationUs", frameDeltaUs); 321 } else if (!mLatestEnqueuedMeta->findInt64("durationUs", &frameDeltaUs)) { 322 // For B frames 323 frameDeltaUs = latestTimeUs - lastQueuedTimeUs; 324 mLatestEnqueuedMeta->setInt64("durationUs", frameDeltaUs); 325 } 326 } 327} 328 329void AnotherPacketSource::clear() { 330 Mutex::Autolock autoLock(mLock); 331 332 mBuffers.clear(); 333 mEOSResult = OK; 334 335 mDiscontinuitySegments.clear(); 336 mDiscontinuitySegments.push_back(DiscontinuitySegment()); 337 338 mFormat = NULL; 339 mLatestEnqueuedMeta = NULL; 340 341 mEstimatedBufferDurationUs = -1; 342} 343 344void AnotherPacketSource::queueDiscontinuity( 345 ATSParser::DiscontinuityType type, 346 const sp<AMessage> &extra, 347 bool discard) { 348 Mutex::Autolock autoLock(mLock); 349 350 if (discard) { 351 // Leave only discontinuities in the queue. 352 List<sp<ABuffer> >::iterator it = mBuffers.begin(); 353 while (it != mBuffers.end()) { 354 sp<ABuffer> oldBuffer = *it; 355 356 int32_t oldDiscontinuityType; 357 if (!oldBuffer->meta()->findInt32( 358 "discontinuity", &oldDiscontinuityType)) { 359 it = mBuffers.erase(it); 360 continue; 361 } 362 363 ++it; 364 } 365 366 for (List<DiscontinuitySegment>::iterator it2 = mDiscontinuitySegments.begin(); 367 it2 != mDiscontinuitySegments.end(); 368 ++it2) { 369 DiscontinuitySegment &seg = *it2; 370 seg.clear(); 371 } 372 373 } 374 375 mEOSResult = OK; 376 mLastQueuedTimeUs = 0; 377 mLatestEnqueuedMeta = NULL; 378 379 if (type == ATSParser::DISCONTINUITY_NONE) { 380 return; 381 } 382 383 mDiscontinuitySegments.push_back(DiscontinuitySegment()); 384 385 sp<ABuffer> buffer = new ABuffer(0); 386 buffer->meta()->setInt32("discontinuity", static_cast<int32_t>(type)); 387 buffer->meta()->setMessage("extra", extra); 388 389 mBuffers.push_back(buffer); 390 mCondition.signal(); 391} 392 393void AnotherPacketSource::signalEOS(status_t result) { 394 CHECK(result != OK); 395 396 Mutex::Autolock autoLock(mLock); 397 mEOSResult = result; 398 mCondition.signal(); 399} 400 401bool AnotherPacketSource::hasBufferAvailable(status_t *finalResult) { 402 Mutex::Autolock autoLock(mLock); 403 *finalResult = OK; 404 if (!mEnabled) { 405 return false; 406 } 407 if (!mBuffers.empty()) { 408 return true; 409 } 410 411 *finalResult = mEOSResult; 412 return false; 413} 414 415bool AnotherPacketSource::hasDataBufferAvailable(status_t *finalResult) { 416 Mutex::Autolock autoLock(mLock); 417 *finalResult = OK; 418 if (!mEnabled) { 419 return false; 420 } 421 List<sp<ABuffer> >::iterator it; 422 for (it = mBuffers.begin(); it != mBuffers.end(); it++) { 423 int32_t discontinuity; 424 if (!(*it)->meta()->findInt32("discontinuity", &discontinuity)) { 425 return true; 426 } 427 } 428 429 *finalResult = mEOSResult; 430 return false; 431} 432 433size_t AnotherPacketSource::getAvailableBufferCount(status_t *finalResult) { 434 Mutex::Autolock autoLock(mLock); 435 436 *finalResult = OK; 437 if (!mEnabled) { 438 return 0; 439 } 440 if (!mBuffers.empty()) { 441 return mBuffers.size(); 442 } 443 *finalResult = mEOSResult; 444 return 0; 445} 446 447int64_t AnotherPacketSource::getBufferedDurationUs(status_t *finalResult) { 448 Mutex::Autolock autoLock(mLock); 449 *finalResult = mEOSResult; 450 451 int64_t durationUs = 0; 452 for (List<DiscontinuitySegment>::iterator it = mDiscontinuitySegments.begin(); 453 it != mDiscontinuitySegments.end(); 454 ++it) { 455 const DiscontinuitySegment &seg = *it; 456 // dequeued access units should be a subset of enqueued access units 457 // CHECK(seg.maxEnqueTimeUs >= seg.mMaxDequeTimeUs); 458 durationUs += (seg.mMaxEnqueTimeUs - seg.mMaxDequeTimeUs); 459 } 460 461 return durationUs; 462} 463 464int64_t AnotherPacketSource::getEstimatedBufferDurationUs() { 465 Mutex::Autolock autoLock(mLock); 466 if (mEstimatedBufferDurationUs >= 0) { 467 return mEstimatedBufferDurationUs; 468 } 469 470 SortedVector<int64_t> maxTimesUs; 471 List<sp<ABuffer> >::iterator it; 472 int64_t t1 = 0, t2 = 0; 473 for (it = mBuffers.begin(); it != mBuffers.end(); ++it) { 474 int64_t timeUs = 0; 475 const sp<ABuffer> &buffer = *it; 476 if (!buffer->meta()->findInt64("timeUs", &timeUs)) { 477 continue; 478 } 479 maxTimesUs.add(timeUs); 480 while (maxTimesUs.size() > 2) { 481 maxTimesUs.removeAt(0); 482 t1 = maxTimesUs.itemAt(0); 483 t2 = maxTimesUs.itemAt(1); 484 } 485 } 486 return mEstimatedBufferDurationUs = t2 - t1; 487} 488 489status_t AnotherPacketSource::nextBufferTime(int64_t *timeUs) { 490 *timeUs = 0; 491 492 Mutex::Autolock autoLock(mLock); 493 494 if (mBuffers.empty()) { 495 return mEOSResult != OK ? mEOSResult : -EWOULDBLOCK; 496 } 497 498 sp<ABuffer> buffer = *mBuffers.begin(); 499 CHECK(buffer->meta()->findInt64("timeUs", timeUs)); 500 501 return OK; 502} 503 504bool AnotherPacketSource::isFinished(int64_t duration) const { 505 if (duration > 0) { 506 int64_t diff = duration - mLastQueuedTimeUs; 507 if (diff < kNearEOSMarkUs && diff > -kNearEOSMarkUs) { 508 ALOGV("Detecting EOS due to near end"); 509 return true; 510 } 511 } 512 return (mEOSResult != OK); 513} 514 515sp<AMessage> AnotherPacketSource::getLatestEnqueuedMeta() { 516 Mutex::Autolock autoLock(mLock); 517 return mLatestEnqueuedMeta; 518} 519 520sp<AMessage> AnotherPacketSource::getLatestDequeuedMeta() { 521 Mutex::Autolock autoLock(mLock); 522 return mLatestDequeuedMeta; 523} 524 525void AnotherPacketSource::enable(bool enable) { 526 Mutex::Autolock autoLock(mLock); 527 mEnabled = enable; 528} 529 530/* 531 * returns the sample meta that's delayUs after queue head 532 * (NULL if such sample is unavailable) 533 */ 534sp<AMessage> AnotherPacketSource::getMetaAfterLastDequeued(int64_t delayUs) { 535 Mutex::Autolock autoLock(mLock); 536 int64_t firstUs = -1; 537 int64_t lastUs = -1; 538 int64_t durationUs = 0; 539 540 List<sp<ABuffer> >::iterator it; 541 for (it = mBuffers.begin(); it != mBuffers.end(); ++it) { 542 const sp<ABuffer> &buffer = *it; 543 int32_t discontinuity; 544 if (buffer->meta()->findInt32("discontinuity", &discontinuity)) { 545 durationUs += lastUs - firstUs; 546 firstUs = -1; 547 lastUs = -1; 548 continue; 549 } 550 int64_t timeUs; 551 if (buffer->meta()->findInt64("timeUs", &timeUs)) { 552 if (firstUs < 0) { 553 firstUs = timeUs; 554 } 555 if (lastUs < 0 || timeUs > lastUs) { 556 lastUs = timeUs; 557 } 558 if (durationUs + (lastUs - firstUs) >= delayUs) { 559 return buffer->meta(); 560 } 561 } 562 } 563 return NULL; 564} 565 566/* 567 * removes samples with time equal or after meta 568 */ 569void AnotherPacketSource::trimBuffersAfterMeta( 570 const sp<AMessage> &meta) { 571 if (meta == NULL) { 572 ALOGW("trimming with NULL meta, ignoring"); 573 return; 574 } 575 576 Mutex::Autolock autoLock(mLock); 577 if (mBuffers.empty()) { 578 return; 579 } 580 581 HLSTime stopTime(meta); 582 ALOGV("trimBuffersAfterMeta: discontinuitySeq %d, timeUs %lld", 583 stopTime.mSeq, (long long)stopTime.mTimeUs); 584 585 List<sp<ABuffer> >::iterator it; 586 List<DiscontinuitySegment >::iterator it2; 587 sp<AMessage> newLatestEnqueuedMeta = NULL; 588 int64_t newLastQueuedTimeUs = 0; 589 for (it = mBuffers.begin(), it2 = mDiscontinuitySegments.begin(); it != mBuffers.end(); ++it) { 590 const sp<ABuffer> &buffer = *it; 591 int32_t discontinuity; 592 if (buffer->meta()->findInt32("discontinuity", &discontinuity)) { 593 // CHECK(it2 != mDiscontinuitySegments.end()); 594 ++it2; 595 continue; 596 } 597 598 HLSTime curTime(buffer->meta()); 599 if (!(curTime < stopTime)) { 600 ALOGV("trimming from %lld (inclusive) to end", 601 (long long)curTime.mTimeUs); 602 break; 603 } 604 newLatestEnqueuedMeta = buffer->meta(); 605 newLastQueuedTimeUs = curTime.mTimeUs; 606 } 607 608 mBuffers.erase(it, mBuffers.end()); 609 mLatestEnqueuedMeta = newLatestEnqueuedMeta; 610 mLastQueuedTimeUs = newLastQueuedTimeUs; 611 612 DiscontinuitySegment &seg = *it2; 613 if (newLatestEnqueuedMeta != NULL) { 614 seg.mMaxEnqueTimeUs = newLastQueuedTimeUs; 615 } else { 616 seg.clear(); 617 } 618 mDiscontinuitySegments.erase(++it2, mDiscontinuitySegments.end()); 619} 620 621/* 622 * removes samples with time equal or before meta; 623 * returns first sample left in the queue. 624 * 625 * (for AVC, if trim happens, the samples left will always start 626 * at next IDR.) 627 */ 628sp<AMessage> AnotherPacketSource::trimBuffersBeforeMeta( 629 const sp<AMessage> &meta) { 630 HLSTime startTime(meta); 631 ALOGV("trimBuffersBeforeMeta: discontinuitySeq %d, timeUs %lld", 632 startTime.mSeq, (long long)startTime.mTimeUs); 633 634 sp<AMessage> firstMeta; 635 int64_t firstTimeUs = -1; 636 Mutex::Autolock autoLock(mLock); 637 if (mBuffers.empty()) { 638 return NULL; 639 } 640 641 sp<MetaData> format; 642 bool isAvc = false; 643 644 List<sp<ABuffer> >::iterator it; 645 for (it = mBuffers.begin(); it != mBuffers.end(); ++it) { 646 const sp<ABuffer> &buffer = *it; 647 int32_t discontinuity; 648 if (buffer->meta()->findInt32("discontinuity", &discontinuity)) { 649 mDiscontinuitySegments.erase(mDiscontinuitySegments.begin()); 650 // CHECK(!mDiscontinuitySegments.empty()); 651 format = NULL; 652 isAvc = false; 653 continue; 654 } 655 if (format == NULL) { 656 sp<RefBase> object; 657 if (buffer->meta()->findObject("format", &object)) { 658 const char* mime; 659 format = static_cast<MetaData*>(object.get()); 660 isAvc = format != NULL 661 && format->findCString(kKeyMIMEType, &mime) 662 && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC); 663 } 664 } 665 if (isAvc && !IsIDR(buffer->data(), buffer->size())) { 666 continue; 667 } 668 669 HLSTime curTime(buffer->meta()); 670 if (startTime < curTime) { 671 ALOGV("trimming from beginning to %lld (not inclusive)", 672 (long long)curTime.mTimeUs); 673 firstMeta = buffer->meta(); 674 firstTimeUs = curTime.mTimeUs; 675 break; 676 } 677 } 678 mBuffers.erase(mBuffers.begin(), it); 679 mLatestDequeuedMeta = NULL; 680 681 // CHECK(!mDiscontinuitySegments.empty()); 682 DiscontinuitySegment &seg = *mDiscontinuitySegments.begin(); 683 if (firstTimeUs >= 0) { 684 seg.mMaxDequeTimeUs = firstTimeUs; 685 } else { 686 seg.clear(); 687 } 688 689 return firstMeta; 690} 691 692} // namespace android 693