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