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