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 #include <cutils/properties.h>
22
23 #include "NuPlayerDriver.h"
24
25 #include "NuPlayer.h"
26 #include "NuPlayerSource.h"
27
28 #include <media/stagefright/foundation/ADebug.h>
29 #include <media/stagefright/foundation/ALooper.h>
30 #include <media/stagefright/foundation/AUtils.h>
31 #include <media/stagefright/foundation/ByteUtils.h>
32 #include <media/stagefright/MediaClock.h>
33 #include <media/stagefright/MetaData.h>
34 #include <media/stagefright/Utils.h>
35
36 #include <media/IMediaAnalyticsService.h>
37
38 static const int kDumpLockRetries = 50;
39 static const int kDumpLockSleepUs = 20000;
40
41 namespace android {
42
43 // key for media statistics
44 static const char *kKeyPlayer = "nuplayer";
45 // attrs for media statistics
46 // NB: these are matched with public Java API constants defined
47 // in frameworks/base/media/java/android/media/MediaPlayer.java
48 // These must be kept synchronized with the constants there.
49 static const char *kPlayerVMime = "android.media.mediaplayer.video.mime";
50 static const char *kPlayerVCodec = "android.media.mediaplayer.video.codec";
51 static const char *kPlayerWidth = "android.media.mediaplayer.width";
52 static const char *kPlayerHeight = "android.media.mediaplayer.height";
53 static const char *kPlayerFrames = "android.media.mediaplayer.frames";
54 static const char *kPlayerFramesDropped = "android.media.mediaplayer.dropped";
55 static const char *kPlayerAMime = "android.media.mediaplayer.audio.mime";
56 static const char *kPlayerACodec = "android.media.mediaplayer.audio.codec";
57 static const char *kPlayerDuration = "android.media.mediaplayer.durationMs";
58 static const char *kPlayerPlaying = "android.media.mediaplayer.playingMs";
59 static const char *kPlayerError = "android.media.mediaplayer.err";
60 static const char *kPlayerErrorCode = "android.media.mediaplayer.errcode";
61
62 // NB: These are not yet exposed as public Java API constants.
63 static const char *kPlayerErrorState = "android.media.mediaplayer.errstate";
64 static const char *kPlayerDataSourceType = "android.media.mediaplayer.dataSource";
65 //
66 static const char *kPlayerRebuffering = "android.media.mediaplayer.rebufferingMs";
67 static const char *kPlayerRebufferingCount = "android.media.mediaplayer.rebuffers";
68 static const char *kPlayerRebufferingAtExit = "android.media.mediaplayer.rebufferExit";
69
70
NuPlayerDriver(pid_t pid)71 NuPlayerDriver::NuPlayerDriver(pid_t pid)
72 : mState(STATE_IDLE),
73 mIsAsyncPrepare(false),
74 mAsyncResult(UNKNOWN_ERROR),
75 mSetSurfaceInProgress(false),
76 mDurationUs(-1),
77 mPositionUs(-1),
78 mSeekInProgress(false),
79 mPlayingTimeUs(0),
80 mRebufferingTimeUs(0),
81 mRebufferingEvents(0),
82 mRebufferingAtExit(false),
83 mLooper(new ALooper),
84 mMediaClock(new MediaClock),
85 mPlayer(new NuPlayer(pid, mMediaClock)),
86 mPlayerFlags(0),
87 mAnalyticsItem(NULL),
88 mClientUid(-1),
89 mAtEOS(false),
90 mLooping(false),
91 mAutoLoop(false) {
92 ALOGD("NuPlayerDriver(%p) created, clientPid(%d)", this, pid);
93 mLooper->setName("NuPlayerDriver Looper");
94
95 mMediaClock->init();
96
97 // set up an analytics record
98 mAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer);
99
100 mLooper->start(
101 false, /* runOnCallingThread */
102 true, /* canCallJava */
103 PRIORITY_AUDIO);
104
105 mLooper->registerHandler(mPlayer);
106
107 mPlayer->init(this);
108 }
109
~NuPlayerDriver()110 NuPlayerDriver::~NuPlayerDriver() {
111 ALOGV("~NuPlayerDriver(%p)", this);
112 mLooper->stop();
113
114 // finalize any pending metrics, usually a no-op.
115 updateMetrics("destructor");
116 logMetrics("destructor");
117
118 Mutex::Autolock autoLock(mMetricsLock);
119 if (mAnalyticsItem != NULL) {
120 delete mAnalyticsItem;
121 mAnalyticsItem = NULL;
122 }
123 }
124
initCheck()125 status_t NuPlayerDriver::initCheck() {
126 return OK;
127 }
128
setUID(uid_t uid)129 status_t NuPlayerDriver::setUID(uid_t uid) {
130 mPlayer->setUID(uid);
131 mClientUid = uid;
132
133 Mutex::Autolock autoLock(mMetricsLock);
134 if (mAnalyticsItem) {
135 mAnalyticsItem->setUid(mClientUid);
136 }
137
138 return OK;
139 }
140
setDataSource(const sp<IMediaHTTPService> & httpService,const char * url,const KeyedVector<String8,String8> * headers)141 status_t NuPlayerDriver::setDataSource(
142 const sp<IMediaHTTPService> &httpService,
143 const char *url,
144 const KeyedVector<String8, String8> *headers) {
145 ALOGV("setDataSource(%p) url(%s)", this, uriDebugString(url, false).c_str());
146 Mutex::Autolock autoLock(mLock);
147
148 if (mState != STATE_IDLE) {
149 return INVALID_OPERATION;
150 }
151
152 mState = STATE_SET_DATASOURCE_PENDING;
153
154 mPlayer->setDataSourceAsync(httpService, url, headers);
155
156 while (mState == STATE_SET_DATASOURCE_PENDING) {
157 mCondition.wait(mLock);
158 }
159
160 return mAsyncResult;
161 }
162
setDataSource(int fd,int64_t offset,int64_t length)163 status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
164 ALOGV("setDataSource(%p) file(%d)", this, fd);
165 Mutex::Autolock autoLock(mLock);
166
167 if (mState != STATE_IDLE) {
168 return INVALID_OPERATION;
169 }
170
171 mState = STATE_SET_DATASOURCE_PENDING;
172
173 mPlayer->setDataSourceAsync(fd, offset, length);
174
175 while (mState == STATE_SET_DATASOURCE_PENDING) {
176 mCondition.wait(mLock);
177 }
178
179 return mAsyncResult;
180 }
181
setDataSource(const sp<IStreamSource> & source)182 status_t NuPlayerDriver::setDataSource(const sp<IStreamSource> &source) {
183 ALOGV("setDataSource(%p) stream source", this);
184 Mutex::Autolock autoLock(mLock);
185
186 if (mState != STATE_IDLE) {
187 return INVALID_OPERATION;
188 }
189
190 mState = STATE_SET_DATASOURCE_PENDING;
191
192 mPlayer->setDataSourceAsync(source);
193
194 while (mState == STATE_SET_DATASOURCE_PENDING) {
195 mCondition.wait(mLock);
196 }
197
198 return mAsyncResult;
199 }
200
setDataSource(const sp<DataSource> & source)201 status_t NuPlayerDriver::setDataSource(const sp<DataSource> &source) {
202 ALOGV("setDataSource(%p) callback source", this);
203 Mutex::Autolock autoLock(mLock);
204
205 if (mState != STATE_IDLE) {
206 return INVALID_OPERATION;
207 }
208
209 mState = STATE_SET_DATASOURCE_PENDING;
210
211 mPlayer->setDataSourceAsync(source);
212
213 while (mState == STATE_SET_DATASOURCE_PENDING) {
214 mCondition.wait(mLock);
215 }
216
217 return mAsyncResult;
218 }
219
setVideoSurfaceTexture(const sp<IGraphicBufferProducer> & bufferProducer)220 status_t NuPlayerDriver::setVideoSurfaceTexture(
221 const sp<IGraphicBufferProducer> &bufferProducer) {
222 ALOGV("setVideoSurfaceTexture(%p)", this);
223 Mutex::Autolock autoLock(mLock);
224
225 if (mSetSurfaceInProgress) {
226 return INVALID_OPERATION;
227 }
228
229 switch (mState) {
230 case STATE_SET_DATASOURCE_PENDING:
231 case STATE_RESET_IN_PROGRESS:
232 return INVALID_OPERATION;
233
234 default:
235 break;
236 }
237
238 mSetSurfaceInProgress = true;
239
240 mPlayer->setVideoSurfaceTextureAsync(bufferProducer);
241
242 while (mSetSurfaceInProgress) {
243 mCondition.wait(mLock);
244 }
245
246 return OK;
247 }
248
getBufferingSettings(BufferingSettings * buffering)249 status_t NuPlayerDriver::getBufferingSettings(BufferingSettings* buffering) {
250 ALOGV("getBufferingSettings(%p)", this);
251 {
252 Mutex::Autolock autoLock(mLock);
253 if (mState == STATE_IDLE) {
254 return INVALID_OPERATION;
255 }
256 }
257
258 return mPlayer->getBufferingSettings(buffering);
259 }
260
setBufferingSettings(const BufferingSettings & buffering)261 status_t NuPlayerDriver::setBufferingSettings(const BufferingSettings& buffering) {
262 ALOGV("setBufferingSettings(%p)", this);
263 {
264 Mutex::Autolock autoLock(mLock);
265 if (mState == STATE_IDLE) {
266 return INVALID_OPERATION;
267 }
268 }
269
270 return mPlayer->setBufferingSettings(buffering);
271 }
272
prepare()273 status_t NuPlayerDriver::prepare() {
274 ALOGV("prepare(%p)", this);
275 Mutex::Autolock autoLock(mLock);
276 return prepare_l();
277 }
278
prepare_l()279 status_t NuPlayerDriver::prepare_l() {
280 switch (mState) {
281 case STATE_UNPREPARED:
282 mState = STATE_PREPARING;
283
284 // Make sure we're not posting any notifications, success or
285 // failure information is only communicated through our result
286 // code.
287 mIsAsyncPrepare = false;
288 mPlayer->prepareAsync();
289 while (mState == STATE_PREPARING) {
290 mCondition.wait(mLock);
291 }
292 return (mState == STATE_PREPARED) ? OK : UNKNOWN_ERROR;
293 case STATE_STOPPED:
294 // this is really just paused. handle as seek to start
295 mAtEOS = false;
296 mState = STATE_STOPPED_AND_PREPARING;
297 mIsAsyncPrepare = false;
298 mPlayer->seekToAsync(0, MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC /* mode */,
299 true /* needNotify */);
300 while (mState == STATE_STOPPED_AND_PREPARING) {
301 mCondition.wait(mLock);
302 }
303 return (mState == STATE_STOPPED_AND_PREPARED) ? OK : UNKNOWN_ERROR;
304 default:
305 return INVALID_OPERATION;
306 };
307 }
308
prepareAsync()309 status_t NuPlayerDriver::prepareAsync() {
310 ALOGV("prepareAsync(%p)", this);
311 Mutex::Autolock autoLock(mLock);
312
313 switch (mState) {
314 case STATE_UNPREPARED:
315 mState = STATE_PREPARING;
316 mIsAsyncPrepare = true;
317 mPlayer->prepareAsync();
318 return OK;
319 case STATE_STOPPED:
320 // this is really just paused. handle as seek to start
321 mAtEOS = false;
322 mState = STATE_STOPPED_AND_PREPARING;
323 mIsAsyncPrepare = true;
324 mPlayer->seekToAsync(0, MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC /* mode */,
325 true /* needNotify */);
326 return OK;
327 default:
328 return INVALID_OPERATION;
329 };
330 }
331
start()332 status_t NuPlayerDriver::start() {
333 ALOGD("start(%p), state is %d, eos is %d", this, mState, mAtEOS);
334 Mutex::Autolock autoLock(mLock);
335 return start_l();
336 }
337
start_l()338 status_t NuPlayerDriver::start_l() {
339 switch (mState) {
340 case STATE_UNPREPARED:
341 {
342 status_t err = prepare_l();
343
344 if (err != OK) {
345 return err;
346 }
347
348 CHECK_EQ(mState, STATE_PREPARED);
349
350 // fall through
351 }
352
353 case STATE_PAUSED:
354 case STATE_STOPPED_AND_PREPARED:
355 case STATE_PREPARED:
356 {
357 mPlayer->start();
358
359 // fall through
360 }
361
362 case STATE_RUNNING:
363 {
364 if (mAtEOS) {
365 mPlayer->seekToAsync(0);
366 mAtEOS = false;
367 mPositionUs = -1;
368 }
369 break;
370 }
371
372 default:
373 return INVALID_OPERATION;
374 }
375
376 mState = STATE_RUNNING;
377
378 return OK;
379 }
380
stop()381 status_t NuPlayerDriver::stop() {
382 ALOGD("stop(%p)", this);
383 Mutex::Autolock autoLock(mLock);
384
385 switch (mState) {
386 case STATE_RUNNING:
387 mPlayer->pause();
388 // fall through
389
390 case STATE_PAUSED:
391 mState = STATE_STOPPED;
392 notifyListener_l(MEDIA_STOPPED);
393 break;
394
395 case STATE_PREPARED:
396 case STATE_STOPPED:
397 case STATE_STOPPED_AND_PREPARING:
398 case STATE_STOPPED_AND_PREPARED:
399 mState = STATE_STOPPED;
400 break;
401
402 default:
403 return INVALID_OPERATION;
404 }
405
406 return OK;
407 }
408
pause()409 status_t NuPlayerDriver::pause() {
410 ALOGD("pause(%p)", this);
411 // The NuPlayerRenderer may get flushed if pause for long enough, e.g. the pause timeout tear
412 // down for audio offload mode. If that happens, the NuPlayerRenderer will no longer know the
413 // current position. So similar to seekTo, update |mPositionUs| to the pause position by calling
414 // getCurrentPosition here.
415 int unused;
416 getCurrentPosition(&unused);
417
418 Mutex::Autolock autoLock(mLock);
419
420 switch (mState) {
421 case STATE_PAUSED:
422 case STATE_PREPARED:
423 return OK;
424
425 case STATE_RUNNING:
426 mState = STATE_PAUSED;
427 notifyListener_l(MEDIA_PAUSED);
428 mPlayer->pause();
429 break;
430
431 default:
432 return INVALID_OPERATION;
433 }
434
435 return OK;
436 }
437
isPlaying()438 bool NuPlayerDriver::isPlaying() {
439 return mState == STATE_RUNNING && !mAtEOS;
440 }
441
setPlaybackSettings(const AudioPlaybackRate & rate)442 status_t NuPlayerDriver::setPlaybackSettings(const AudioPlaybackRate &rate) {
443 status_t err = mPlayer->setPlaybackSettings(rate);
444 if (err == OK) {
445 // try to update position
446 int unused;
447 getCurrentPosition(&unused);
448 Mutex::Autolock autoLock(mLock);
449 if (rate.mSpeed == 0.f && mState == STATE_RUNNING) {
450 mState = STATE_PAUSED;
451 notifyListener_l(MEDIA_PAUSED);
452 } else if (rate.mSpeed != 0.f
453 && (mState == STATE_PAUSED
454 || mState == STATE_STOPPED_AND_PREPARED
455 || mState == STATE_PREPARED)) {
456 err = start_l();
457 }
458 }
459 return err;
460 }
461
getPlaybackSettings(AudioPlaybackRate * rate)462 status_t NuPlayerDriver::getPlaybackSettings(AudioPlaybackRate *rate) {
463 return mPlayer->getPlaybackSettings(rate);
464 }
465
setSyncSettings(const AVSyncSettings & sync,float videoFpsHint)466 status_t NuPlayerDriver::setSyncSettings(const AVSyncSettings &sync, float videoFpsHint) {
467 return mPlayer->setSyncSettings(sync, videoFpsHint);
468 }
469
getSyncSettings(AVSyncSettings * sync,float * videoFps)470 status_t NuPlayerDriver::getSyncSettings(AVSyncSettings *sync, float *videoFps) {
471 return mPlayer->getSyncSettings(sync, videoFps);
472 }
473
seekTo(int msec,MediaPlayerSeekMode mode)474 status_t NuPlayerDriver::seekTo(int msec, MediaPlayerSeekMode mode) {
475 ALOGD("seekTo(%p) (%d ms, %d) at state %d", this, msec, mode, mState);
476 Mutex::Autolock autoLock(mLock);
477
478 int64_t seekTimeUs = msec * 1000ll;
479
480 switch (mState) {
481 case STATE_PREPARED:
482 case STATE_STOPPED_AND_PREPARED:
483 case STATE_PAUSED:
484 case STATE_RUNNING:
485 {
486 mAtEOS = false;
487 mSeekInProgress = true;
488 // seeks can take a while, so we essentially paused
489 notifyListener_l(MEDIA_PAUSED);
490 mPlayer->seekToAsync(seekTimeUs, mode, true /* needNotify */);
491 break;
492 }
493
494 default:
495 return INVALID_OPERATION;
496 }
497
498 mPositionUs = seekTimeUs;
499 return OK;
500 }
501
getCurrentPosition(int * msec)502 status_t NuPlayerDriver::getCurrentPosition(int *msec) {
503 int64_t tempUs = 0;
504 {
505 Mutex::Autolock autoLock(mLock);
506 if (mSeekInProgress || (mState == STATE_PAUSED && !mAtEOS)) {
507 tempUs = (mPositionUs <= 0) ? 0 : mPositionUs;
508 *msec = (int)divRound(tempUs, (int64_t)(1000));
509 return OK;
510 }
511 }
512
513 status_t ret = mPlayer->getCurrentPosition(&tempUs);
514
515 Mutex::Autolock autoLock(mLock);
516 // We need to check mSeekInProgress here because mPlayer->seekToAsync is an async call, which
517 // means getCurrentPosition can be called before seek is completed. Iow, renderer may return a
518 // position value that's different the seek to position.
519 if (ret != OK) {
520 tempUs = (mPositionUs <= 0) ? 0 : mPositionUs;
521 } else {
522 mPositionUs = tempUs;
523 }
524 *msec = (int)divRound(tempUs, (int64_t)(1000));
525 return OK;
526 }
527
getDuration(int * msec)528 status_t NuPlayerDriver::getDuration(int *msec) {
529 Mutex::Autolock autoLock(mLock);
530
531 if (mDurationUs < 0) {
532 return UNKNOWN_ERROR;
533 }
534
535 *msec = (mDurationUs + 500ll) / 1000;
536
537 return OK;
538 }
539
updateMetrics(const char * where)540 void NuPlayerDriver::updateMetrics(const char *where) {
541
542 if (where == NULL) {
543 where = "unknown";
544 }
545 ALOGV("updateMetrics(%p) from %s at state %d", this, where, mState);
546
547 // gather the final track statistics for this record
548 Vector<sp<AMessage>> trackStats;
549 mPlayer->getStats(&trackStats);
550
551 // getDuration() uses mLock
552 int duration_ms = -1;
553 getDuration(&duration_ms);
554
555 mPlayer->updateInternalTimers();
556
557 int64_t playingTimeUs;
558 int64_t rebufferingTimeUs;
559 int32_t rebufferingEvents;
560 bool rebufferingAtExit;
561 {
562 Mutex::Autolock autoLock(mLock);
563
564 playingTimeUs = mPlayingTimeUs;
565 rebufferingTimeUs = mRebufferingTimeUs;
566 rebufferingEvents = mRebufferingEvents;
567 rebufferingAtExit = mRebufferingAtExit;
568 }
569
570 // finish the rest of the gathering holding mLock;
571 // some of the fields we read are updated under mLock.
572 // we also avoid any races within mMetricsItem machinery
573 Mutex::Autolock autoLock(mMetricsLock);
574
575 mAnalyticsItem->setInt64(kPlayerDuration, duration_ms);
576
577 // these update under mLock, we'll read them under mLock
578 mAnalyticsItem->setInt64(kPlayerPlaying, (playingTimeUs+500)/1000 );
579
580 if (rebufferingEvents != 0) {
581 mAnalyticsItem->setInt64(kPlayerRebuffering, (rebufferingTimeUs+500)/1000 );
582 mAnalyticsItem->setInt32(kPlayerRebufferingCount, rebufferingEvents);
583 mAnalyticsItem->setInt32(kPlayerRebufferingAtExit, rebufferingAtExit);
584 }
585
586 mAnalyticsItem->setCString(kPlayerDataSourceType, mPlayer->getDataSourceType());
587
588 if (trackStats.size() > 0) {
589 for (size_t i = 0; i < trackStats.size(); ++i) {
590 const sp<AMessage> &stats = trackStats.itemAt(i);
591
592 AString mime;
593 stats->findString("mime", &mime);
594
595 AString name;
596 stats->findString("component-name", &name);
597
598 if (mime.startsWith("video/")) {
599 int32_t width, height;
600 mAnalyticsItem->setCString(kPlayerVMime, mime.c_str());
601 if (!name.empty()) {
602 mAnalyticsItem->setCString(kPlayerVCodec, name.c_str());
603 }
604
605 if (stats->findInt32("width", &width)
606 && stats->findInt32("height", &height)) {
607 mAnalyticsItem->setInt32(kPlayerWidth, width);
608 mAnalyticsItem->setInt32(kPlayerHeight, height);
609 }
610
611 int64_t numFramesTotal = 0;
612 int64_t numFramesDropped = 0;
613 stats->findInt64("frames-total", &numFramesTotal);
614 stats->findInt64("frames-dropped-output", &numFramesDropped);
615
616 mAnalyticsItem->setInt64(kPlayerFrames, numFramesTotal);
617 mAnalyticsItem->setInt64(kPlayerFramesDropped, numFramesDropped);
618
619
620 } else if (mime.startsWith("audio/")) {
621 mAnalyticsItem->setCString(kPlayerAMime, mime.c_str());
622 if (!name.empty()) {
623 mAnalyticsItem->setCString(kPlayerACodec, name.c_str());
624 }
625 }
626 }
627 }
628
629 }
630
631
logMetrics(const char * where)632 void NuPlayerDriver::logMetrics(const char *where) {
633 if (where == NULL) {
634 where = "unknown";
635 }
636 ALOGV("logMetrics(%p) from %s at state %d", this, where, mState);
637
638 // make sure that the stats are stable while we're writing them.
639 Mutex::Autolock autoLock(mMetricsLock);
640
641 if (mAnalyticsItem == NULL || mAnalyticsItem->isEnabled() == false) {
642 return;
643 }
644
645 // log only non-empty records
646 // we always updateMetrics() before we get here
647 // and that always injects 3 fields (duration, playing time, and
648 // datasource) into the record.
649 // So the canonical "empty" record has 3 elements in it.
650 if (mAnalyticsItem->count() > 3) {
651
652 mAnalyticsItem->selfrecord();
653
654 // re-init in case we prepare() and start() again.
655 delete mAnalyticsItem ;
656 mAnalyticsItem = new MediaAnalyticsItem("nuplayer");
657 if (mAnalyticsItem) {
658 mAnalyticsItem->setUid(mClientUid);
659 }
660 } else {
661 ALOGV("nothing to record (only %d fields)", mAnalyticsItem->count());
662 }
663 }
664
reset()665 status_t NuPlayerDriver::reset() {
666 ALOGD("reset(%p) at state %d", this, mState);
667
668 updateMetrics("reset");
669 logMetrics("reset");
670
671 Mutex::Autolock autoLock(mLock);
672
673 switch (mState) {
674 case STATE_IDLE:
675 return OK;
676
677 case STATE_SET_DATASOURCE_PENDING:
678 case STATE_RESET_IN_PROGRESS:
679 return INVALID_OPERATION;
680
681 case STATE_PREPARING:
682 {
683 CHECK(mIsAsyncPrepare);
684
685 notifyListener_l(MEDIA_PREPARED);
686 break;
687 }
688
689 default:
690 break;
691 }
692
693 if (mState != STATE_STOPPED) {
694 notifyListener_l(MEDIA_STOPPED);
695 }
696
697 if (property_get_bool("persist.debug.sf.stats", false)) {
698 Vector<String16> args;
699 dump(-1, args);
700 }
701
702 mState = STATE_RESET_IN_PROGRESS;
703 mPlayer->resetAsync();
704
705 while (mState == STATE_RESET_IN_PROGRESS) {
706 mCondition.wait(mLock);
707 }
708
709 mDurationUs = -1;
710 mPositionUs = -1;
711 mLooping = false;
712 mPlayingTimeUs = 0;
713 mRebufferingTimeUs = 0;
714 mRebufferingEvents = 0;
715 mRebufferingAtExit = false;
716
717 return OK;
718 }
719
notifyAt(int64_t mediaTimeUs)720 status_t NuPlayerDriver::notifyAt(int64_t mediaTimeUs) {
721 ALOGV("notifyAt(%p), time:%lld", this, (long long)mediaTimeUs);
722 return mPlayer->notifyAt(mediaTimeUs);
723 }
724
setLooping(int loop)725 status_t NuPlayerDriver::setLooping(int loop) {
726 mLooping = loop != 0;
727 return OK;
728 }
729
playerType()730 player_type NuPlayerDriver::playerType() {
731 return NU_PLAYER;
732 }
733
invoke(const Parcel & request,Parcel * reply)734 status_t NuPlayerDriver::invoke(const Parcel &request, Parcel *reply) {
735 if (reply == NULL) {
736 ALOGE("reply is a NULL pointer");
737 return BAD_VALUE;
738 }
739
740 int32_t methodId;
741 status_t ret = request.readInt32(&methodId);
742 if (ret != OK) {
743 ALOGE("Failed to retrieve the requested method to invoke");
744 return ret;
745 }
746
747 switch (methodId) {
748 case INVOKE_ID_SET_VIDEO_SCALING_MODE:
749 {
750 int mode = request.readInt32();
751 return mPlayer->setVideoScalingMode(mode);
752 }
753
754 case INVOKE_ID_GET_TRACK_INFO:
755 {
756 return mPlayer->getTrackInfo(reply);
757 }
758
759 case INVOKE_ID_SELECT_TRACK:
760 {
761 int trackIndex = request.readInt32();
762 int msec = 0;
763 // getCurrentPosition should always return OK
764 getCurrentPosition(&msec);
765 return mPlayer->selectTrack(trackIndex, true /* select */, msec * 1000ll);
766 }
767
768 case INVOKE_ID_UNSELECT_TRACK:
769 {
770 int trackIndex = request.readInt32();
771 return mPlayer->selectTrack(trackIndex, false /* select */, 0xdeadbeef /* not used */);
772 }
773
774 case INVOKE_ID_GET_SELECTED_TRACK:
775 {
776 int32_t type = request.readInt32();
777 return mPlayer->getSelectedTrack(type, reply);
778 }
779
780 default:
781 {
782 return INVALID_OPERATION;
783 }
784 }
785 }
786
setAudioSink(const sp<AudioSink> & audioSink)787 void NuPlayerDriver::setAudioSink(const sp<AudioSink> &audioSink) {
788 mPlayer->setAudioSink(audioSink);
789 mAudioSink = audioSink;
790 }
791
setParameter(int,const Parcel &)792 status_t NuPlayerDriver::setParameter(
793 int /* key */, const Parcel & /* request */) {
794 return INVALID_OPERATION;
795 }
796
getParameter(int key,Parcel * reply)797 status_t NuPlayerDriver::getParameter(int key, Parcel *reply) {
798
799 if (key == FOURCC('m','t','r','X')) {
800 // mtrX -- a play on 'metrics' (not matrix)
801 // gather current info all together, parcel it, and send it back
802 updateMetrics("api");
803
804 // make sure that the stats are static while we're writing to the parcel
805 Mutex::Autolock autoLock(mMetricsLock);
806 mAnalyticsItem->writeToParcel(reply);
807 return OK;
808 }
809
810 return INVALID_OPERATION;
811 }
812
getMetadata(const media::Metadata::Filter &,Parcel * records)813 status_t NuPlayerDriver::getMetadata(
814 const media::Metadata::Filter& /* ids */, Parcel *records) {
815 Mutex::Autolock autoLock(mLock);
816
817 using media::Metadata;
818
819 Metadata meta(records);
820
821 meta.appendBool(
822 Metadata::kPauseAvailable,
823 mPlayerFlags & NuPlayer::Source::FLAG_CAN_PAUSE);
824
825 meta.appendBool(
826 Metadata::kSeekBackwardAvailable,
827 mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_BACKWARD);
828
829 meta.appendBool(
830 Metadata::kSeekForwardAvailable,
831 mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_FORWARD);
832
833 meta.appendBool(
834 Metadata::kSeekAvailable,
835 mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK);
836
837 return OK;
838 }
839
notifyResetComplete()840 void NuPlayerDriver::notifyResetComplete() {
841 ALOGD("notifyResetComplete(%p)", this);
842 Mutex::Autolock autoLock(mLock);
843
844 CHECK_EQ(mState, STATE_RESET_IN_PROGRESS);
845 mState = STATE_IDLE;
846 mCondition.broadcast();
847 }
848
notifySetSurfaceComplete()849 void NuPlayerDriver::notifySetSurfaceComplete() {
850 ALOGV("notifySetSurfaceComplete(%p)", this);
851 Mutex::Autolock autoLock(mLock);
852
853 CHECK(mSetSurfaceInProgress);
854 mSetSurfaceInProgress = false;
855
856 mCondition.broadcast();
857 }
858
notifyDuration(int64_t durationUs)859 void NuPlayerDriver::notifyDuration(int64_t durationUs) {
860 Mutex::Autolock autoLock(mLock);
861 mDurationUs = durationUs;
862 }
863
notifyMorePlayingTimeUs(int64_t playingUs)864 void NuPlayerDriver::notifyMorePlayingTimeUs(int64_t playingUs) {
865 Mutex::Autolock autoLock(mLock);
866 mPlayingTimeUs += playingUs;
867 }
868
notifyMoreRebufferingTimeUs(int64_t rebufferingUs)869 void NuPlayerDriver::notifyMoreRebufferingTimeUs(int64_t rebufferingUs) {
870 Mutex::Autolock autoLock(mLock);
871 mRebufferingTimeUs += rebufferingUs;
872 mRebufferingEvents++;
873 }
874
notifyRebufferingWhenExit(bool status)875 void NuPlayerDriver::notifyRebufferingWhenExit(bool status) {
876 Mutex::Autolock autoLock(mLock);
877 mRebufferingAtExit = status;
878 }
879
notifySeekComplete()880 void NuPlayerDriver::notifySeekComplete() {
881 ALOGV("notifySeekComplete(%p)", this);
882 Mutex::Autolock autoLock(mLock);
883 mSeekInProgress = false;
884 notifySeekComplete_l();
885 }
886
notifySeekComplete_l()887 void NuPlayerDriver::notifySeekComplete_l() {
888 bool wasSeeking = true;
889 if (mState == STATE_STOPPED_AND_PREPARING) {
890 wasSeeking = false;
891 mState = STATE_STOPPED_AND_PREPARED;
892 mCondition.broadcast();
893 if (!mIsAsyncPrepare) {
894 // if we are preparing synchronously, no need to notify listener
895 return;
896 }
897 } else if (mState == STATE_STOPPED) {
898 // no need to notify listener
899 return;
900 }
901 notifyListener_l(wasSeeking ? MEDIA_SEEK_COMPLETE : MEDIA_PREPARED);
902 }
903
dump(int fd,const Vector<String16> &) const904 status_t NuPlayerDriver::dump(
905 int fd, const Vector<String16> & /* args */) const {
906
907 Vector<sp<AMessage> > trackStats;
908 mPlayer->getStats(&trackStats);
909
910 AString logString(" NuPlayer\n");
911 char buf[256] = {0};
912
913 bool locked = false;
914 for (int i = 0; i < kDumpLockRetries; ++i) {
915 if (mLock.tryLock() == NO_ERROR) {
916 locked = true;
917 break;
918 }
919 usleep(kDumpLockSleepUs);
920 }
921
922 if (locked) {
923 snprintf(buf, sizeof(buf), " state(%d), atEOS(%d), looping(%d), autoLoop(%d)\n",
924 mState, mAtEOS, mLooping, mAutoLoop);
925 mLock.unlock();
926 } else {
927 snprintf(buf, sizeof(buf), " NPD(%p) lock is taken\n", this);
928 }
929 logString.append(buf);
930
931 for (size_t i = 0; i < trackStats.size(); ++i) {
932 const sp<AMessage> &stats = trackStats.itemAt(i);
933
934 AString mime;
935 if (stats->findString("mime", &mime)) {
936 snprintf(buf, sizeof(buf), " mime(%s)\n", mime.c_str());
937 logString.append(buf);
938 }
939
940 AString name;
941 if (stats->findString("component-name", &name)) {
942 snprintf(buf, sizeof(buf), " decoder(%s)\n", name.c_str());
943 logString.append(buf);
944 }
945
946 if (mime.startsWith("video/")) {
947 int32_t width, height;
948 if (stats->findInt32("width", &width)
949 && stats->findInt32("height", &height)) {
950 snprintf(buf, sizeof(buf), " resolution(%d x %d)\n", width, height);
951 logString.append(buf);
952 }
953
954 int64_t numFramesTotal = 0;
955 int64_t numFramesDropped = 0;
956
957 stats->findInt64("frames-total", &numFramesTotal);
958 stats->findInt64("frames-dropped-output", &numFramesDropped);
959 snprintf(buf, sizeof(buf), " numFramesTotal(%lld), numFramesDropped(%lld), "
960 "percentageDropped(%.2f%%)\n",
961 (long long)numFramesTotal,
962 (long long)numFramesDropped,
963 numFramesTotal == 0
964 ? 0.0 : (double)(numFramesDropped * 100) / numFramesTotal);
965 logString.append(buf);
966 }
967 }
968
969 ALOGI("%s", logString.c_str());
970
971 if (fd >= 0) {
972 FILE *out = fdopen(dup(fd), "w");
973 fprintf(out, "%s", logString.c_str());
974 fclose(out);
975 out = NULL;
976 }
977
978 return OK;
979 }
980
notifyListener(int msg,int ext1,int ext2,const Parcel * in)981 void NuPlayerDriver::notifyListener(
982 int msg, int ext1, int ext2, const Parcel *in) {
983 Mutex::Autolock autoLock(mLock);
984 notifyListener_l(msg, ext1, ext2, in);
985 }
986
notifyListener_l(int msg,int ext1,int ext2,const Parcel * in)987 void NuPlayerDriver::notifyListener_l(
988 int msg, int ext1, int ext2, const Parcel *in) {
989 ALOGD("notifyListener_l(%p), (%d, %d, %d, %d), loop setting(%d, %d)",
990 this, msg, ext1, ext2, (in == NULL ? -1 : (int)in->dataSize()), mAutoLoop, mLooping);
991 switch (msg) {
992 case MEDIA_PLAYBACK_COMPLETE:
993 {
994 if (mState != STATE_RESET_IN_PROGRESS) {
995 if (mAutoLoop) {
996 audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
997 if (mAudioSink != NULL) {
998 streamType = mAudioSink->getAudioStreamType();
999 }
1000 if (streamType == AUDIO_STREAM_NOTIFICATION) {
1001 ALOGW("disabling auto-loop for notification");
1002 mAutoLoop = false;
1003 }
1004 }
1005 if (mLooping || mAutoLoop) {
1006 mPlayer->seekToAsync(0);
1007 if (mAudioSink != NULL) {
1008 // The renderer has stopped the sink at the end in order to play out
1009 // the last little bit of audio. If we're looping, we need to restart it.
1010 mAudioSink->start();
1011 }
1012 // don't send completion event when looping
1013 return;
1014 }
1015 if (property_get_bool("persist.debug.sf.stats", false)) {
1016 Vector<String16> args;
1017 dump(-1, args);
1018 }
1019 mPlayer->pause();
1020 mState = STATE_PAUSED;
1021 }
1022 // fall through
1023 }
1024
1025 case MEDIA_ERROR:
1026 {
1027 // when we have an error, add it to the analytics for this playback.
1028 // ext1 is our primary 'error type' value. Only add ext2 when non-zero.
1029 // [test against msg is due to fall through from previous switch value]
1030 if (msg == MEDIA_ERROR) {
1031 Mutex::Autolock autoLock(mMetricsLock);
1032 if (mAnalyticsItem != NULL) {
1033 mAnalyticsItem->setInt32(kPlayerError, ext1);
1034 if (ext2 != 0) {
1035 mAnalyticsItem->setInt32(kPlayerErrorCode, ext2);
1036 }
1037 mAnalyticsItem->setCString(kPlayerErrorState, stateString(mState).c_str());
1038 }
1039 }
1040 mAtEOS = true;
1041 break;
1042 }
1043
1044 default:
1045 break;
1046 }
1047
1048 mLock.unlock();
1049 sendEvent(msg, ext1, ext2, in);
1050 mLock.lock();
1051 }
1052
notifySetDataSourceCompleted(status_t err)1053 void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) {
1054 Mutex::Autolock autoLock(mLock);
1055
1056 CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING);
1057
1058 mAsyncResult = err;
1059 mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE;
1060 mCondition.broadcast();
1061 }
1062
notifyPrepareCompleted(status_t err)1063 void NuPlayerDriver::notifyPrepareCompleted(status_t err) {
1064 ALOGV("notifyPrepareCompleted %d", err);
1065
1066 Mutex::Autolock autoLock(mLock);
1067
1068 if (mState != STATE_PREPARING) {
1069 // We were preparing asynchronously when the client called
1070 // reset(), we sent a premature "prepared" notification and
1071 // then initiated the reset. This notification is stale.
1072 CHECK(mState == STATE_RESET_IN_PROGRESS || mState == STATE_IDLE);
1073 return;
1074 }
1075
1076 CHECK_EQ(mState, STATE_PREPARING);
1077
1078 mAsyncResult = err;
1079
1080 if (err == OK) {
1081 // update state before notifying client, so that if client calls back into NuPlayerDriver
1082 // in response, NuPlayerDriver has the right state
1083 mState = STATE_PREPARED;
1084 if (mIsAsyncPrepare) {
1085 notifyListener_l(MEDIA_PREPARED);
1086 }
1087 } else {
1088 mState = STATE_UNPREPARED;
1089 if (mIsAsyncPrepare) {
1090 notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
1091 }
1092 }
1093
1094 sp<MetaData> meta = mPlayer->getFileMeta();
1095 int32_t loop;
1096 if (meta != NULL
1097 && meta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {
1098 mAutoLoop = true;
1099 }
1100
1101 mCondition.broadcast();
1102 }
1103
notifyFlagsChanged(uint32_t flags)1104 void NuPlayerDriver::notifyFlagsChanged(uint32_t flags) {
1105 Mutex::Autolock autoLock(mLock);
1106
1107 mPlayerFlags = flags;
1108 }
1109
1110 // Modular DRM
prepareDrm(const uint8_t uuid[16],const Vector<uint8_t> & drmSessionId)1111 status_t NuPlayerDriver::prepareDrm(const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId)
1112 {
1113 ALOGV("prepareDrm(%p) state: %d", this, mState);
1114
1115 // leaving the state verification for mediaplayer.cpp
1116 status_t ret = mPlayer->prepareDrm(uuid, drmSessionId);
1117
1118 ALOGV("prepareDrm ret: %d", ret);
1119
1120 return ret;
1121 }
1122
releaseDrm()1123 status_t NuPlayerDriver::releaseDrm()
1124 {
1125 ALOGV("releaseDrm(%p) state: %d", this, mState);
1126
1127 // leaving the state verification for mediaplayer.cpp
1128 status_t ret = mPlayer->releaseDrm();
1129
1130 ALOGV("releaseDrm ret: %d", ret);
1131
1132 return ret;
1133 }
1134
stateString(State state)1135 std::string NuPlayerDriver::stateString(State state) {
1136 const char *rval = NULL;
1137 char rawbuffer[16]; // allows "%d"
1138
1139 switch (state) {
1140 case STATE_IDLE: rval = "IDLE"; break;
1141 case STATE_SET_DATASOURCE_PENDING: rval = "SET_DATASOURCE_PENDING"; break;
1142 case STATE_UNPREPARED: rval = "UNPREPARED"; break;
1143 case STATE_PREPARING: rval = "PREPARING"; break;
1144 case STATE_PREPARED: rval = "PREPARED"; break;
1145 case STATE_RUNNING: rval = "RUNNING"; break;
1146 case STATE_PAUSED: rval = "PAUSED"; break;
1147 case STATE_RESET_IN_PROGRESS: rval = "RESET_IN_PROGRESS"; break;
1148 case STATE_STOPPED: rval = "STOPPED"; break;
1149 case STATE_STOPPED_AND_PREPARING: rval = "STOPPED_AND_PREPARING"; break;
1150 case STATE_STOPPED_AND_PREPARED: rval = "STOPPED_AND_PREPARED"; break;
1151 default:
1152 // yes, this buffer is shared and vulnerable to races
1153 snprintf(rawbuffer, sizeof(rawbuffer), "%d", state);
1154 rval = rawbuffer;
1155 break;
1156 }
1157
1158 return rval;
1159 }
1160
1161 } // namespace android
1162