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