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