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