NuPlayerDriver.cpp revision e1e5d7a3d3d4d6d644e6c731f977422e004140d5
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::setPlaybackSettings(const AudioPlaybackRate &rate) { 364 Mutex::Autolock autoLock(mLock); 365 status_t err = mPlayer->setPlaybackSettings(rate); 366 if (err == OK) { 367 if (rate.mSpeed == 0.f && mState == STATE_RUNNING) { 368 mState = STATE_PAUSED; 369 // try to update position 370 (void)mPlayer->getCurrentPosition(&mPositionUs); 371 notifyListener_l(MEDIA_PAUSED); 372 } else if (rate.mSpeed != 0.f && mState == STATE_PAUSED) { 373 mState = STATE_RUNNING; 374 } 375 } 376 return err; 377} 378 379status_t NuPlayerDriver::getPlaybackSettings(AudioPlaybackRate *rate) { 380 return mPlayer->getPlaybackSettings(rate); 381} 382 383status_t NuPlayerDriver::setSyncSettings(const AVSyncSettings &sync, float videoFpsHint) { 384 return mPlayer->setSyncSettings(sync, videoFpsHint); 385} 386 387status_t NuPlayerDriver::getSyncSettings(AVSyncSettings *sync, float *videoFps) { 388 return mPlayer->getSyncSettings(sync, videoFps); 389} 390 391status_t NuPlayerDriver::seekTo(int msec) { 392 ALOGD("seekTo(%p) %d ms", this, msec); 393 Mutex::Autolock autoLock(mLock); 394 395 int64_t seekTimeUs = msec * 1000ll; 396 397 switch (mState) { 398 case STATE_PREPARED: 399 case STATE_STOPPED_AND_PREPARED: 400 case STATE_PAUSED: 401 mStartupSeekTimeUs = seekTimeUs; 402 // fall through. 403 case STATE_RUNNING: 404 { 405 mAtEOS = false; 406 mSeekInProgress = true; 407 // seeks can take a while, so we essentially paused 408 notifyListener_l(MEDIA_PAUSED); 409 mPlayer->seekToAsync(seekTimeUs, true /* needNotify */); 410 break; 411 } 412 413 default: 414 return INVALID_OPERATION; 415 } 416 417 mPositionUs = seekTimeUs; 418 return OK; 419} 420 421status_t NuPlayerDriver::getCurrentPosition(int *msec) { 422 int64_t tempUs = 0; 423 { 424 Mutex::Autolock autoLock(mLock); 425 if (mSeekInProgress || mState == STATE_PAUSED) { 426 tempUs = (mPositionUs <= 0) ? 0 : mPositionUs; 427 *msec = (int)divRound(tempUs, (int64_t)(1000)); 428 return OK; 429 } 430 } 431 432 status_t ret = mPlayer->getCurrentPosition(&tempUs); 433 434 Mutex::Autolock autoLock(mLock); 435 // We need to check mSeekInProgress here because mPlayer->seekToAsync is an async call, which 436 // means getCurrentPosition can be called before seek is completed. Iow, renderer may return a 437 // position value that's different the seek to position. 438 if (ret != OK) { 439 tempUs = (mPositionUs <= 0) ? 0 : mPositionUs; 440 } else { 441 mPositionUs = tempUs; 442 } 443 *msec = (int)divRound(tempUs, (int64_t)(1000)); 444 return OK; 445} 446 447status_t NuPlayerDriver::getDuration(int *msec) { 448 Mutex::Autolock autoLock(mLock); 449 450 if (mDurationUs < 0) { 451 return UNKNOWN_ERROR; 452 } 453 454 *msec = (mDurationUs + 500ll) / 1000; 455 456 return OK; 457} 458 459status_t NuPlayerDriver::reset() { 460 ALOGD("reset(%p)", this); 461 Mutex::Autolock autoLock(mLock); 462 463 switch (mState) { 464 case STATE_IDLE: 465 return OK; 466 467 case STATE_SET_DATASOURCE_PENDING: 468 case STATE_RESET_IN_PROGRESS: 469 return INVALID_OPERATION; 470 471 case STATE_PREPARING: 472 { 473 CHECK(mIsAsyncPrepare); 474 475 notifyListener_l(MEDIA_PREPARED); 476 break; 477 } 478 479 default: 480 break; 481 } 482 483 if (mState != STATE_STOPPED) { 484 notifyListener_l(MEDIA_STOPPED); 485 } 486 487 mState = STATE_RESET_IN_PROGRESS; 488 mPlayer->resetAsync(); 489 490 while (mState == STATE_RESET_IN_PROGRESS) { 491 mCondition.wait(mLock); 492 } 493 494 mDurationUs = -1; 495 mPositionUs = -1; 496 mStartupSeekTimeUs = -1; 497 mLooping = false; 498 499 return OK; 500} 501 502status_t NuPlayerDriver::setLooping(int loop) { 503 mLooping = loop != 0; 504 return OK; 505} 506 507player_type NuPlayerDriver::playerType() { 508 return NU_PLAYER; 509} 510 511status_t NuPlayerDriver::invoke(const Parcel &request, Parcel *reply) { 512 if (reply == NULL) { 513 ALOGE("reply is a NULL pointer"); 514 return BAD_VALUE; 515 } 516 517 int32_t methodId; 518 status_t ret = request.readInt32(&methodId); 519 if (ret != OK) { 520 ALOGE("Failed to retrieve the requested method to invoke"); 521 return ret; 522 } 523 524 switch (methodId) { 525 case INVOKE_ID_SET_VIDEO_SCALING_MODE: 526 { 527 int mode = request.readInt32(); 528 return mPlayer->setVideoScalingMode(mode); 529 } 530 531 case INVOKE_ID_GET_TRACK_INFO: 532 { 533 return mPlayer->getTrackInfo(reply); 534 } 535 536 case INVOKE_ID_SELECT_TRACK: 537 { 538 int trackIndex = request.readInt32(); 539 int msec = 0; 540 // getCurrentPosition should always return OK 541 getCurrentPosition(&msec); 542 return mPlayer->selectTrack(trackIndex, true /* select */, msec * 1000ll); 543 } 544 545 case INVOKE_ID_UNSELECT_TRACK: 546 { 547 int trackIndex = request.readInt32(); 548 return mPlayer->selectTrack(trackIndex, false /* select */, 0xdeadbeef /* not used */); 549 } 550 551 case INVOKE_ID_GET_SELECTED_TRACK: 552 { 553 int32_t type = request.readInt32(); 554 return mPlayer->getSelectedTrack(type, reply); 555 } 556 557 default: 558 { 559 return INVALID_OPERATION; 560 } 561 } 562} 563 564void NuPlayerDriver::setAudioSink(const sp<AudioSink> &audioSink) { 565 mPlayer->setAudioSink(audioSink); 566 mAudioSink = audioSink; 567} 568 569status_t NuPlayerDriver::setParameter( 570 int /* key */, const Parcel & /* request */) { 571 return INVALID_OPERATION; 572} 573 574status_t NuPlayerDriver::getParameter(int /* key */, Parcel * /* reply */) { 575 return INVALID_OPERATION; 576} 577 578status_t NuPlayerDriver::getMetadata( 579 const media::Metadata::Filter& /* ids */, Parcel *records) { 580 Mutex::Autolock autoLock(mLock); 581 582 using media::Metadata; 583 584 Metadata meta(records); 585 586 meta.appendBool( 587 Metadata::kPauseAvailable, 588 mPlayerFlags & NuPlayer::Source::FLAG_CAN_PAUSE); 589 590 meta.appendBool( 591 Metadata::kSeekBackwardAvailable, 592 mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_BACKWARD); 593 594 meta.appendBool( 595 Metadata::kSeekForwardAvailable, 596 mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_FORWARD); 597 598 meta.appendBool( 599 Metadata::kSeekAvailable, 600 mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK); 601 602 return OK; 603} 604 605void NuPlayerDriver::notifyResetComplete() { 606 ALOGD("notifyResetComplete(%p)", this); 607 Mutex::Autolock autoLock(mLock); 608 609 CHECK_EQ(mState, STATE_RESET_IN_PROGRESS); 610 mState = STATE_IDLE; 611 mCondition.broadcast(); 612} 613 614void NuPlayerDriver::notifySetSurfaceComplete() { 615 ALOGV("notifySetSurfaceComplete(%p)", this); 616 Mutex::Autolock autoLock(mLock); 617 618 CHECK(mSetSurfaceInProgress); 619 mSetSurfaceInProgress = false; 620 621 mCondition.broadcast(); 622} 623 624void NuPlayerDriver::notifyDuration(int64_t durationUs) { 625 Mutex::Autolock autoLock(mLock); 626 mDurationUs = durationUs; 627} 628 629void NuPlayerDriver::notifySeekComplete() { 630 ALOGV("notifySeekComplete(%p)", this); 631 Mutex::Autolock autoLock(mLock); 632 mSeekInProgress = false; 633 notifySeekComplete_l(); 634} 635 636void NuPlayerDriver::notifySeekComplete_l() { 637 bool wasSeeking = true; 638 if (mState == STATE_STOPPED_AND_PREPARING) { 639 wasSeeking = false; 640 mState = STATE_STOPPED_AND_PREPARED; 641 mCondition.broadcast(); 642 if (!mIsAsyncPrepare) { 643 // if we are preparing synchronously, no need to notify listener 644 return; 645 } 646 } else if (mState == STATE_STOPPED) { 647 // no need to notify listener 648 return; 649 } 650 notifyListener_l(wasSeeking ? MEDIA_SEEK_COMPLETE : MEDIA_PREPARED); 651} 652 653status_t NuPlayerDriver::dump( 654 int fd, const Vector<String16> & /* args */) const { 655 656 Vector<sp<AMessage> > trackStats; 657 mPlayer->getStats(&trackStats); 658 659 FILE *out = fdopen(dup(fd), "w"); 660 661 fprintf(out, " NuPlayer\n"); 662 for (size_t i = 0; i < trackStats.size(); ++i) { 663 const sp<AMessage> &stats = trackStats.itemAt(i); 664 665 AString mime; 666 if (stats->findString("mime", &mime)) { 667 fprintf(out, " mime(%s)\n", mime.c_str()); 668 } 669 670 AString name; 671 if (stats->findString("component-name", &name)) { 672 fprintf(out, " decoder(%s)\n", name.c_str()); 673 } 674 675 if (mime.startsWith("video/")) { 676 int32_t width, height; 677 if (stats->findInt32("width", &width) 678 && stats->findInt32("height", &height)) { 679 fprintf(out, " resolution(%d x %d)\n", width, height); 680 } 681 682 int64_t numFramesTotal = 0; 683 int64_t numFramesDropped = 0; 684 685 stats->findInt64("frames-total", &numFramesTotal); 686 stats->findInt64("frames-dropped-output", &numFramesDropped); 687 fprintf(out, " numFramesTotal(%lld), numFramesDropped(%lld), " 688 "percentageDropped(%.2f%%)\n", 689 (long long)numFramesTotal, 690 (long long)numFramesDropped, 691 numFramesTotal == 0 692 ? 0.0 : (double)(numFramesDropped * 100) / numFramesTotal); 693 } 694 } 695 fclose(out); 696 out = NULL; 697 698 return OK; 699} 700 701void NuPlayerDriver::notifyListener( 702 int msg, int ext1, int ext2, const Parcel *in) { 703 Mutex::Autolock autoLock(mLock); 704 notifyListener_l(msg, ext1, ext2, in); 705} 706 707void NuPlayerDriver::notifyListener_l( 708 int msg, int ext1, int ext2, const Parcel *in) { 709 switch (msg) { 710 case MEDIA_PLAYBACK_COMPLETE: 711 { 712 if (mState != STATE_RESET_IN_PROGRESS) { 713 if (mAutoLoop) { 714 audio_stream_type_t streamType = AUDIO_STREAM_MUSIC; 715 if (mAudioSink != NULL) { 716 streamType = mAudioSink->getAudioStreamType(); 717 } 718 if (streamType == AUDIO_STREAM_NOTIFICATION) { 719 ALOGW("disabling auto-loop for notification"); 720 mAutoLoop = false; 721 } 722 } 723 if (mLooping || mAutoLoop) { 724 mPlayer->seekToAsync(0); 725 if (mAudioSink != NULL) { 726 // The renderer has stopped the sink at the end in order to play out 727 // the last little bit of audio. If we're looping, we need to restart it. 728 mAudioSink->start(); 729 } 730 break; 731 } 732 733 mPlayer->pause(); 734 mState = STATE_PAUSED; 735 } 736 // fall through 737 } 738 739 case MEDIA_ERROR: 740 { 741 mAtEOS = true; 742 break; 743 } 744 745 default: 746 break; 747 } 748 749 mLock.unlock(); 750 sendEvent(msg, ext1, ext2, in); 751 mLock.lock(); 752} 753 754void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) { 755 Mutex::Autolock autoLock(mLock); 756 757 CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING); 758 759 mAsyncResult = err; 760 mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE; 761 mCondition.broadcast(); 762} 763 764void NuPlayerDriver::notifyPrepareCompleted(status_t err) { 765 Mutex::Autolock autoLock(mLock); 766 767 if (mState != STATE_PREPARING) { 768 // We were preparing asynchronously when the client called 769 // reset(), we sent a premature "prepared" notification and 770 // then initiated the reset. This notification is stale. 771 CHECK(mState == STATE_RESET_IN_PROGRESS || mState == STATE_IDLE); 772 return; 773 } 774 775 CHECK_EQ(mState, STATE_PREPARING); 776 777 mAsyncResult = err; 778 779 if (err == OK) { 780 // update state before notifying client, so that if client calls back into NuPlayerDriver 781 // in response, NuPlayerDriver has the right state 782 mState = STATE_PREPARED; 783 if (mIsAsyncPrepare) { 784 notifyListener_l(MEDIA_PREPARED); 785 } 786 } else { 787 mState = STATE_UNPREPARED; 788 if (mIsAsyncPrepare) { 789 notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err); 790 } 791 } 792 793 sp<MetaData> meta = mPlayer->getFileMeta(); 794 int32_t loop; 795 if (meta != NULL 796 && meta->findInt32(kKeyAutoLoop, &loop) && loop != 0) { 797 mAutoLoop = true; 798 } 799 800 mCondition.broadcast(); 801} 802 803void NuPlayerDriver::notifyFlagsChanged(uint32_t flags) { 804 Mutex::Autolock autoLock(mLock); 805 806 mPlayerFlags = flags; 807} 808 809} // namespace android 810