| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "sles_allinclusive.h" |
| #include "utils/RefBase.h" |
| #include "android_prompts.h" |
| // LocAVPlayer and StreamPlayer derive from GenericMediaPlayer, |
| // so no need to #include "android_GenericMediaPlayer.h" |
| #include "android_LocAVPlayer.h" |
| #include "android_StreamPlayer.h" |
| |
| |
| //----------------------------------------------------------------------------- |
| static void player_handleMediaPlayerEventNotifications(int event, int data1, int data2, void* user) |
| { |
| |
| // FIXME This code is derived from similar code in sfplayer_handlePrefetchEvent. The two |
| // versions are quite similar, but still different enough that they need to be separate. |
| // At some point they should be re-factored and merged if feasible. |
| // As with other OpenMAX AL implementation code, this copy mostly uses SL_ symbols |
| // rather than XA_ unless the difference is significant. |
| |
| if (NULL == user) { |
| return; |
| } |
| |
| CMediaPlayer* mp = (CMediaPlayer*) user; |
| if (!android::CallbackProtector::enterCbIfOk(mp->mCallbackProtector)) { |
| // it is not safe to enter the callback (the media player is about to go away) |
| return; |
| } |
| union { |
| char c[sizeof(int)]; |
| int i; |
| } u; |
| u.i = event; |
| SL_LOGV("player_handleMediaPlayerEventNotifications(event='%c%c%c%c' (%d), data1=%d, data2=%d, " |
| "user=%p) from AVPlayer", u.c[3], u.c[2], u.c[1], u.c[0], event, data1, data2, user); |
| switch(event) { |
| |
| case android::GenericPlayer::kEventPrepared: { |
| SL_LOGV("Received GenericPlayer::kEventPrepared for CMediaPlayer %p", mp); |
| |
| // assume no callback |
| slPrefetchCallback callback = NULL; |
| void* callbackPContext; |
| XAuint32 events; |
| |
| object_lock_exclusive(&mp->mObject); |
| |
| // mark object as prepared; same state is used for successful or unsuccessful prepare |
| assert(mp->mAndroidObjState == ANDROID_PREPARING); |
| mp->mAndroidObjState = ANDROID_READY; |
| |
| if (PLAYER_SUCCESS == data1) { |
| // Most of successful prepare completion for mp->mAVPlayer |
| // is handled by GenericPlayer and its subclasses. |
| } else { |
| // AVPlayer prepare() failed prefetching, there is no event in XAPrefetchStatus to |
| // indicate a prefetch error, so we signal it by sending simultaneously two events: |
| // - SL_PREFETCHEVENT_FILLLEVELCHANGE with a level of 0 |
| // - SL_PREFETCHEVENT_STATUSCHANGE with a status of SL_PREFETCHSTATUS_UNDERFLOW |
| SL_LOGE(ERROR_PLAYER_PREFETCH_d, data1); |
| if (IsInterfaceInitialized(&mp->mObject, MPH_XAPREFETCHSTATUS)) { |
| mp->mPrefetchStatus.mLevel = 0; |
| mp->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW; |
| if (!(~mp->mPrefetchStatus.mCallbackEventsMask & |
| (SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE))) { |
| callback = mp->mPrefetchStatus.mCallback; |
| callbackPContext = mp->mPrefetchStatus.mContext; |
| events = SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE; |
| } |
| } |
| } |
| |
| object_unlock_exclusive(&mp->mObject); |
| |
| // callback with no lock held |
| if (NULL != callback) { |
| (*callback)(&mp->mPrefetchStatus.mItf, callbackPContext, events); |
| } |
| |
| break; |
| } |
| |
| case android::GenericPlayer::kEventHasVideoSize: { |
| SL_LOGV("Received AVPlayer::kEventHasVideoSize (%d,%d) for CMediaPlayer %p", |
| data1, data2, mp); |
| |
| object_lock_exclusive(&mp->mObject); |
| |
| // remove an existing video info entry (here we only have one video stream) |
| for(size_t i=0 ; i < mp->mStreamInfo.mStreamInfoTable.size() ; i++) { |
| if (XA_DOMAINTYPE_VIDEO == mp->mStreamInfo.mStreamInfoTable.itemAt(i).domain) { |
| mp->mStreamInfo.mStreamInfoTable.removeAt(i); |
| break; |
| } |
| } |
| // update the stream information with a new video info entry |
| StreamInfo streamInfo; |
| streamInfo.domain = XA_DOMAINTYPE_VIDEO; |
| streamInfo.videoInfo.codecId = 0;// unknown, we don't have that info FIXME |
| streamInfo.videoInfo.width = (XAuint32)data1; |
| streamInfo.videoInfo.height = (XAuint32)data2; |
| streamInfo.videoInfo.bitRate = 0;// unknown, we don't have that info FIXME |
| streamInfo.videoInfo.frameRate = 0; |
| streamInfo.videoInfo.duration = XA_TIME_UNKNOWN; |
| StreamInfo &contInfo = mp->mStreamInfo.mStreamInfoTable.editItemAt(0); |
| contInfo.containerInfo.numStreams = 1; |
| ssize_t index = mp->mStreamInfo.mStreamInfoTable.add(streamInfo); |
| |
| // callback is unconditional; there is no bitmask of enabled events |
| xaStreamEventChangeCallback callback = mp->mStreamInfo.mCallback; |
| void* callbackPContext = mp->mStreamInfo.mContext; |
| |
| object_unlock_exclusive(&mp->mObject); |
| |
| // enqueue notification (outside of lock) that the stream information has been updated |
| if ((NULL != callback) && (index >= 0)) { |
| #ifndef USE_ASYNCHRONOUS_STREAMCBEVENT_PROPERTYCHANGE_CALLBACK |
| (*callback)(&mp->mStreamInfo.mItf, XA_STREAMCBEVENT_PROPERTYCHANGE /*eventId*/, |
| 1 /*streamIndex, only one stream supported here, 0 is reserved*/, |
| NULL /*pEventData, always NULL in OpenMAX AL 1.0.1*/, |
| callbackPContext /*pContext*/); |
| #else |
| SLresult res = EnqueueAsyncCallback_piipp(mp, callback, |
| /*p1*/ &mp->mStreamInfo.mItf, |
| /*i1*/ XA_STREAMCBEVENT_PROPERTYCHANGE /*eventId*/, |
| /*i2*/ 1 /*streamIndex, only one stream supported here, 0 is reserved*/, |
| /*p2*/ NULL /*pEventData, always NULL in OpenMAX AL 1.0.1*/, |
| /*p3*/ callbackPContext /*pContext*/); |
| LOGW_IF(SL_RESULT_SUCCESS != res, |
| "Callback %p(%p, XA_STREAMCBEVENT_PROPERTYCHANGE, 1, NULL, %p) dropped", |
| callback, &mp->mStreamInfo.mItf, callbackPContext); |
| #endif |
| } |
| break; |
| } |
| |
| case android::GenericPlayer::kEventEndOfStream: { |
| SL_LOGV("Received AVPlayer::kEventEndOfStream for CMediaPlayer %p", mp); |
| |
| object_lock_exclusive(&mp->mObject); |
| // should be xaPlayCallback but we're sharing the itf between SL and AL |
| slPlayCallback playCallback = NULL; |
| void * playContext = NULL; |
| // XAPlayItf callback or no callback? |
| if (mp->mPlay.mEventFlags & XA_PLAYEVENT_HEADATEND) { |
| playCallback = mp->mPlay.mCallback; |
| playContext = mp->mPlay.mContext; |
| } |
| mp->mPlay.mState = XA_PLAYSTATE_PAUSED; |
| object_unlock_exclusive(&mp->mObject); |
| |
| // enqueue callback with no lock held |
| if (NULL != playCallback) { |
| #ifndef USE_ASYNCHRONOUS_PLAY_CALLBACK |
| (*playCallback)(&mp->mPlay.mItf, playContext, XA_PLAYEVENT_HEADATEND); |
| #else |
| SLresult res = EnqueueAsyncCallback_ppi(mp, playCallback, &mp->mPlay.mItf, playContext, |
| XA_PLAYEVENT_HEADATEND); |
| LOGW_IF(SL_RESULT_SUCCESS != res, |
| "Callback %p(%p, %p, SL_PLAYEVENT_HEADATEND) dropped", playCallback, |
| &mp->mPlay.mItf, playContext); |
| #endif |
| } |
| break; |
| } |
| |
| case android::GenericPlayer::kEventChannelCount: { |
| SL_LOGV("kEventChannelCount channels = %d", data1); |
| object_lock_exclusive(&mp->mObject); |
| if (UNKNOWN_NUMCHANNELS == mp->mNumChannels && UNKNOWN_NUMCHANNELS != data1) { |
| mp->mNumChannels = data1; |
| android_Player_volumeUpdate(mp); |
| } |
| object_unlock_exclusive(&mp->mObject); |
| } |
| break; |
| |
| case android::GenericPlayer::kEventPrefetchFillLevelUpdate: { |
| SL_LOGV("kEventPrefetchFillLevelUpdate"); |
| if (!IsInterfaceInitialized(&mp->mObject, MPH_XAPREFETCHSTATUS)) { |
| break; |
| } |
| slPrefetchCallback callback = NULL; |
| void* callbackPContext = NULL; |
| |
| // SLPrefetchStatusItf callback or no callback? |
| interface_lock_exclusive(&mp->mPrefetchStatus); |
| if (mp->mPrefetchStatus.mCallbackEventsMask & SL_PREFETCHEVENT_FILLLEVELCHANGE) { |
| callback = mp->mPrefetchStatus.mCallback; |
| callbackPContext = mp->mPrefetchStatus.mContext; |
| } |
| mp->mPrefetchStatus.mLevel = (SLpermille)data1; |
| interface_unlock_exclusive(&mp->mPrefetchStatus); |
| |
| // callback with no lock held |
| if (NULL != callback) { |
| (*callback)(&mp->mPrefetchStatus.mItf, callbackPContext, |
| SL_PREFETCHEVENT_FILLLEVELCHANGE); |
| } |
| } |
| break; |
| |
| case android::GenericPlayer::kEventPrefetchStatusChange: { |
| SL_LOGV("kEventPrefetchStatusChange"); |
| if (!IsInterfaceInitialized(&mp->mObject, MPH_XAPREFETCHSTATUS)) { |
| break; |
| } |
| slPrefetchCallback callback = NULL; |
| void* callbackPContext = NULL; |
| |
| // SLPrefetchStatusItf callback or no callback? |
| object_lock_exclusive(&mp->mObject); |
| if (mp->mPrefetchStatus.mCallbackEventsMask & SL_PREFETCHEVENT_STATUSCHANGE) { |
| callback = mp->mPrefetchStatus.mCallback; |
| callbackPContext = mp->mPrefetchStatus.mContext; |
| } |
| if (data1 >= android::kStatusIntermediate) { |
| mp->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_SUFFICIENTDATA; |
| } else if (data1 < android::kStatusIntermediate) { |
| mp->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW; |
| } |
| object_unlock_exclusive(&mp->mObject); |
| |
| // callback with no lock held |
| if (NULL != callback) { |
| (*callback)(&mp->mPrefetchStatus.mItf, callbackPContext, SL_PREFETCHEVENT_STATUSCHANGE); |
| } |
| } |
| break; |
| |
| case android::GenericPlayer::kEventPlay: { |
| SL_LOGV("kEventPlay"); |
| |
| interface_lock_shared(&mp->mPlay); |
| slPlayCallback callback = mp->mPlay.mCallback; |
| void* callbackPContext = mp->mPlay.mContext; |
| interface_unlock_shared(&mp->mPlay); |
| |
| if (NULL != callback) { |
| (*callback)(&mp->mPlay.mItf, callbackPContext, (SLuint32) data1); // SL_PLAYEVENT_HEAD* |
| } |
| } |
| break; |
| |
| case android::GenericPlayer::kEventErrorAfterPrepare: { |
| SL_LOGV("kEventErrorAfterPrepare"); |
| |
| // assume no callback |
| slPrefetchCallback callback = NULL; |
| void* callbackPContext = NULL; |
| |
| object_lock_exclusive(&mp->mObject); |
| if (IsInterfaceInitialized(&mp->mObject, MPH_XAPREFETCHSTATUS)) { |
| mp->mPrefetchStatus.mLevel = 0; |
| mp->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW; |
| if (!(~mp->mPrefetchStatus.mCallbackEventsMask & |
| (SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE))) { |
| callback = mp->mPrefetchStatus.mCallback; |
| callbackPContext = mp->mPrefetchStatus.mContext; |
| } |
| } |
| object_unlock_exclusive(&mp->mObject); |
| |
| // FIXME there's interesting information in data1, but no API to convey it to client |
| SL_LOGE("Error after prepare: %d", data1); |
| |
| // callback with no lock held |
| if (NULL != callback) { |
| (*callback)(&mp->mPrefetchStatus.mItf, callbackPContext, |
| SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE); |
| } |
| |
| } |
| break; |
| |
| default: { |
| SL_LOGE("Received unknown event %d, data %d from AVPlayer", event, data1); |
| } |
| } |
| |
| mp->mCallbackProtector->exitCb(); |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| XAresult android_Player_checkSourceSink(CMediaPlayer *mp) { |
| |
| XAresult result = XA_RESULT_SUCCESS; |
| |
| const SLDataSource *pSrc = &mp->mDataSource.u.mSource; |
| const SLDataSink *pAudioSnk = &mp->mAudioSink.u.mSink; |
| |
| // format check: |
| const SLuint32 sourceLocatorType = *(SLuint32 *)pSrc->pLocator; |
| const SLuint32 sourceFormatType = *(SLuint32 *)pSrc->pFormat; |
| const SLuint32 audioSinkLocatorType = *(SLuint32 *)pAudioSnk->pLocator; |
| //const SLuint32 sinkFormatType = *(SLuint32 *)pAudioSnk->pFormat; |
| |
| // Source check |
| switch(sourceLocatorType) { |
| |
| case XA_DATALOCATOR_ANDROIDBUFFERQUEUE: { |
| switch (sourceFormatType) { |
| case XA_DATAFORMAT_MIME: { |
| SLDataFormat_MIME *df_mime = (SLDataFormat_MIME *) pSrc->pFormat; |
| if (SL_CONTAINERTYPE_MPEG_TS != df_mime->containerType) { |
| SL_LOGE("Cannot create player with XA_DATALOCATOR_ANDROIDBUFFERQUEUE data source " |
| "that is not fed MPEG-2 TS data"); |
| return SL_RESULT_CONTENT_UNSUPPORTED; |
| } |
| } break; |
| default: |
| SL_LOGE("Cannot create player with XA_DATALOCATOR_ANDROIDBUFFERQUEUE data source " |
| "without SL_DATAFORMAT_MIME format"); |
| return XA_RESULT_CONTENT_UNSUPPORTED; |
| } |
| } break; |
| |
| case XA_DATALOCATOR_URI: // intended fall-through |
| case XA_DATALOCATOR_ANDROIDFD: |
| break; |
| |
| default: |
| SL_LOGE("Cannot create media player with data locator type 0x%x", |
| (unsigned) sourceLocatorType); |
| return SL_RESULT_PARAMETER_INVALID; |
| }// switch (locatorType) |
| |
| // Audio sink check: only playback is supported here |
| switch(audioSinkLocatorType) { |
| |
| case XA_DATALOCATOR_OUTPUTMIX: |
| break; |
| |
| default: |
| SL_LOGE("Cannot create media player with audio sink data locator of type 0x%x", |
| (unsigned) audioSinkLocatorType); |
| return XA_RESULT_PARAMETER_INVALID; |
| }// switch (locaaudioSinkLocatorTypeorType) |
| |
| return result; |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| XAresult android_Player_create(CMediaPlayer *mp) { |
| |
| XAresult result = XA_RESULT_SUCCESS; |
| |
| // FIXME verify data source |
| const SLDataSource *pDataSrc = &mp->mDataSource.u.mSource; |
| // FIXME verify audio data sink |
| const SLDataSink *pAudioSnk = &mp->mAudioSink.u.mSink; |
| // FIXME verify image data sink |
| const SLDataSink *pVideoSnk = &mp->mImageVideoSink.u.mSink; |
| |
| XAuint32 sourceLocator = *(XAuint32 *)pDataSrc->pLocator; |
| switch(sourceLocator) { |
| // FIXME support Android simple buffer queue as well |
| case XA_DATALOCATOR_ANDROIDBUFFERQUEUE: |
| mp->mAndroidObjType = AUDIOVIDEOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE; |
| break; |
| case XA_DATALOCATOR_URI: // intended fall-through |
| case SL_DATALOCATOR_ANDROIDFD: |
| mp->mAndroidObjType = AUDIOVIDEOPLAYER_FROM_URIFD; |
| break; |
| case XA_DATALOCATOR_ADDRESS: // intended fall-through |
| default: |
| mp->mAndroidObjType = INVALID_TYPE; |
| SL_LOGE("Unable to create MediaPlayer for data source locator 0x%x", sourceLocator); |
| result = XA_RESULT_PARAMETER_INVALID; |
| break; |
| } |
| |
| // FIXME duplicates an initialization also done by higher level |
| mp->mAndroidObjState = ANDROID_UNINITIALIZED; |
| mp->mStreamType = ANDROID_DEFAULT_OUTPUT_STREAM_TYPE; |
| mp->mSessionId = android::AudioSystem::newAudioSessionId(); |
| |
| // placeholder: not necessary yet as session ID lifetime doesn't extend beyond player |
| // android::AudioSystem::acquireAudioSessionId(mp->mSessionId); |
| |
| mp->mCallbackProtector = new android::CallbackProtector(); |
| |
| return result; |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // FIXME abstract out the diff between CMediaPlayer and CAudioPlayer |
| XAresult android_Player_realize(CMediaPlayer *mp, SLboolean async) { |
| SL_LOGV("android_Player_realize_l(%p)", mp); |
| XAresult result = XA_RESULT_SUCCESS; |
| |
| const SLDataSource *pDataSrc = &mp->mDataSource.u.mSource; |
| const SLuint32 sourceLocator = *(SLuint32 *)pDataSrc->pLocator; |
| |
| AudioPlayback_Parameters ap_params; |
| ap_params.sessionId = mp->mSessionId; |
| ap_params.streamType = mp->mStreamType; |
| |
| switch(mp->mAndroidObjType) { |
| case AUDIOVIDEOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: { |
| mp->mAVPlayer = new android::StreamPlayer(&ap_params, true /*hasVideo*/, |
| &mp->mAndroidBufferQueue, mp->mCallbackProtector); |
| mp->mAVPlayer->init(player_handleMediaPlayerEventNotifications, (void*)mp); |
| } |
| break; |
| case AUDIOVIDEOPLAYER_FROM_URIFD: { |
| mp->mAVPlayer = new android::LocAVPlayer(&ap_params, true /*hasVideo*/); |
| mp->mAVPlayer->init(player_handleMediaPlayerEventNotifications, (void*)mp); |
| switch (mp->mDataSource.mLocator.mLocatorType) { |
| case XA_DATALOCATOR_URI: |
| ((android::LocAVPlayer*)mp->mAVPlayer.get())->setDataSource( |
| (const char*)mp->mDataSource.mLocator.mURI.URI); |
| break; |
| case XA_DATALOCATOR_ANDROIDFD: { |
| int64_t offset = (int64_t)mp->mDataSource.mLocator.mFD.offset; |
| ((android::LocAVPlayer*)mp->mAVPlayer.get())->setDataSource( |
| (int)mp->mDataSource.mLocator.mFD.fd, |
| offset == SL_DATALOCATOR_ANDROIDFD_USE_FILE_SIZE ? |
| (int64_t)PLAYER_FD_FIND_FILE_SIZE : offset, |
| (int64_t)mp->mDataSource.mLocator.mFD.length); |
| } |
| break; |
| default: |
| SL_LOGE("Invalid or unsupported data locator type %u for data source", |
| mp->mDataSource.mLocator.mLocatorType); |
| result = XA_RESULT_PARAMETER_INVALID; |
| } |
| } |
| break; |
| case INVALID_TYPE: // intended fall-through |
| default: |
| SL_LOGE("Unable to realize MediaPlayer, invalid internal Android object type"); |
| result = XA_RESULT_PARAMETER_INVALID; |
| break; |
| } |
| |
| if (XA_RESULT_SUCCESS == result) { |
| |
| // if there is a video sink |
| if (XA_DATALOCATOR_NATIVEDISPLAY == |
| mp->mImageVideoSink.mLocator.mLocatorType) { |
| ANativeWindow *nativeWindow = (ANativeWindow *) |
| mp->mImageVideoSink.mLocator.mNativeDisplay.hWindow; |
| // we already verified earlier that hWindow is non-NULL |
| assert(nativeWindow != NULL); |
| result = android_Player_setNativeWindow(mp, nativeWindow); |
| } |
| |
| } |
| |
| return result; |
| } |
| |
| // Called with a lock on MediaPlayer, and blocks until safe to destroy |
| XAresult android_Player_preDestroy(CMediaPlayer *mp) { |
| SL_LOGV("android_Player_preDestroy(%p)", mp); |
| |
| // Not yet clear why this order is important, but it reduces detected deadlocks |
| object_unlock_exclusive(&mp->mObject); |
| if (mp->mCallbackProtector != 0) { |
| mp->mCallbackProtector->requestCbExitAndWait(); |
| } |
| object_lock_exclusive(&mp->mObject); |
| |
| if (mp->mAVPlayer != 0) { |
| mp->mAVPlayer->preDestroy(); |
| } |
| SL_LOGV("android_Player_preDestroy(%p) after mAVPlayer->preDestroy()", mp); |
| |
| return XA_RESULT_SUCCESS; |
| } |
| |
| //----------------------------------------------------------------------------- |
| XAresult android_Player_destroy(CMediaPlayer *mp) { |
| SL_LOGV("android_Player_destroy(%p)", mp); |
| |
| mp->mAVPlayer.clear(); |
| |
| // placeholder: not necessary yet as session ID lifetime doesn't extend beyond player |
| // android::AudioSystem::releaseAudioSessionId(mp->mSessionId); |
| |
| mp->mCallbackProtector.clear(); |
| |
| // explicit destructor |
| mp->mAVPlayer.~sp(); |
| mp->mCallbackProtector.~sp(); |
| |
| return XA_RESULT_SUCCESS; |
| } |
| |
| |
| void android_Player_usePlayEventMask(CMediaPlayer *mp) { |
| if (mp->mAVPlayer != 0) { |
| IPlay *pPlayItf = &mp->mPlay; |
| mp->mAVPlayer->setPlayEvents((int32_t) pPlayItf->mEventFlags, |
| (int32_t) pPlayItf->mMarkerPosition, (int32_t) pPlayItf->mPositionUpdatePeriod); |
| } |
| } |
| |
| |
| XAresult android_Player_getDuration(IPlay *pPlayItf, XAmillisecond *pDurMsec) { |
| CMediaPlayer *avp = (CMediaPlayer *)pPlayItf->mThis; |
| |
| switch (avp->mAndroidObjType) { |
| |
| case AUDIOVIDEOPLAYER_FROM_URIFD: { |
| int dur = ANDROID_UNKNOWN_TIME; |
| if (avp->mAVPlayer != 0) { |
| avp->mAVPlayer->getDurationMsec(&dur); |
| } |
| if (dur == ANDROID_UNKNOWN_TIME) { |
| *pDurMsec = XA_TIME_UNKNOWN; |
| } else { |
| *pDurMsec = (XAmillisecond)dur; |
| } |
| } break; |
| |
| case AUDIOVIDEOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: // intended fall-through |
| default: |
| *pDurMsec = XA_TIME_UNKNOWN; |
| break; |
| } |
| |
| return XA_RESULT_SUCCESS; |
| } |
| |
| |
| XAresult android_Player_getPosition(IPlay *pPlayItf, XAmillisecond *pPosMsec) { |
| SL_LOGD("android_Player_getPosition()"); |
| XAresult result = XA_RESULT_SUCCESS; |
| CMediaPlayer *avp = (CMediaPlayer *)pPlayItf->mThis; |
| |
| switch (avp->mAndroidObjType) { |
| |
| case AUDIOVIDEOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: // intended fall-through |
| case AUDIOVIDEOPLAYER_FROM_URIFD: { |
| int pos = ANDROID_UNKNOWN_TIME; |
| if (avp->mAVPlayer != 0) { |
| avp->mAVPlayer->getPositionMsec(&pos); |
| } |
| if (pos == ANDROID_UNKNOWN_TIME) { |
| *pPosMsec = 0; |
| } else { |
| *pPosMsec = (XAmillisecond)pos; |
| } |
| } break; |
| |
| default: |
| // we shouldn't be here |
| assert(false); |
| break; |
| } |
| |
| return result; |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| /** |
| * pre-condition: mp != NULL |
| */ |
| void android_Player_volumeUpdate(CMediaPlayer* mp) |
| { |
| android::GenericPlayer* avp = mp->mAVPlayer.get(); |
| if (avp != NULL) { |
| float volumes[2]; |
| // MediaPlayer does not currently support EffectSend or MuteSolo |
| android_player_volumeUpdate(volumes, &mp->mVolume, mp->mNumChannels, 1.0f, NULL); |
| float leftVol = volumes[0], rightVol = volumes[1]; |
| avp->setVolume(leftVol, rightVol); |
| } |
| } |
| |
| //----------------------------------------------------------------------------- |
| /** |
| * pre-condition: gp != 0 |
| */ |
| XAresult android_Player_setPlayState(const android::sp<android::GenericPlayer> &gp, |
| SLuint32 playState, |
| AndroidObjectState* pObjState) |
| { |
| XAresult result = XA_RESULT_SUCCESS; |
| AndroidObjectState objState = *pObjState; |
| |
| switch (playState) { |
| case SL_PLAYSTATE_STOPPED: { |
| SL_LOGV("setting AVPlayer to SL_PLAYSTATE_STOPPED"); |
| gp->stop(); |
| } |
| break; |
| case SL_PLAYSTATE_PAUSED: { |
| SL_LOGV("setting AVPlayer to SL_PLAYSTATE_PAUSED"); |
| switch(objState) { |
| case ANDROID_UNINITIALIZED: |
| *pObjState = ANDROID_PREPARING; |
| gp->prepare(); |
| break; |
| case ANDROID_PREPARING: |
| break; |
| case ANDROID_READY: |
| gp->pause(); |
| break; |
| default: |
| SL_LOGE("Android object in invalid state"); |
| break; |
| } |
| } |
| break; |
| case SL_PLAYSTATE_PLAYING: { |
| SL_LOGV("setting AVPlayer to SL_PLAYSTATE_PLAYING"); |
| switch(objState) { |
| case ANDROID_UNINITIALIZED: |
| *pObjState = ANDROID_PREPARING; |
| gp->prepare(); |
| // intended fall through |
| case ANDROID_PREPARING: |
| // intended fall through |
| case ANDROID_READY: |
| gp->play(); |
| break; |
| default: |
| SL_LOGE("Android object in invalid state"); |
| break; |
| } |
| } |
| break; |
| default: |
| // checked by caller, should not happen |
| break; |
| } |
| |
| return result; |
| } |
| |
| |
| /** |
| * pre-condition: mp != NULL |
| */ |
| XAresult android_Player_seek(CMediaPlayer *mp, SLmillisecond posMsec) { |
| XAresult result = XA_RESULT_SUCCESS; |
| switch (mp->mAndroidObjType) { |
| case AUDIOVIDEOPLAYER_FROM_URIFD: |
| if (mp->mAVPlayer !=0) { |
| mp->mAVPlayer->seek(posMsec); |
| } |
| break; |
| case AUDIOVIDEOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: // intended fall-through |
| default: { |
| result = XA_RESULT_FEATURE_UNSUPPORTED; |
| } |
| } |
| return result; |
| } |
| |
| |
| /** |
| * pre-condition: mp != NULL |
| */ |
| XAresult android_Player_loop(CMediaPlayer *mp, SLboolean loopEnable) { |
| XAresult result = XA_RESULT_SUCCESS; |
| switch (mp->mAndroidObjType) { |
| case AUDIOVIDEOPLAYER_FROM_URIFD: |
| if (mp->mAVPlayer !=0) { |
| mp->mAVPlayer->loop(loopEnable); |
| } |
| break; |
| case AUDIOVIDEOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: // intended fall-through |
| default: { |
| result = XA_RESULT_FEATURE_UNSUPPORTED; |
| } |
| } |
| return result; |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| void android_Player_androidBufferQueue_clear_l(CMediaPlayer *mp) { |
| if ((mp->mAndroidObjType == AUDIOVIDEOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE) |
| && (mp->mAVPlayer != 0)) { |
| android::StreamPlayer* splr = static_cast<android::StreamPlayer*>(mp->mAVPlayer.get()); |
| splr->appClear_l(); |
| } |
| } |
| |
| |
| void android_Player_androidBufferQueue_onRefilled_l(CMediaPlayer *mp) { |
| if ((mp->mAndroidObjType == AUDIOVIDEOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE) |
| && (mp->mAVPlayer != 0)) { |
| android::StreamPlayer* splr = static_cast<android::StreamPlayer*>(mp->mAVPlayer.get()); |
| splr->queueRefilled(); |
| } |
| } |
| |
| |
| /* |
| * pre-conditions: |
| * mp != NULL |
| * mp->mAVPlayer != 0 (player is realized) |
| * nativeWindow can be NULL, but if NULL it is treated as an error |
| */ |
| SLresult android_Player_setNativeWindow(CMediaPlayer *mp, ANativeWindow *nativeWindow) |
| { |
| assert(mp != NULL); |
| assert(mp->mAVPlayer != 0); |
| if (nativeWindow == NULL) { |
| SL_LOGE("ANativeWindow is NULL"); |
| return SL_RESULT_PARAMETER_INVALID; |
| } |
| SLresult result; |
| int err; |
| int value; |
| // this could crash if app passes in a bad parameter, but that's OK |
| err = (*nativeWindow->query)(nativeWindow, NATIVE_WINDOW_CONCRETE_TYPE, &value); |
| if (0 != err) { |
| SL_LOGE("Query NATIVE_WINDOW_CONCRETE_TYPE on ANativeWindow * %p failed; " |
| "errno %d", nativeWindow, err); |
| result = SL_RESULT_PARAMETER_INVALID; |
| } else { |
| switch (value) { |
| case NATIVE_WINDOW_SURFACE: { // Surface |
| SL_LOGV("Displaying on ANativeWindow of type NATIVE_WINDOW_SURFACE"); |
| android::sp<android::Surface> nativeSurface( |
| static_cast<android::Surface *>(nativeWindow)); |
| mp->mAVPlayer->setVideoSurfaceTexture( |
| nativeSurface->getSurfaceTexture()); |
| result = SL_RESULT_SUCCESS; |
| } break; |
| case NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT: { // SurfaceTextureClient |
| SL_LOGV("Displaying on ANativeWindow of type NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT"); |
| android::sp<android::SurfaceTextureClient> surfaceTextureClient( |
| static_cast<android::SurfaceTextureClient *>(nativeWindow)); |
| android::sp<android::ISurfaceTexture> nativeSurfaceTexture( |
| surfaceTextureClient->getISurfaceTexture()); |
| mp->mAVPlayer->setVideoSurfaceTexture(nativeSurfaceTexture); |
| result = SL_RESULT_SUCCESS; |
| } break; |
| case NATIVE_WINDOW_FRAMEBUFFER: // FramebufferNativeWindow |
| // fall through |
| default: |
| SL_LOGE("ANativeWindow * %p has unknown or unsupported concrete type %d", |
| nativeWindow, value); |
| result = SL_RESULT_PARAMETER_INVALID; |
| break; |
| } |
| } |
| return result; |
| } |