• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
33 namespace android {
34 
NuPlayerDriver()35 NuPlayerDriver::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 
~NuPlayerDriver()63 NuPlayerDriver::~NuPlayerDriver() {
64     ALOGV("~NuPlayerDriver(%p)", this);
65     mLooper->stop();
66 }
67 
initCheck()68 status_t NuPlayerDriver::initCheck() {
69     return OK;
70 }
71 
setUID(uid_t uid)72 status_t NuPlayerDriver::setUID(uid_t uid) {
73     mPlayer->setUID(uid);
74 
75     return OK;
76 }
77 
setDataSource(const sp<IMediaHTTPService> & httpService,const char * url,const KeyedVector<String8,String8> * headers)78 status_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 
setDataSource(int fd,int64_t offset,int64_t length)100 status_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 
setDataSource(const sp<IStreamSource> & source)119 status_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 
setVideoSurfaceTexture(const sp<IGraphicBufferProducer> & bufferProducer)138 status_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 
prepare()167 status_t NuPlayerDriver::prepare() {
168     ALOGV("prepare(%p)", this);
169     Mutex::Autolock autoLock(mLock);
170     return prepare_l();
171 }
172 
prepare_l()173 status_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 
prepareAsync()202 status_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 
start()224 status_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 
stop()284 status_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 
pause()312 status_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 
isPlaying()340 bool NuPlayerDriver::isPlaying() {
341     return mState == STATE_RUNNING && !mAtEOS;
342 }
343 
seekTo(int msec)344 status_t NuPlayerDriver::seekTo(int msec) {
345     ALOGD("seekTo(%p) %d ms", this, msec);
346     Mutex::Autolock autoLock(mLock);
347 
348     int64_t seekTimeUs = msec * 1000ll;
349 
350     switch (mState) {
351         case STATE_PREPARED:
352         case STATE_STOPPED_AND_PREPARED:
353         {
354             mStartupSeekTimeUs = seekTimeUs;
355             // pretend that the seek completed. It will actually happen when starting playback.
356             // TODO: actually perform the seek here, so the player is ready to go at the new
357             // location
358             notifySeekComplete_l();
359             break;
360         }
361 
362         case STATE_RUNNING:
363         case STATE_PAUSED:
364         {
365             mAtEOS = false;
366             mSeekInProgress = true;
367             // seeks can take a while, so we essentially paused
368             notifyListener_l(MEDIA_PAUSED);
369             mPlayer->seekToAsync(seekTimeUs, true /* needNotify */);
370             break;
371         }
372 
373         default:
374             return INVALID_OPERATION;
375     }
376 
377     mPositionUs = seekTimeUs;
378     return OK;
379 }
380 
getCurrentPosition(int * msec)381 status_t NuPlayerDriver::getCurrentPosition(int *msec) {
382     int64_t tempUs = 0;
383     {
384         Mutex::Autolock autoLock(mLock);
385         if (mSeekInProgress || mState == STATE_PAUSED) {
386             tempUs = (mPositionUs <= 0) ? 0 : mPositionUs;
387             *msec = (int)divRound(tempUs, (int64_t)(1000));
388             return OK;
389         }
390     }
391 
392     status_t ret = mPlayer->getCurrentPosition(&tempUs);
393 
394     Mutex::Autolock autoLock(mLock);
395     // We need to check mSeekInProgress here because mPlayer->seekToAsync is an async call, which
396     // means getCurrentPosition can be called before seek is completed. Iow, renderer may return a
397     // position value that's different the seek to position.
398     if (ret != OK) {
399         tempUs = (mPositionUs <= 0) ? 0 : mPositionUs;
400     } else {
401         mPositionUs = tempUs;
402     }
403     *msec = (int)divRound(tempUs, (int64_t)(1000));
404     return OK;
405 }
406 
getDuration(int * msec)407 status_t NuPlayerDriver::getDuration(int *msec) {
408     Mutex::Autolock autoLock(mLock);
409 
410     if (mDurationUs < 0) {
411         return UNKNOWN_ERROR;
412     }
413 
414     *msec = (mDurationUs + 500ll) / 1000;
415 
416     return OK;
417 }
418 
reset()419 status_t NuPlayerDriver::reset() {
420     ALOGD("reset(%p)", this);
421     Mutex::Autolock autoLock(mLock);
422 
423     switch (mState) {
424         case STATE_IDLE:
425             return OK;
426 
427         case STATE_SET_DATASOURCE_PENDING:
428         case STATE_RESET_IN_PROGRESS:
429             return INVALID_OPERATION;
430 
431         case STATE_PREPARING:
432         {
433             CHECK(mIsAsyncPrepare);
434 
435             notifyListener_l(MEDIA_PREPARED);
436             break;
437         }
438 
439         default:
440             break;
441     }
442 
443     if (mState != STATE_STOPPED) {
444         notifyListener_l(MEDIA_STOPPED);
445     }
446 
447     mState = STATE_RESET_IN_PROGRESS;
448     mPlayer->resetAsync();
449 
450     while (mState == STATE_RESET_IN_PROGRESS) {
451         mCondition.wait(mLock);
452     }
453 
454     mDurationUs = -1;
455     mPositionUs = -1;
456     mStartupSeekTimeUs = -1;
457     mLooping = false;
458 
459     return OK;
460 }
461 
setLooping(int loop)462 status_t NuPlayerDriver::setLooping(int loop) {
463     mLooping = loop != 0;
464     return OK;
465 }
466 
playerType()467 player_type NuPlayerDriver::playerType() {
468     return NU_PLAYER;
469 }
470 
invoke(const Parcel & request,Parcel * reply)471 status_t NuPlayerDriver::invoke(const Parcel &request, Parcel *reply) {
472     if (reply == NULL) {
473         ALOGE("reply is a NULL pointer");
474         return BAD_VALUE;
475     }
476 
477     int32_t methodId;
478     status_t ret = request.readInt32(&methodId);
479     if (ret != OK) {
480         ALOGE("Failed to retrieve the requested method to invoke");
481         return ret;
482     }
483 
484     switch (methodId) {
485         case INVOKE_ID_SET_VIDEO_SCALING_MODE:
486         {
487             int mode = request.readInt32();
488             return mPlayer->setVideoScalingMode(mode);
489         }
490 
491         case INVOKE_ID_GET_TRACK_INFO:
492         {
493             return mPlayer->getTrackInfo(reply);
494         }
495 
496         case INVOKE_ID_SELECT_TRACK:
497         {
498             int trackIndex = request.readInt32();
499             int msec = 0;
500             // getCurrentPosition should always return OK
501             getCurrentPosition(&msec);
502             return mPlayer->selectTrack(trackIndex, true /* select */, msec * 1000ll);
503         }
504 
505         case INVOKE_ID_UNSELECT_TRACK:
506         {
507             int trackIndex = request.readInt32();
508             return mPlayer->selectTrack(trackIndex, false /* select */, 0xdeadbeef /* not used */);
509         }
510 
511         case INVOKE_ID_GET_SELECTED_TRACK:
512         {
513             int32_t type = request.readInt32();
514             return mPlayer->getSelectedTrack(type, reply);
515         }
516 
517         default:
518         {
519             return INVALID_OPERATION;
520         }
521     }
522 }
523 
setAudioSink(const sp<AudioSink> & audioSink)524 void NuPlayerDriver::setAudioSink(const sp<AudioSink> &audioSink) {
525     mPlayer->setAudioSink(audioSink);
526     mAudioSink = audioSink;
527 }
528 
setParameter(int,const Parcel &)529 status_t NuPlayerDriver::setParameter(
530         int /* key */, const Parcel & /* request */) {
531     return INVALID_OPERATION;
532 }
533 
getParameter(int,Parcel *)534 status_t NuPlayerDriver::getParameter(int /* key */, Parcel * /* reply */) {
535     return INVALID_OPERATION;
536 }
537 
getMetadata(const media::Metadata::Filter &,Parcel * records)538 status_t NuPlayerDriver::getMetadata(
539         const media::Metadata::Filter& /* ids */, Parcel *records) {
540     Mutex::Autolock autoLock(mLock);
541 
542     using media::Metadata;
543 
544     Metadata meta(records);
545 
546     meta.appendBool(
547             Metadata::kPauseAvailable,
548             mPlayerFlags & NuPlayer::Source::FLAG_CAN_PAUSE);
549 
550     meta.appendBool(
551             Metadata::kSeekBackwardAvailable,
552             mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_BACKWARD);
553 
554     meta.appendBool(
555             Metadata::kSeekForwardAvailable,
556             mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_FORWARD);
557 
558     meta.appendBool(
559             Metadata::kSeekAvailable,
560             mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK);
561 
562     return OK;
563 }
564 
notifyResetComplete()565 void NuPlayerDriver::notifyResetComplete() {
566     ALOGD("notifyResetComplete(%p)", this);
567     Mutex::Autolock autoLock(mLock);
568 
569     CHECK_EQ(mState, STATE_RESET_IN_PROGRESS);
570     mState = STATE_IDLE;
571     mCondition.broadcast();
572 }
573 
notifySetSurfaceComplete()574 void NuPlayerDriver::notifySetSurfaceComplete() {
575     ALOGV("notifySetSurfaceComplete(%p)", this);
576     Mutex::Autolock autoLock(mLock);
577 
578     CHECK(mSetSurfaceInProgress);
579     mSetSurfaceInProgress = false;
580 
581     mCondition.broadcast();
582 }
583 
notifyDuration(int64_t durationUs)584 void NuPlayerDriver::notifyDuration(int64_t durationUs) {
585     Mutex::Autolock autoLock(mLock);
586     mDurationUs = durationUs;
587 }
588 
notifySeekComplete()589 void NuPlayerDriver::notifySeekComplete() {
590     ALOGV("notifySeekComplete(%p)", this);
591     Mutex::Autolock autoLock(mLock);
592     mSeekInProgress = false;
593     notifySeekComplete_l();
594 }
595 
notifySeekComplete_l()596 void NuPlayerDriver::notifySeekComplete_l() {
597     bool wasSeeking = true;
598     if (mState == STATE_STOPPED_AND_PREPARING) {
599         wasSeeking = false;
600         mState = STATE_STOPPED_AND_PREPARED;
601         mCondition.broadcast();
602         if (!mIsAsyncPrepare) {
603             // if we are preparing synchronously, no need to notify listener
604             return;
605         }
606     } else if (mState == STATE_STOPPED) {
607         // no need to notify listener
608         return;
609     }
610     notifyListener_l(wasSeeking ? MEDIA_SEEK_COMPLETE : MEDIA_PREPARED);
611 }
612 
dump(int fd,const Vector<String16> &) const613 status_t NuPlayerDriver::dump(
614         int fd, const Vector<String16> & /* args */) const {
615     int64_t numFramesTotal;
616     int64_t numFramesDropped;
617     mPlayer->getStats(&numFramesTotal, &numFramesDropped);
618 
619     FILE *out = fdopen(dup(fd), "w");
620 
621     fprintf(out, " NuPlayer\n");
622     fprintf(out, "  numFramesTotal(%" PRId64 "), numFramesDropped(%" PRId64 "), "
623                  "percentageDropped(%.2f)\n",
624                  numFramesTotal,
625                  numFramesDropped,
626                  numFramesTotal == 0
627                     ? 0.0 : (double)numFramesDropped / numFramesTotal);
628 
629     fclose(out);
630     out = NULL;
631 
632     return OK;
633 }
634 
notifyListener(int msg,int ext1,int ext2,const Parcel * in)635 void NuPlayerDriver::notifyListener(
636         int msg, int ext1, int ext2, const Parcel *in) {
637     Mutex::Autolock autoLock(mLock);
638     notifyListener_l(msg, ext1, ext2, in);
639 }
640 
notifyListener_l(int msg,int ext1,int ext2,const Parcel * in)641 void NuPlayerDriver::notifyListener_l(
642         int msg, int ext1, int ext2, const Parcel *in) {
643     switch (msg) {
644         case MEDIA_PLAYBACK_COMPLETE:
645         {
646             if (mState != STATE_RESET_IN_PROGRESS) {
647                 if (mAutoLoop) {
648                     audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
649                     if (mAudioSink != NULL) {
650                         streamType = mAudioSink->getAudioStreamType();
651                     }
652                     if (streamType == AUDIO_STREAM_NOTIFICATION) {
653                         ALOGW("disabling auto-loop for notification");
654                         mAutoLoop = false;
655                     }
656                 }
657                 if (mLooping || (mAutoLoop
658                         && (mAudioSink == NULL || mAudioSink->realtime()))) {
659                     mPlayer->seekToAsync(0);
660                     if (mAudioSink != NULL) {
661                         // The renderer has stopped the sink at the end in order to play out
662                         // the last little bit of audio. If we're looping, we need to restart it.
663                         mAudioSink->start();
664                     }
665                     break;
666                 }
667 
668                 mPlayer->pause();
669                 mState = STATE_PAUSED;
670             }
671             // fall through
672         }
673 
674         case MEDIA_ERROR:
675         {
676             mAtEOS = true;
677             break;
678         }
679 
680         default:
681             break;
682     }
683 
684     mLock.unlock();
685     sendEvent(msg, ext1, ext2, in);
686     mLock.lock();
687 }
688 
notifySetDataSourceCompleted(status_t err)689 void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) {
690     Mutex::Autolock autoLock(mLock);
691 
692     CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING);
693 
694     mAsyncResult = err;
695     mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE;
696     mCondition.broadcast();
697 }
698 
notifyPrepareCompleted(status_t err)699 void NuPlayerDriver::notifyPrepareCompleted(status_t err) {
700     Mutex::Autolock autoLock(mLock);
701 
702     if (mState != STATE_PREPARING) {
703         // We were preparing asynchronously when the client called
704         // reset(), we sent a premature "prepared" notification and
705         // then initiated the reset. This notification is stale.
706         CHECK(mState == STATE_RESET_IN_PROGRESS || mState == STATE_IDLE);
707         return;
708     }
709 
710     CHECK_EQ(mState, STATE_PREPARING);
711 
712     mAsyncResult = err;
713 
714     if (err == OK) {
715         // update state before notifying client, so that if client calls back into NuPlayerDriver
716         // in response, NuPlayerDriver has the right state
717         mState = STATE_PREPARED;
718         if (mIsAsyncPrepare) {
719             notifyListener_l(MEDIA_PREPARED);
720         }
721     } else {
722         mState = STATE_UNPREPARED;
723         if (mIsAsyncPrepare) {
724             notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
725         }
726     }
727 
728     sp<MetaData> meta = mPlayer->getFileMeta();
729     int32_t loop;
730     if (meta != NULL
731             && meta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {
732         mAutoLoop = true;
733     }
734 
735     mCondition.broadcast();
736 }
737 
notifyFlagsChanged(uint32_t flags)738 void NuPlayerDriver::notifyFlagsChanged(uint32_t flags) {
739     Mutex::Autolock autoLock(mLock);
740 
741     mPlayerFlags = flags;
742 }
743 
744 }  // namespace android
745