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