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