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