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