NuPlayerDriver.cpp revision d3c28f66a48dd450f5f6d8a3ba92a40132bc45de
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 "NuPlayerDriver" 19#include <inttypes.h> 20#include <utils/Log.h> 21 22#include "NuPlayerDriver.h" 23 24#include "NuPlayer.h" 25#include "NuPlayerSource.h" 26 27#include <media/stagefright/foundation/ADebug.h> 28#include <media/stagefright/foundation/ALooper.h> 29#include <media/stagefright/foundation/AUtils.h> 30#include <media/stagefright/MetaData.h> 31#include <media/stagefright/Utils.h> 32 33namespace android { 34 35NuPlayerDriver::NuPlayerDriver() 36 : mState(STATE_IDLE), 37 mIsAsyncPrepare(false), 38 mAsyncResult(UNKNOWN_ERROR), 39 mSetSurfaceInProgress(false), 40 mDurationUs(-1), 41 mPositionUs(-1), 42 mSeekInProgress(false), 43 mLooper(new ALooper), 44 mPlayerFlags(0), 45 mAtEOS(false), 46 mLooping(false), 47 mAutoLoop(false), 48 mStartupSeekTimeUs(-1) { 49 ALOGV("NuPlayerDriver(%p)", this); 50 mLooper->setName("NuPlayerDriver Looper"); 51 52 mLooper->start( 53 false, /* runOnCallingThread */ 54 true, /* canCallJava */ 55 PRIORITY_AUDIO); 56 57 mPlayer = new NuPlayer; 58 mLooper->registerHandler(mPlayer); 59 60 mPlayer->setDriver(this); 61} 62 63NuPlayerDriver::~NuPlayerDriver() { 64 ALOGV("~NuPlayerDriver(%p)", this); 65 mLooper->stop(); 66} 67 68status_t NuPlayerDriver::initCheck() { 69 return OK; 70} 71 72status_t NuPlayerDriver::setUID(uid_t uid) { 73 mPlayer->setUID(uid); 74 75 return OK; 76} 77 78status_t NuPlayerDriver::setDataSource( 79 const sp<IMediaHTTPService> &httpService, 80 const char *url, 81 const KeyedVector<String8, String8> *headers) { 82 ALOGV("setDataSource(%p) url(%s)", this, uriDebugString(url, false).c_str()); 83 Mutex::Autolock autoLock(mLock); 84 85 if (mState != STATE_IDLE) { 86 return INVALID_OPERATION; 87 } 88 89 mState = STATE_SET_DATASOURCE_PENDING; 90 91 mPlayer->setDataSourceAsync(httpService, url, headers); 92 93 while (mState == STATE_SET_DATASOURCE_PENDING) { 94 mCondition.wait(mLock); 95 } 96 97 return mAsyncResult; 98} 99 100status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) { 101 ALOGV("setDataSource(%p) file(%d)", this, fd); 102 Mutex::Autolock autoLock(mLock); 103 104 if (mState != STATE_IDLE) { 105 return INVALID_OPERATION; 106 } 107 108 mState = STATE_SET_DATASOURCE_PENDING; 109 110 mPlayer->setDataSourceAsync(fd, offset, length); 111 112 while (mState == STATE_SET_DATASOURCE_PENDING) { 113 mCondition.wait(mLock); 114 } 115 116 return mAsyncResult; 117} 118 119status_t NuPlayerDriver::setDataSource(const sp<IStreamSource> &source) { 120 ALOGV("setDataSource(%p) stream source", this); 121 Mutex::Autolock autoLock(mLock); 122 123 if (mState != STATE_IDLE) { 124 return INVALID_OPERATION; 125 } 126 127 mState = STATE_SET_DATASOURCE_PENDING; 128 129 mPlayer->setDataSourceAsync(source); 130 131 while (mState == STATE_SET_DATASOURCE_PENDING) { 132 mCondition.wait(mLock); 133 } 134 135 return mAsyncResult; 136} 137 138status_t NuPlayerDriver::setDataSource(const sp<DataSource> &source) { 139 ALOGV("setDataSource(%p) callback source", this); 140 Mutex::Autolock autoLock(mLock); 141 142 if (mState != STATE_IDLE) { 143 return INVALID_OPERATION; 144 } 145 146 mState = STATE_SET_DATASOURCE_PENDING; 147 148 mPlayer->setDataSourceAsync(source); 149 150 while (mState == STATE_SET_DATASOURCE_PENDING) { 151 mCondition.wait(mLock); 152 } 153 154 return mAsyncResult; 155} 156 157status_t NuPlayerDriver::setVideoSurfaceTexture( 158 const sp<IGraphicBufferProducer> &bufferProducer) { 159 ALOGV("setVideoSurfaceTexture(%p)", this); 160 Mutex::Autolock autoLock(mLock); 161 162 if (mSetSurfaceInProgress) { 163 return INVALID_OPERATION; 164 } 165 166 switch (mState) { 167 case STATE_SET_DATASOURCE_PENDING: 168 case STATE_RESET_IN_PROGRESS: 169 return INVALID_OPERATION; 170 171 default: 172 break; 173 } 174 175 mSetSurfaceInProgress = true; 176 177 mPlayer->setVideoSurfaceTextureAsync(bufferProducer); 178 179 while (mSetSurfaceInProgress) { 180 mCondition.wait(mLock); 181 } 182 183 return OK; 184} 185 186status_t NuPlayerDriver::prepare() { 187 ALOGV("prepare(%p)", this); 188 Mutex::Autolock autoLock(mLock); 189 return prepare_l(); 190} 191 192status_t NuPlayerDriver::prepare_l() { 193 switch (mState) { 194 case STATE_UNPREPARED: 195 mState = STATE_PREPARING; 196 197 // Make sure we're not posting any notifications, success or 198 // failure information is only communicated through our result 199 // code. 200 mIsAsyncPrepare = false; 201 mPlayer->prepareAsync(); 202 while (mState == STATE_PREPARING) { 203 mCondition.wait(mLock); 204 } 205 return (mState == STATE_PREPARED) ? OK : UNKNOWN_ERROR; 206 case STATE_STOPPED: 207 // this is really just paused. handle as seek to start 208 mAtEOS = false; 209 mState = STATE_STOPPED_AND_PREPARING; 210 mIsAsyncPrepare = false; 211 mPlayer->seekToAsync(0, true /* needNotify */); 212 while (mState == STATE_STOPPED_AND_PREPARING) { 213 mCondition.wait(mLock); 214 } 215 return (mState == STATE_STOPPED_AND_PREPARED) ? OK : UNKNOWN_ERROR; 216 default: 217 return INVALID_OPERATION; 218 }; 219} 220 221status_t NuPlayerDriver::prepareAsync() { 222 ALOGV("prepareAsync(%p)", this); 223 Mutex::Autolock autoLock(mLock); 224 225 switch (mState) { 226 case STATE_UNPREPARED: 227 mState = STATE_PREPARING; 228 mIsAsyncPrepare = true; 229 mPlayer->prepareAsync(); 230 return OK; 231 case STATE_STOPPED: 232 // this is really just paused. handle as seek to start 233 mAtEOS = false; 234 mState = STATE_STOPPED_AND_PREPARING; 235 mIsAsyncPrepare = true; 236 mPlayer->seekToAsync(0, true /* needNotify */); 237 return OK; 238 default: 239 return INVALID_OPERATION; 240 }; 241} 242 243status_t NuPlayerDriver::start() { 244 ALOGD("start(%p)", this); 245 Mutex::Autolock autoLock(mLock); 246 247 switch (mState) { 248 case STATE_UNPREPARED: 249 { 250 status_t err = prepare_l(); 251 252 if (err != OK) { 253 return err; 254 } 255 256 CHECK_EQ(mState, STATE_PREPARED); 257 258 // fall through 259 } 260 261 case STATE_PAUSED: 262 case STATE_STOPPED_AND_PREPARED: 263 { 264 if (mAtEOS && mStartupSeekTimeUs < 0) { 265 mStartupSeekTimeUs = 0; 266 mPositionUs = -1; 267 } 268 269 // fall through 270 } 271 272 case STATE_PREPARED: 273 { 274 mAtEOS = false; 275 mPlayer->start(); 276 277 if (mStartupSeekTimeUs >= 0) { 278 mPlayer->seekToAsync(mStartupSeekTimeUs); 279 mStartupSeekTimeUs = -1; 280 } 281 break; 282 } 283 284 case STATE_RUNNING: 285 { 286 if (mAtEOS) { 287 mPlayer->seekToAsync(0); 288 mAtEOS = false; 289 mPositionUs = -1; 290 } 291 break; 292 } 293 294 default: 295 return INVALID_OPERATION; 296 } 297 298 mState = STATE_RUNNING; 299 300 return OK; 301} 302 303status_t NuPlayerDriver::stop() { 304 ALOGD("stop(%p)", this); 305 Mutex::Autolock autoLock(mLock); 306 307 switch (mState) { 308 case STATE_RUNNING: 309 mPlayer->pause(); 310 // fall through 311 312 case STATE_PAUSED: 313 mState = STATE_STOPPED; 314 notifyListener_l(MEDIA_STOPPED); 315 break; 316 317 case STATE_PREPARED: 318 case STATE_STOPPED: 319 case STATE_STOPPED_AND_PREPARING: 320 case STATE_STOPPED_AND_PREPARED: 321 mState = STATE_STOPPED; 322 break; 323 324 default: 325 return INVALID_OPERATION; 326 } 327 328 return OK; 329} 330 331status_t NuPlayerDriver::pause() { 332 // The NuPlayerRenderer may get flushed if pause for long enough, e.g. the pause timeout tear 333 // down for audio offload mode. If that happens, the NuPlayerRenderer will no longer know the 334 // current position. So similar to seekTo, update |mPositionUs| to the pause position by calling 335 // getCurrentPosition here. 336 int msec; 337 getCurrentPosition(&msec); 338 339 Mutex::Autolock autoLock(mLock); 340 341 switch (mState) { 342 case STATE_PAUSED: 343 case STATE_PREPARED: 344 return OK; 345 346 case STATE_RUNNING: 347 mState = STATE_PAUSED; 348 notifyListener_l(MEDIA_PAUSED); 349 mPlayer->pause(); 350 break; 351 352 default: 353 return INVALID_OPERATION; 354 } 355 356 return OK; 357} 358 359bool NuPlayerDriver::isPlaying() { 360 return mState == STATE_RUNNING && !mAtEOS; 361} 362 363status_t NuPlayerDriver::setPlaybackRate(float rate) { 364 mPlayer->setPlaybackRate(rate); 365 return OK; 366} 367 368status_t NuPlayerDriver::seekTo(int msec) { 369 ALOGD("seekTo(%p) %d ms", this, msec); 370 Mutex::Autolock autoLock(mLock); 371 372 int64_t seekTimeUs = msec * 1000ll; 373 374 switch (mState) { 375 case STATE_PREPARED: 376 case STATE_STOPPED_AND_PREPARED: 377 { 378 mStartupSeekTimeUs = seekTimeUs; 379 // pretend that the seek completed. It will actually happen when starting playback. 380 // TODO: actually perform the seek here, so the player is ready to go at the new 381 // location 382 notifySeekComplete_l(); 383 break; 384 } 385 386 case STATE_RUNNING: 387 case STATE_PAUSED: 388 { 389 mAtEOS = false; 390 mSeekInProgress = true; 391 if (mState == STATE_PAUSED) { 392 mStartupSeekTimeUs = seekTimeUs; 393 } 394 // seeks can take a while, so we essentially paused 395 notifyListener_l(MEDIA_PAUSED); 396 mPlayer->seekToAsync(seekTimeUs, true /* needNotify */); 397 break; 398 } 399 400 default: 401 return INVALID_OPERATION; 402 } 403 404 mPositionUs = seekTimeUs; 405 return OK; 406} 407 408status_t NuPlayerDriver::getCurrentPosition(int *msec) { 409 int64_t tempUs = 0; 410 { 411 Mutex::Autolock autoLock(mLock); 412 if (mSeekInProgress || mState == STATE_PAUSED) { 413 tempUs = (mPositionUs <= 0) ? 0 : mPositionUs; 414 *msec = (int)divRound(tempUs, (int64_t)(1000)); 415 return OK; 416 } 417 } 418 419 status_t ret = mPlayer->getCurrentPosition(&tempUs); 420 421 Mutex::Autolock autoLock(mLock); 422 // We need to check mSeekInProgress here because mPlayer->seekToAsync is an async call, which 423 // means getCurrentPosition can be called before seek is completed. Iow, renderer may return a 424 // position value that's different the seek to position. 425 if (ret != OK) { 426 tempUs = (mPositionUs <= 0) ? 0 : mPositionUs; 427 } else { 428 mPositionUs = tempUs; 429 } 430 *msec = (int)divRound(tempUs, (int64_t)(1000)); 431 return OK; 432} 433 434status_t NuPlayerDriver::getDuration(int *msec) { 435 Mutex::Autolock autoLock(mLock); 436 437 if (mDurationUs < 0) { 438 return UNKNOWN_ERROR; 439 } 440 441 *msec = (mDurationUs + 500ll) / 1000; 442 443 return OK; 444} 445 446status_t NuPlayerDriver::reset() { 447 ALOGD("reset(%p)", this); 448 Mutex::Autolock autoLock(mLock); 449 450 switch (mState) { 451 case STATE_IDLE: 452 return OK; 453 454 case STATE_SET_DATASOURCE_PENDING: 455 case STATE_RESET_IN_PROGRESS: 456 return INVALID_OPERATION; 457 458 case STATE_PREPARING: 459 { 460 CHECK(mIsAsyncPrepare); 461 462 notifyListener_l(MEDIA_PREPARED); 463 break; 464 } 465 466 default: 467 break; 468 } 469 470 if (mState != STATE_STOPPED) { 471 notifyListener_l(MEDIA_STOPPED); 472 } 473 474 mState = STATE_RESET_IN_PROGRESS; 475 mPlayer->resetAsync(); 476 477 while (mState == STATE_RESET_IN_PROGRESS) { 478 mCondition.wait(mLock); 479 } 480 481 mDurationUs = -1; 482 mPositionUs = -1; 483 mStartupSeekTimeUs = -1; 484 mLooping = false; 485 486 return OK; 487} 488 489status_t NuPlayerDriver::setLooping(int loop) { 490 mLooping = loop != 0; 491 return OK; 492} 493 494player_type NuPlayerDriver::playerType() { 495 return NU_PLAYER; 496} 497 498status_t NuPlayerDriver::invoke(const Parcel &request, Parcel *reply) { 499 if (reply == NULL) { 500 ALOGE("reply is a NULL pointer"); 501 return BAD_VALUE; 502 } 503 504 int32_t methodId; 505 status_t ret = request.readInt32(&methodId); 506 if (ret != OK) { 507 ALOGE("Failed to retrieve the requested method to invoke"); 508 return ret; 509 } 510 511 switch (methodId) { 512 case INVOKE_ID_SET_VIDEO_SCALING_MODE: 513 { 514 int mode = request.readInt32(); 515 return mPlayer->setVideoScalingMode(mode); 516 } 517 518 case INVOKE_ID_GET_TRACK_INFO: 519 { 520 return mPlayer->getTrackInfo(reply); 521 } 522 523 case INVOKE_ID_SELECT_TRACK: 524 { 525 int trackIndex = request.readInt32(); 526 int msec = 0; 527 // getCurrentPosition should always return OK 528 getCurrentPosition(&msec); 529 return mPlayer->selectTrack(trackIndex, true /* select */, msec * 1000ll); 530 } 531 532 case INVOKE_ID_UNSELECT_TRACK: 533 { 534 int trackIndex = request.readInt32(); 535 return mPlayer->selectTrack(trackIndex, false /* select */, 0xdeadbeef /* not used */); 536 } 537 538 case INVOKE_ID_GET_SELECTED_TRACK: 539 { 540 int32_t type = request.readInt32(); 541 return mPlayer->getSelectedTrack(type, reply); 542 } 543 544 default: 545 { 546 return INVALID_OPERATION; 547 } 548 } 549} 550 551void NuPlayerDriver::setAudioSink(const sp<AudioSink> &audioSink) { 552 mPlayer->setAudioSink(audioSink); 553 mAudioSink = audioSink; 554} 555 556status_t NuPlayerDriver::setParameter( 557 int /* key */, const Parcel & /* request */) { 558 return INVALID_OPERATION; 559} 560 561status_t NuPlayerDriver::getParameter(int /* key */, Parcel * /* reply */) { 562 return INVALID_OPERATION; 563} 564 565status_t NuPlayerDriver::getMetadata( 566 const media::Metadata::Filter& /* ids */, Parcel *records) { 567 Mutex::Autolock autoLock(mLock); 568 569 using media::Metadata; 570 571 Metadata meta(records); 572 573 meta.appendBool( 574 Metadata::kPauseAvailable, 575 mPlayerFlags & NuPlayer::Source::FLAG_CAN_PAUSE); 576 577 meta.appendBool( 578 Metadata::kSeekBackwardAvailable, 579 mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_BACKWARD); 580 581 meta.appendBool( 582 Metadata::kSeekForwardAvailable, 583 mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_FORWARD); 584 585 meta.appendBool( 586 Metadata::kSeekAvailable, 587 mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK); 588 589 return OK; 590} 591 592void NuPlayerDriver::notifyResetComplete() { 593 ALOGD("notifyResetComplete(%p)", this); 594 Mutex::Autolock autoLock(mLock); 595 596 CHECK_EQ(mState, STATE_RESET_IN_PROGRESS); 597 mState = STATE_IDLE; 598 mCondition.broadcast(); 599} 600 601void NuPlayerDriver::notifySetSurfaceComplete() { 602 ALOGV("notifySetSurfaceComplete(%p)", this); 603 Mutex::Autolock autoLock(mLock); 604 605 CHECK(mSetSurfaceInProgress); 606 mSetSurfaceInProgress = false; 607 608 mCondition.broadcast(); 609} 610 611void NuPlayerDriver::notifyDuration(int64_t durationUs) { 612 Mutex::Autolock autoLock(mLock); 613 mDurationUs = durationUs; 614} 615 616void NuPlayerDriver::notifySeekComplete() { 617 ALOGV("notifySeekComplete(%p)", this); 618 Mutex::Autolock autoLock(mLock); 619 mSeekInProgress = false; 620 notifySeekComplete_l(); 621} 622 623void NuPlayerDriver::notifySeekComplete_l() { 624 bool wasSeeking = true; 625 if (mState == STATE_STOPPED_AND_PREPARING) { 626 wasSeeking = false; 627 mState = STATE_STOPPED_AND_PREPARED; 628 mCondition.broadcast(); 629 if (!mIsAsyncPrepare) { 630 // if we are preparing synchronously, no need to notify listener 631 return; 632 } 633 } else if (mState == STATE_STOPPED) { 634 // no need to notify listener 635 return; 636 } 637 notifyListener_l(wasSeeking ? MEDIA_SEEK_COMPLETE : MEDIA_PREPARED); 638} 639 640status_t NuPlayerDriver::dump( 641 int fd, const Vector<String16> & /* args */) const { 642 int64_t numFramesTotal; 643 int64_t numFramesDropped; 644 mPlayer->getStats(&numFramesTotal, &numFramesDropped); 645 646 FILE *out = fdopen(dup(fd), "w"); 647 648 fprintf(out, " NuPlayer\n"); 649 fprintf(out, " numFramesTotal(%" PRId64 "), numFramesDropped(%" PRId64 "), " 650 "percentageDropped(%.2f)\n", 651 numFramesTotal, 652 numFramesDropped, 653 numFramesTotal == 0 654 ? 0.0 : (double)numFramesDropped / numFramesTotal); 655 656 fclose(out); 657 out = NULL; 658 659 return OK; 660} 661 662void NuPlayerDriver::notifyListener( 663 int msg, int ext1, int ext2, const Parcel *in) { 664 Mutex::Autolock autoLock(mLock); 665 notifyListener_l(msg, ext1, ext2, in); 666} 667 668void NuPlayerDriver::notifyListener_l( 669 int msg, int ext1, int ext2, const Parcel *in) { 670 switch (msg) { 671 case MEDIA_PLAYBACK_COMPLETE: 672 { 673 if (mState != STATE_RESET_IN_PROGRESS) { 674 if (mAutoLoop) { 675 audio_stream_type_t streamType = AUDIO_STREAM_MUSIC; 676 if (mAudioSink != NULL) { 677 streamType = mAudioSink->getAudioStreamType(); 678 } 679 if (streamType == AUDIO_STREAM_NOTIFICATION) { 680 ALOGW("disabling auto-loop for notification"); 681 mAutoLoop = false; 682 } 683 } 684 if (mLooping || mAutoLoop) { 685 mPlayer->seekToAsync(0); 686 if (mAudioSink != NULL) { 687 // The renderer has stopped the sink at the end in order to play out 688 // the last little bit of audio. If we're looping, we need to restart it. 689 mAudioSink->start(); 690 } 691 break; 692 } 693 694 mPlayer->pause(); 695 mState = STATE_PAUSED; 696 } 697 // fall through 698 } 699 700 case MEDIA_ERROR: 701 { 702 mAtEOS = true; 703 break; 704 } 705 706 default: 707 break; 708 } 709 710 mLock.unlock(); 711 sendEvent(msg, ext1, ext2, in); 712 mLock.lock(); 713} 714 715void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) { 716 Mutex::Autolock autoLock(mLock); 717 718 CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING); 719 720 mAsyncResult = err; 721 mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE; 722 mCondition.broadcast(); 723} 724 725void NuPlayerDriver::notifyPrepareCompleted(status_t err) { 726 Mutex::Autolock autoLock(mLock); 727 728 if (mState != STATE_PREPARING) { 729 // We were preparing asynchronously when the client called 730 // reset(), we sent a premature "prepared" notification and 731 // then initiated the reset. This notification is stale. 732 CHECK(mState == STATE_RESET_IN_PROGRESS || mState == STATE_IDLE); 733 return; 734 } 735 736 CHECK_EQ(mState, STATE_PREPARING); 737 738 mAsyncResult = err; 739 740 if (err == OK) { 741 // update state before notifying client, so that if client calls back into NuPlayerDriver 742 // in response, NuPlayerDriver has the right state 743 mState = STATE_PREPARED; 744 if (mIsAsyncPrepare) { 745 notifyListener_l(MEDIA_PREPARED); 746 } 747 } else { 748 mState = STATE_UNPREPARED; 749 if (mIsAsyncPrepare) { 750 notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err); 751 } 752 } 753 754 sp<MetaData> meta = mPlayer->getFileMeta(); 755 int32_t loop; 756 if (meta != NULL 757 && meta->findInt32(kKeyAutoLoop, &loop) && loop != 0) { 758 mAutoLoop = true; 759 } 760 761 mCondition.broadcast(); 762} 763 764void NuPlayerDriver::notifyFlagsChanged(uint32_t flags) { 765 Mutex::Autolock autoLock(mLock); 766 767 mPlayerFlags = flags; 768} 769 770} // namespace android 771