NuPlayerRenderer.cpp revision d6074f08cc37843e8c599b2ab4b16d87c3bc67eb
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 "NuPlayerRenderer" 19#include <utils/Log.h> 20 21#include "NuPlayerRenderer.h" 22 23#include "SoftwareRenderer.h" 24 25#include <media/stagefright/foundation/ABuffer.h> 26#include <media/stagefright/foundation/ADebug.h> 27#include <media/stagefright/foundation/AMessage.h> 28 29namespace android { 30 31// static 32const int64_t NuPlayer::Renderer::kMinPositionUpdateDelayUs = 100000ll; 33 34NuPlayer::Renderer::Renderer( 35 const sp<MediaPlayerBase::AudioSink> &sink, 36 const sp<AMessage> ¬ify, 37 uint32_t flags) 38 : mAudioSink(sink), 39 mSoftRenderer(NULL), 40 mNotify(notify), 41 mFlags(flags), 42 mNumFramesWritten(0), 43 mDrainAudioQueuePending(false), 44 mDrainVideoQueuePending(false), 45 mAudioQueueGeneration(0), 46 mVideoQueueGeneration(0), 47 mAnchorTimeMediaUs(-1), 48 mAnchorTimeRealUs(-1), 49 mFlushingAudio(false), 50 mFlushingVideo(false), 51 mHasAudio(false), 52 mHasVideo(false), 53 mSyncQueues(false), 54 mPaused(false), 55 mVideoRenderingStarted(false), 56 mVideoRenderingStartGeneration(0), 57 mAudioRenderingStartGeneration(0), 58 mLastPositionUpdateUs(-1ll), 59 mVideoLateByUs(0ll) { 60} 61 62NuPlayer::Renderer::~Renderer() { 63 delete mSoftRenderer; 64} 65 66void NuPlayer::Renderer::setSoftRenderer(SoftwareRenderer *softRenderer) { 67 delete mSoftRenderer; 68 mSoftRenderer = softRenderer; 69} 70 71void NuPlayer::Renderer::queueBuffer( 72 bool audio, 73 const sp<ABuffer> &buffer, 74 const sp<AMessage> ¬ifyConsumed) { 75 sp<AMessage> msg = new AMessage(kWhatQueueBuffer, id()); 76 msg->setInt32("audio", static_cast<int32_t>(audio)); 77 msg->setBuffer("buffer", buffer); 78 msg->setMessage("notifyConsumed", notifyConsumed); 79 msg->post(); 80} 81 82void NuPlayer::Renderer::queueEOS(bool audio, status_t finalResult) { 83 CHECK_NE(finalResult, (status_t)OK); 84 85 sp<AMessage> msg = new AMessage(kWhatQueueEOS, id()); 86 msg->setInt32("audio", static_cast<int32_t>(audio)); 87 msg->setInt32("finalResult", finalResult); 88 msg->post(); 89} 90 91void NuPlayer::Renderer::flush(bool audio) { 92 { 93 Mutex::Autolock autoLock(mFlushLock); 94 if (audio) { 95 CHECK(!mFlushingAudio); 96 mFlushingAudio = true; 97 } else { 98 CHECK(!mFlushingVideo); 99 mFlushingVideo = true; 100 } 101 } 102 103 sp<AMessage> msg = new AMessage(kWhatFlush, id()); 104 msg->setInt32("audio", static_cast<int32_t>(audio)); 105 msg->post(); 106} 107 108void NuPlayer::Renderer::signalTimeDiscontinuity() { 109 // CHECK(mAudioQueue.empty()); 110 // CHECK(mVideoQueue.empty()); 111 mAnchorTimeMediaUs = -1; 112 mAnchorTimeRealUs = -1; 113 mSyncQueues = false; 114} 115 116void NuPlayer::Renderer::pause() { 117 (new AMessage(kWhatPause, id()))->post(); 118} 119 120void NuPlayer::Renderer::resume() { 121 (new AMessage(kWhatResume, id()))->post(); 122} 123 124void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { 125 switch (msg->what()) { 126 case kWhatDrainAudioQueue: 127 { 128 int32_t generation; 129 CHECK(msg->findInt32("generation", &generation)); 130 if (generation != mAudioQueueGeneration) { 131 break; 132 } 133 134 mDrainAudioQueuePending = false; 135 136 if (onDrainAudioQueue()) { 137 uint32_t numFramesPlayed; 138 CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), 139 (status_t)OK); 140 141 uint32_t numFramesPendingPlayout = 142 mNumFramesWritten - numFramesPlayed; 143 144 // This is how long the audio sink will have data to 145 // play back. 146 int64_t delayUs = 147 mAudioSink->msecsPerFrame() 148 * numFramesPendingPlayout * 1000ll; 149 150 // Let's give it more data after about half that time 151 // has elapsed. 152 postDrainAudioQueue(delayUs / 2); 153 } 154 break; 155 } 156 157 case kWhatDrainVideoQueue: 158 { 159 int32_t generation; 160 CHECK(msg->findInt32("generation", &generation)); 161 if (generation != mVideoQueueGeneration) { 162 break; 163 } 164 165 mDrainVideoQueuePending = false; 166 167 onDrainVideoQueue(); 168 169 postDrainVideoQueue(); 170 break; 171 } 172 173 case kWhatQueueBuffer: 174 { 175 onQueueBuffer(msg); 176 break; 177 } 178 179 case kWhatQueueEOS: 180 { 181 onQueueEOS(msg); 182 break; 183 } 184 185 case kWhatFlush: 186 { 187 onFlush(msg); 188 break; 189 } 190 191 case kWhatAudioSinkChanged: 192 { 193 onAudioSinkChanged(); 194 break; 195 } 196 197 case kWhatPause: 198 { 199 onPause(); 200 break; 201 } 202 203 case kWhatResume: 204 { 205 onResume(); 206 break; 207 } 208 209 default: 210 TRESPASS(); 211 break; 212 } 213} 214 215void NuPlayer::Renderer::postDrainAudioQueue(int64_t delayUs) { 216 if (mDrainAudioQueuePending || mSyncQueues || mPaused) { 217 return; 218 } 219 220 if (mAudioQueue.empty()) { 221 return; 222 } 223 224 mDrainAudioQueuePending = true; 225 sp<AMessage> msg = new AMessage(kWhatDrainAudioQueue, id()); 226 msg->setInt32("generation", mAudioQueueGeneration); 227 msg->post(delayUs); 228} 229 230void NuPlayer::Renderer::signalAudioSinkChanged() { 231 (new AMessage(kWhatAudioSinkChanged, id()))->post(); 232} 233 234void NuPlayer::Renderer::prepareForMediaRenderingStart() { 235 mAudioRenderingStartGeneration = mAudioQueueGeneration; 236 mVideoRenderingStartGeneration = mVideoQueueGeneration; 237} 238 239void NuPlayer::Renderer::notifyIfMediaRenderingStarted() { 240 if (mVideoRenderingStartGeneration == mVideoQueueGeneration && 241 mAudioRenderingStartGeneration == mAudioQueueGeneration) { 242 mVideoRenderingStartGeneration = -1; 243 mAudioRenderingStartGeneration = -1; 244 245 sp<AMessage> notify = mNotify->dup(); 246 notify->setInt32("what", kWhatMediaRenderingStart); 247 notify->post(); 248 } 249} 250 251bool NuPlayer::Renderer::onDrainAudioQueue() { 252 uint32_t numFramesPlayed; 253 if (mAudioSink->getPosition(&numFramesPlayed) != OK) { 254 return false; 255 } 256 257 ssize_t numFramesAvailableToWrite = 258 mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed); 259 260#if 0 261 if (numFramesAvailableToWrite == mAudioSink->frameCount()) { 262 ALOGI("audio sink underrun"); 263 } else { 264 ALOGV("audio queue has %d frames left to play", 265 mAudioSink->frameCount() - numFramesAvailableToWrite); 266 } 267#endif 268 269 size_t numBytesAvailableToWrite = 270 numFramesAvailableToWrite * mAudioSink->frameSize(); 271 272 while (numBytesAvailableToWrite > 0 && !mAudioQueue.empty()) { 273 QueueEntry *entry = &*mAudioQueue.begin(); 274 275 if (entry->mBuffer == NULL) { 276 // EOS 277 278 notifyEOS(true /* audio */, entry->mFinalResult); 279 280 mAudioQueue.erase(mAudioQueue.begin()); 281 entry = NULL; 282 return false; 283 } 284 285 if (entry->mOffset == 0) { 286 int64_t mediaTimeUs; 287 CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); 288 289 ALOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6); 290 291 mAnchorTimeMediaUs = mediaTimeUs; 292 293 uint32_t numFramesPlayed; 294 CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK); 295 296 uint32_t numFramesPendingPlayout = 297 mNumFramesWritten - numFramesPlayed; 298 299 int64_t realTimeOffsetUs = 300 (mAudioSink->latency() / 2 /* XXX */ 301 + numFramesPendingPlayout 302 * mAudioSink->msecsPerFrame()) * 1000ll; 303 304 // ALOGI("realTimeOffsetUs = %lld us", realTimeOffsetUs); 305 306 mAnchorTimeRealUs = 307 ALooper::GetNowUs() + realTimeOffsetUs; 308 } 309 310 size_t copy = entry->mBuffer->size() - entry->mOffset; 311 if (copy > numBytesAvailableToWrite) { 312 copy = numBytesAvailableToWrite; 313 } 314 315 CHECK_EQ(mAudioSink->write( 316 entry->mBuffer->data() + entry->mOffset, copy), 317 (ssize_t)copy); 318 319 entry->mOffset += copy; 320 if (entry->mOffset == entry->mBuffer->size()) { 321 entry->mNotifyConsumed->post(); 322 mAudioQueue.erase(mAudioQueue.begin()); 323 324 entry = NULL; 325 } 326 327 numBytesAvailableToWrite -= copy; 328 size_t copiedFrames = copy / mAudioSink->frameSize(); 329 mNumFramesWritten += copiedFrames; 330 331 notifyIfMediaRenderingStarted(); 332 } 333 334 notifyPosition(); 335 336 return !mAudioQueue.empty(); 337} 338 339void NuPlayer::Renderer::postDrainVideoQueue() { 340 if (mDrainVideoQueuePending || mSyncQueues || mPaused) { 341 return; 342 } 343 344 if (mVideoQueue.empty()) { 345 return; 346 } 347 348 QueueEntry &entry = *mVideoQueue.begin(); 349 350 sp<AMessage> msg = new AMessage(kWhatDrainVideoQueue, id()); 351 msg->setInt32("generation", mVideoQueueGeneration); 352 353 int64_t delayUs; 354 355 if (entry.mBuffer == NULL) { 356 // EOS doesn't carry a timestamp. 357 delayUs = 0; 358 } else if (mFlags & FLAG_REAL_TIME) { 359 int64_t mediaTimeUs; 360 CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); 361 362 delayUs = mediaTimeUs - ALooper::GetNowUs(); 363 } else { 364 int64_t mediaTimeUs; 365 CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); 366 367 if (mAnchorTimeMediaUs < 0) { 368 delayUs = 0; 369 370 if (!mHasAudio) { 371 mAnchorTimeMediaUs = mediaTimeUs; 372 mAnchorTimeRealUs = ALooper::GetNowUs(); 373 } 374 } else { 375 int64_t realTimeUs = 376 (mediaTimeUs - mAnchorTimeMediaUs) + mAnchorTimeRealUs; 377 378 delayUs = realTimeUs - ALooper::GetNowUs(); 379 } 380 } 381 382 msg->post(delayUs); 383 384 mDrainVideoQueuePending = true; 385} 386 387void NuPlayer::Renderer::onDrainVideoQueue() { 388 if (mVideoQueue.empty()) { 389 return; 390 } 391 392 QueueEntry *entry = &*mVideoQueue.begin(); 393 394 if (entry->mBuffer == NULL) { 395 // EOS 396 397 notifyEOS(false /* audio */, entry->mFinalResult); 398 399 mVideoQueue.erase(mVideoQueue.begin()); 400 entry = NULL; 401 402 mVideoLateByUs = 0ll; 403 404 notifyPosition(); 405 return; 406 } 407 408 int64_t realTimeUs; 409 if (mFlags & FLAG_REAL_TIME) { 410 CHECK(entry->mBuffer->meta()->findInt64("timeUs", &realTimeUs)); 411 } else { 412 int64_t mediaTimeUs; 413 CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); 414 415 realTimeUs = mediaTimeUs - mAnchorTimeMediaUs + mAnchorTimeRealUs; 416 } 417 418 mVideoLateByUs = ALooper::GetNowUs() - realTimeUs; 419 bool tooLate = (mVideoLateByUs > 40000); 420 421 if (tooLate) { 422 ALOGV("video late by %lld us (%.2f secs)", 423 mVideoLateByUs, mVideoLateByUs / 1E6); 424 } else { 425 ALOGV("rendering video at media time %.2f secs", 426 (mFlags & FLAG_REAL_TIME ? realTimeUs : 427 (realTimeUs + mAnchorTimeMediaUs - mAnchorTimeRealUs)) / 1E6); 428 if (mSoftRenderer != NULL) { 429 mSoftRenderer->render(entry->mBuffer->data(), entry->mBuffer->size(), NULL); 430 } 431 } 432 433 entry->mNotifyConsumed->setInt32("render", !tooLate); 434 entry->mNotifyConsumed->post(); 435 mVideoQueue.erase(mVideoQueue.begin()); 436 entry = NULL; 437 438 if (!mVideoRenderingStarted) { 439 mVideoRenderingStarted = true; 440 notifyVideoRenderingStart(); 441 } 442 443 notifyIfMediaRenderingStarted(); 444 445 notifyPosition(); 446} 447 448void NuPlayer::Renderer::notifyVideoRenderingStart() { 449 sp<AMessage> notify = mNotify->dup(); 450 notify->setInt32("what", kWhatVideoRenderingStart); 451 notify->post(); 452} 453 454void NuPlayer::Renderer::notifyEOS(bool audio, status_t finalResult) { 455 sp<AMessage> notify = mNotify->dup(); 456 notify->setInt32("what", kWhatEOS); 457 notify->setInt32("audio", static_cast<int32_t>(audio)); 458 notify->setInt32("finalResult", finalResult); 459 notify->post(); 460} 461 462void NuPlayer::Renderer::onQueueBuffer(const sp<AMessage> &msg) { 463 int32_t audio; 464 CHECK(msg->findInt32("audio", &audio)); 465 466 if (audio) { 467 mHasAudio = true; 468 } else { 469 mHasVideo = true; 470 } 471 472 if (dropBufferWhileFlushing(audio, msg)) { 473 return; 474 } 475 476 sp<ABuffer> buffer; 477 CHECK(msg->findBuffer("buffer", &buffer)); 478 479 sp<AMessage> notifyConsumed; 480 CHECK(msg->findMessage("notifyConsumed", ¬ifyConsumed)); 481 482 QueueEntry entry; 483 entry.mBuffer = buffer; 484 entry.mNotifyConsumed = notifyConsumed; 485 entry.mOffset = 0; 486 entry.mFinalResult = OK; 487 488 if (audio) { 489 mAudioQueue.push_back(entry); 490 postDrainAudioQueue(); 491 } else { 492 mVideoQueue.push_back(entry); 493 postDrainVideoQueue(); 494 } 495 496 if (!mSyncQueues || mAudioQueue.empty() || mVideoQueue.empty()) { 497 return; 498 } 499 500 sp<ABuffer> firstAudioBuffer = (*mAudioQueue.begin()).mBuffer; 501 sp<ABuffer> firstVideoBuffer = (*mVideoQueue.begin()).mBuffer; 502 503 if (firstAudioBuffer == NULL || firstVideoBuffer == NULL) { 504 // EOS signalled on either queue. 505 syncQueuesDone(); 506 return; 507 } 508 509 int64_t firstAudioTimeUs; 510 int64_t firstVideoTimeUs; 511 CHECK(firstAudioBuffer->meta() 512 ->findInt64("timeUs", &firstAudioTimeUs)); 513 CHECK(firstVideoBuffer->meta() 514 ->findInt64("timeUs", &firstVideoTimeUs)); 515 516 int64_t diff = firstVideoTimeUs - firstAudioTimeUs; 517 518 ALOGV("queueDiff = %.2f secs", diff / 1E6); 519 520 if (diff > 100000ll) { 521 // Audio data starts More than 0.1 secs before video. 522 // Drop some audio. 523 524 (*mAudioQueue.begin()).mNotifyConsumed->post(); 525 mAudioQueue.erase(mAudioQueue.begin()); 526 return; 527 } 528 529 syncQueuesDone(); 530} 531 532void NuPlayer::Renderer::syncQueuesDone() { 533 if (!mSyncQueues) { 534 return; 535 } 536 537 mSyncQueues = false; 538 539 if (!mAudioQueue.empty()) { 540 postDrainAudioQueue(); 541 } 542 543 if (!mVideoQueue.empty()) { 544 postDrainVideoQueue(); 545 } 546} 547 548void NuPlayer::Renderer::onQueueEOS(const sp<AMessage> &msg) { 549 int32_t audio; 550 CHECK(msg->findInt32("audio", &audio)); 551 552 if (dropBufferWhileFlushing(audio, msg)) { 553 return; 554 } 555 556 int32_t finalResult; 557 CHECK(msg->findInt32("finalResult", &finalResult)); 558 559 QueueEntry entry; 560 entry.mOffset = 0; 561 entry.mFinalResult = finalResult; 562 563 if (audio) { 564 if (mAudioQueue.empty() && mSyncQueues) { 565 syncQueuesDone(); 566 } 567 mAudioQueue.push_back(entry); 568 postDrainAudioQueue(); 569 } else { 570 if (mVideoQueue.empty() && mSyncQueues) { 571 syncQueuesDone(); 572 } 573 mVideoQueue.push_back(entry); 574 postDrainVideoQueue(); 575 } 576} 577 578void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) { 579 int32_t audio; 580 CHECK(msg->findInt32("audio", &audio)); 581 582 // If we're currently syncing the queues, i.e. dropping audio while 583 // aligning the first audio/video buffer times and only one of the 584 // two queues has data, we may starve that queue by not requesting 585 // more buffers from the decoder. If the other source then encounters 586 // a discontinuity that leads to flushing, we'll never find the 587 // corresponding discontinuity on the other queue. 588 // Therefore we'll stop syncing the queues if at least one of them 589 // is flushed. 590 syncQueuesDone(); 591 592 ALOGV("flushing %s", audio ? "audio" : "video"); 593 if (audio) { 594 flushQueue(&mAudioQueue); 595 596 Mutex::Autolock autoLock(mFlushLock); 597 mFlushingAudio = false; 598 599 mDrainAudioQueuePending = false; 600 ++mAudioQueueGeneration; 601 602 prepareForMediaRenderingStart(); 603 } else { 604 flushQueue(&mVideoQueue); 605 606 Mutex::Autolock autoLock(mFlushLock); 607 mFlushingVideo = false; 608 609 mDrainVideoQueuePending = false; 610 ++mVideoQueueGeneration; 611 612 prepareForMediaRenderingStart(); 613 } 614 615 notifyFlushComplete(audio); 616} 617 618void NuPlayer::Renderer::flushQueue(List<QueueEntry> *queue) { 619 while (!queue->empty()) { 620 QueueEntry *entry = &*queue->begin(); 621 622 if (entry->mBuffer != NULL) { 623 entry->mNotifyConsumed->post(); 624 } 625 626 queue->erase(queue->begin()); 627 entry = NULL; 628 } 629} 630 631void NuPlayer::Renderer::notifyFlushComplete(bool audio) { 632 sp<AMessage> notify = mNotify->dup(); 633 notify->setInt32("what", kWhatFlushComplete); 634 notify->setInt32("audio", static_cast<int32_t>(audio)); 635 notify->post(); 636} 637 638bool NuPlayer::Renderer::dropBufferWhileFlushing( 639 bool audio, const sp<AMessage> &msg) { 640 bool flushing = false; 641 642 { 643 Mutex::Autolock autoLock(mFlushLock); 644 if (audio) { 645 flushing = mFlushingAudio; 646 } else { 647 flushing = mFlushingVideo; 648 } 649 } 650 651 if (!flushing) { 652 return false; 653 } 654 655 sp<AMessage> notifyConsumed; 656 if (msg->findMessage("notifyConsumed", ¬ifyConsumed)) { 657 notifyConsumed->post(); 658 } 659 660 return true; 661} 662 663void NuPlayer::Renderer::onAudioSinkChanged() { 664 CHECK(!mDrainAudioQueuePending); 665 mNumFramesWritten = 0; 666 uint32_t written; 667 if (mAudioSink->getFramesWritten(&written) == OK) { 668 mNumFramesWritten = written; 669 } 670} 671 672void NuPlayer::Renderer::notifyPosition() { 673 if (mAnchorTimeRealUs < 0 || mAnchorTimeMediaUs < 0) { 674 return; 675 } 676 677 int64_t nowUs = ALooper::GetNowUs(); 678 679 if (mLastPositionUpdateUs >= 0 680 && nowUs < mLastPositionUpdateUs + kMinPositionUpdateDelayUs) { 681 return; 682 } 683 mLastPositionUpdateUs = nowUs; 684 685 int64_t positionUs = (nowUs - mAnchorTimeRealUs) + mAnchorTimeMediaUs; 686 687 sp<AMessage> notify = mNotify->dup(); 688 notify->setInt32("what", kWhatPosition); 689 notify->setInt64("positionUs", positionUs); 690 notify->setInt64("videoLateByUs", mVideoLateByUs); 691 notify->post(); 692} 693 694void NuPlayer::Renderer::onPause() { 695 CHECK(!mPaused); 696 697 mDrainAudioQueuePending = false; 698 ++mAudioQueueGeneration; 699 700 mDrainVideoQueuePending = false; 701 ++mVideoQueueGeneration; 702 703 prepareForMediaRenderingStart(); 704 705 if (mHasAudio) { 706 mAudioSink->pause(); 707 } 708 709 ALOGV("now paused audio queue has %d entries, video has %d entries", 710 mAudioQueue.size(), mVideoQueue.size()); 711 712 mPaused = true; 713} 714 715void NuPlayer::Renderer::onResume() { 716 if (!mPaused) { 717 return; 718 } 719 720 if (mHasAudio) { 721 mAudioSink->start(); 722 } 723 724 mPaused = false; 725 726 if (!mAudioQueue.empty()) { 727 postDrainAudioQueue(); 728 } 729 730 if (!mVideoQueue.empty()) { 731 postDrainVideoQueue(); 732 } 733} 734 735} // namespace android 736 737