MediaPlayer_to_android.cpp revision cfc58893afb25621c08372e35d7ee9e69cf37656
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#include "sles_allinclusive.h"
18#include "utils/RefBase.h"
19#include "android_prompts.h"
20// LocAVPlayer and StreamPlayer derive from GenericMediaPlayer,
21//    so no need to #include "android_GenericMediaPlayer.h"
22#include "android_LocAVPlayer.h"
23#include "android_StreamPlayer.h"
24
25
26//-----------------------------------------------------------------------------
27static void player_handleMediaPlayerEventNotifications(int event, int data1, int data2, void* user)
28{
29    if (NULL == user) {
30        return;
31    }
32
33    CMediaPlayer* mp = (CMediaPlayer*) user;
34    SL_LOGV("received event %d, data %d from AVPlayer", event, data1);
35
36    switch(event) {
37
38      case android::GenericPlayer::kEventPrepared: {
39        if (PLAYER_SUCCESS == data1) {
40            object_lock_exclusive(&mp->mObject);
41            SL_LOGV("Received AVPlayer::kEventPrepared from AVPlayer for CMediaPlayer %p", mp);
42            mp->mAndroidObjState = ANDROID_READY;
43            object_unlock_exclusive(&mp->mObject);
44        }
45        break;
46      }
47
48      case android::GenericPlayer::kEventHasVideoSize: {
49        SL_LOGV("Received AVPlayer::kEventHasVideoSize (%d,%d) for CMediaPlayer %p",
50                data1, data2, mp);
51
52        object_lock_exclusive(&mp->mObject);
53
54        // remove an existing video info entry (here we only have one video stream)
55        for(size_t i=0 ; i < mp->mStreamInfo.mStreamInfoTable.size() ; i++) {
56            if (XA_DOMAINTYPE_VIDEO == mp->mStreamInfo.mStreamInfoTable.itemAt(i).domain) {
57                mp->mStreamInfo.mStreamInfoTable.removeAt(i);
58                break;
59            }
60        }
61        // update the stream information with a new video info entry
62        StreamInfo streamInfo;
63        streamInfo.domain = XA_DOMAINTYPE_VIDEO;
64        streamInfo.videoInfo.codecId = 0;// unknown, we don't have that info FIXME
65        streamInfo.videoInfo.width = (XAuint32)data1;
66        streamInfo.videoInfo.height = (XAuint32)data2;
67        streamInfo.videoInfo.bitRate = 0;// unknown, we don't have that info FIXME
68        streamInfo.videoInfo.duration = XA_TIME_UNKNOWN;
69        StreamInfo &contInfo = mp->mStreamInfo.mStreamInfoTable.editItemAt(0);
70        contInfo.containerInfo.numStreams = 1;
71        ssize_t index = mp->mStreamInfo.mStreamInfoTable.add(streamInfo);
72
73        xaStreamEventChangeCallback callback = mp->mStreamInfo.mCallback;
74        void* callbackPContext = mp->mStreamInfo.mContext;
75
76        object_unlock_exclusive(&mp->mObject);
77
78        // enqueue notification (outside of lock) that the stream information has been updated
79        if ((NULL != callback) && (index >= 0)) {
80#ifdef XA_SYNCHRONOUS_STREAMCBEVENT_PROPERTYCHANGE
81            (*callback)(&mp->mStreamInfo.mItf, XA_STREAMCBEVENT_PROPERTYCHANGE /*eventId*/,
82                    1 /*streamIndex, only one stream supported here, 0 is reserved*/,
83                    NULL /*pEventData, always NULL in OpenMAX AL 1.0.1*/,
84                    callbackPContext /*pContext*/);
85#else
86            SLresult res = EnqueueAsyncCallback_piipp(mp, callback,
87                    /*p1*/ &mp->mStreamInfo.mItf,
88                    /*i1*/ XA_STREAMCBEVENT_PROPERTYCHANGE /*eventId*/,
89                    /*i2*/ 1 /*streamIndex, only one stream supported here, 0 is reserved*/,
90                    /*p2*/ NULL /*pEventData, always NULL in OpenMAX AL 1.0.1*/,
91                    /*p3*/ callbackPContext /*pContext*/);
92#endif
93        }
94        break;
95      }
96
97      case android::GenericPlayer::kEventEndOfStream: {
98        SL_LOGV("Received AVPlayer::kEventEndOfStream for CMediaPlayer %p", mp);
99
100        object_lock_exclusive(&mp->mObject);
101        // should be xaPlayCallback but we're sharing the itf between SL and AL
102        slPlayCallback playCallback = NULL;
103        void * playContext = NULL;
104        // XAPlayItf callback or no callback?
105        if (mp->mPlay.mEventFlags & XA_PLAYEVENT_HEADATEND) {
106            playCallback = mp->mPlay.mCallback;
107            playContext = mp->mPlay.mContext;
108        }
109        mp->mPlay.mState = XA_PLAYSTATE_PAUSED;
110        object_unlock_exclusive(&mp->mObject);
111
112        // enqueue callback with no lock held
113        if (NULL != playCallback) {
114#ifdef XA_SYNCHRONOUS_PLAYEVENT_HEADATEND
115            (*playCallback)(&mp->mPlay.mItf, playContext, XA_PLAYEVENT_HEADATEND);
116#else
117            SLresult res = EnqueueAsyncCallback_ppi(mp, playCallback, &mp->mPlay.mItf, playContext,
118                    XA_PLAYEVENT_HEADATEND);
119            LOGW_IF(SL_RESULT_SUCCESS != res,
120                    "Callback %p(%p, %p, XA_PLAYEVENT_HEADATEND) dropped", playCallback,
121                    &mp->mPlay.mItf, playContext);
122#endif
123        }
124        break;
125      }
126
127      case android::GenericPlayer::kEventChannelCount: {
128        SL_LOGV("kEventChannelCount channels = %d", data1);
129        object_lock_exclusive(&mp->mObject);
130        if (UNKNOWN_NUMCHANNELS == mp->mNumChannels && UNKNOWN_NUMCHANNELS != data1) {
131            mp->mNumChannels = data1;
132            android_Player_volumeUpdate(mp);
133        }
134        object_unlock_exclusive(&mp->mObject);
135      }
136      break;
137
138      case android::GenericPlayer::kEventPrefetchFillLevelUpdate: {
139        SL_LOGV("kEventPrefetchFillLevelUpdate");
140      }
141      break;
142
143      case android::GenericPlayer::kEventPrefetchStatusChange: {
144        SL_LOGV("kEventPrefetchStatusChange");
145      }
146      break;
147
148
149      default: {
150        SL_LOGE("Received unknown event %d, data %d from AVPlayer", event, data1);
151      }
152    }
153}
154
155
156//-----------------------------------------------------------------------------
157XAresult android_Player_checkSourceSink(CMediaPlayer *mp) {
158
159    XAresult result = XA_RESULT_SUCCESS;
160
161    const SLDataSource *pSrc    = &mp->mDataSource.u.mSource;
162    const SLDataSink *pAudioSnk = &mp->mAudioSink.u.mSink;
163
164    // format check:
165    const SLuint32 sourceLocatorType = *(SLuint32 *)pSrc->pLocator;
166    const SLuint32 sourceFormatType  = *(SLuint32 *)pSrc->pFormat;
167    const SLuint32 audioSinkLocatorType = *(SLuint32 *)pAudioSnk->pLocator;
168    //const SLuint32 sinkFormatType = *(SLuint32 *)pAudioSnk->pFormat;
169
170    // Source check
171    switch(sourceLocatorType) {
172
173    case XA_DATALOCATOR_ANDROIDBUFFERQUEUE: {
174        switch (sourceFormatType) {
175        case XA_DATAFORMAT_MIME: {
176            SLDataFormat_MIME *df_mime = (SLDataFormat_MIME *) pSrc->pFormat;
177            if (SL_CONTAINERTYPE_MPEG_TS != df_mime->containerType) {
178                SL_LOGE("Cannot create player with XA_DATALOCATOR_ANDROIDBUFFERQUEUE data source "
179                        "that is not fed MPEG-2 TS data");
180                return SL_RESULT_CONTENT_UNSUPPORTED;
181            }
182        } break;
183        default:
184            SL_LOGE("Cannot create player with XA_DATALOCATOR_ANDROIDBUFFERQUEUE data source "
185                    "without SL_DATAFORMAT_MIME format");
186            return XA_RESULT_CONTENT_UNSUPPORTED;
187        }
188    } break;
189
190    case XA_DATALOCATOR_URI: // intended fall-through
191    case XA_DATALOCATOR_ANDROIDFD:
192        break;
193
194    default:
195        SL_LOGE("Cannot create media player with data locator type 0x%x",
196                (unsigned) sourceLocatorType);
197        return SL_RESULT_PARAMETER_INVALID;
198    }// switch (locatorType)
199
200    // Audio sink check: only playback is supported here
201    switch(audioSinkLocatorType) {
202
203    case XA_DATALOCATOR_OUTPUTMIX:
204        break;
205
206    default:
207        SL_LOGE("Cannot create media player with audio sink data locator of type 0x%x",
208                (unsigned) audioSinkLocatorType);
209        return XA_RESULT_PARAMETER_INVALID;
210    }// switch (locaaudioSinkLocatorTypeorType)
211
212    return result;
213}
214
215
216//-----------------------------------------------------------------------------
217XAresult android_Player_create(CMediaPlayer *mp) {
218
219    XAresult result = XA_RESULT_SUCCESS;
220
221    // FIXME verify data source
222    const SLDataSource *pDataSrc = &mp->mDataSource.u.mSource;
223    // FIXME verify audio data sink
224    const SLDataSink *pAudioSnk = &mp->mAudioSink.u.mSink;
225    // FIXME verify image data sink
226    const SLDataSink *pVideoSnk = &mp->mImageVideoSink.u.mSink;
227
228    XAuint32 sourceLocator = *(XAuint32 *)pDataSrc->pLocator;
229    switch(sourceLocator) {
230    // FIXME support Android simple buffer queue as well
231    case XA_DATALOCATOR_ANDROIDBUFFERQUEUE:
232        mp->mAndroidObjType = AUDIOVIDEOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE;
233        break;
234    case XA_DATALOCATOR_URI: // intended fall-through
235    case SL_DATALOCATOR_ANDROIDFD:
236        mp->mAndroidObjType = AUDIOVIDEOPLAYER_FROM_URIFD;
237        break;
238    case XA_DATALOCATOR_ADDRESS: // intended fall-through
239    default:
240        SL_LOGE("Unable to create MediaPlayer for data source locator 0x%x", sourceLocator);
241        result = XA_RESULT_PARAMETER_INVALID;
242        break;
243    }
244
245    // FIXME duplicates an initialization also done by higher level
246    mp->mAndroidObjState = ANDROID_UNINITIALIZED;
247    mp->mStreamType = ANDROID_DEFAULT_OUTPUT_STREAM_TYPE;
248    mp->mSessionId = android::AudioSystem::newAudioSessionId();
249
250    return result;
251}
252
253
254//-----------------------------------------------------------------------------
255// FIXME abstract out the diff between CMediaPlayer and CAudioPlayer
256XAresult android_Player_realize(CMediaPlayer *mp, SLboolean async) {
257    SL_LOGV("android_Player_realize_l(%p)", mp);
258    XAresult result = XA_RESULT_SUCCESS;
259
260    const SLDataSource *pDataSrc = &mp->mDataSource.u.mSource;
261    const SLuint32 sourceLocator = *(SLuint32 *)pDataSrc->pLocator;
262
263    AudioPlayback_Parameters ap_params;
264    ap_params.sessionId = mp->mSessionId;
265    ap_params.streamType = mp->mStreamType;
266    ap_params.trackcb = NULL;
267    ap_params.trackcbUser = NULL;
268
269    switch(mp->mAndroidObjType) {
270    case AUDIOVIDEOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: {
271        mp->mAVPlayer = new android::StreamPlayer(&ap_params, true /*hasVideo*/);
272        mp->mAVPlayer->init(player_handleMediaPlayerEventNotifications, (void*)mp);
273        }
274        break;
275    case AUDIOVIDEOPLAYER_FROM_URIFD: {
276        mp->mAVPlayer = new android::LocAVPlayer(&ap_params, true /*hasVideo*/);
277        mp->mAVPlayer->init(player_handleMediaPlayerEventNotifications, (void*)mp);
278        switch (mp->mDataSource.mLocator.mLocatorType) {
279        case XA_DATALOCATOR_URI:
280            ((android::LocAVPlayer*)mp->mAVPlayer.get())->setDataSource(
281                    (const char*)mp->mDataSource.mLocator.mURI.URI);
282            break;
283        case XA_DATALOCATOR_ANDROIDFD: {
284            int64_t offset = (int64_t)mp->mDataSource.mLocator.mFD.offset;
285            ((android::LocAVPlayer*)mp->mAVPlayer.get())->setDataSource(
286                    (int)mp->mDataSource.mLocator.mFD.fd,
287                    offset == SL_DATALOCATOR_ANDROIDFD_USE_FILE_SIZE ?
288                            (int64_t)PLAYER_FD_FIND_FILE_SIZE : offset,
289                    (int64_t)mp->mDataSource.mLocator.mFD.length);
290            }
291            break;
292        default:
293            SL_LOGE("Invalid or unsupported data locator type %u for data source",
294                    mp->mDataSource.mLocator.mLocatorType);
295            result = XA_RESULT_PARAMETER_INVALID;
296        }
297        }
298        break;
299    case INVALID_TYPE: // intended fall-through
300    default:
301        SL_LOGE("Unable to realize MediaPlayer, invalid internal Android object type");
302        result = XA_RESULT_PARAMETER_INVALID;
303        break;
304    }
305
306    if (XA_RESULT_SUCCESS == result) {
307
308        // if there is a video sink
309        if (XA_DATALOCATOR_NATIVEDISPLAY ==
310                mp->mImageVideoSink.mLocator.mLocatorType) {
311            ANativeWindow *nativeWindow = (ANativeWindow *)
312                    mp->mImageVideoSink.mLocator.mNativeDisplay.hWindow;
313            // we already verified earlier that hWindow is non-NULL
314            assert(nativeWindow != NULL);
315            result = android_Player_setNativeWindow(mp, nativeWindow);
316        }
317
318    }
319
320    return result;
321}
322
323//-----------------------------------------------------------------------------
324XAresult android_Player_destroy(CMediaPlayer *mp) {
325    SL_LOGV("android_Player_destroy(%p)", mp);
326    XAresult result = XA_RESULT_SUCCESS;
327
328    if (mp->mAVPlayer != 0) {
329        mp->mAVPlayer.clear();
330    }
331
332    return result;
333}
334
335
336XAresult android_Player_getDuration(IPlay *pPlayItf, XAmillisecond *pDurMsec) {
337    CMediaPlayer *avp = (CMediaPlayer *)pPlayItf->mThis;
338
339    switch (avp->mAndroidObjType) {
340
341    case AUDIOVIDEOPLAYER_FROM_URIFD: {
342        int dur = ANDROID_UNKNOWN_TIME;
343        if (avp->mAVPlayer != 0) {
344            avp->mAVPlayer->getDurationMsec(&dur);
345        }
346        if (dur == ANDROID_UNKNOWN_TIME) {
347            *pDurMsec = XA_TIME_UNKNOWN;
348        } else {
349            *pDurMsec = (XAmillisecond)dur;
350        }
351    } break;
352
353    case AUDIOVIDEOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: // intended fall-through
354    default:
355        *pDurMsec = XA_TIME_UNKNOWN;
356        break;
357    }
358
359    return XA_RESULT_SUCCESS;
360}
361
362
363XAresult android_Player_getPosition(IPlay *pPlayItf, XAmillisecond *pPosMsec) {
364    SL_LOGD("android_Player_getPosition()");
365    XAresult result = XA_RESULT_SUCCESS;
366    CMediaPlayer *avp = (CMediaPlayer *)pPlayItf->mThis;
367
368    switch (avp->mAndroidObjType) {
369
370    case AUDIOVIDEOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: // intended fall-through
371    case AUDIOVIDEOPLAYER_FROM_URIFD: {
372        int pos = -1;
373        if (avp->mAVPlayer != 0) {
374            avp->mAVPlayer->getPositionMsec(&pos);
375        }
376        if (pos == ANDROID_UNKNOWN_TIME) {
377            *pPosMsec = XA_TIME_UNKNOWN;
378        } else {
379            *pPosMsec = (XAmillisecond)pos;
380        }
381    } break;
382
383    default:
384        // we shouldn't be here
385        assert(false);
386        break;
387    }
388
389    return result;
390}
391
392
393//-----------------------------------------------------------------------------
394/**
395 * pre-condition: mp != NULL
396 */
397void android_Player_volumeUpdate(CMediaPlayer* mp)
398{
399    android::GenericPlayer* avp = mp->mAVPlayer.get();
400    if (avp != NULL) {
401        float volumes[2];
402        // MediaPlayer does not currently support EffectSend or MuteSolo
403        android_player_volumeUpdate(volumes, &mp->mVolume, mp->mNumChannels, 1.0f, NULL);
404        float leftVol = volumes[0], rightVol = volumes[1];
405        avp->setVolume(leftVol, rightVol);
406    }
407}
408
409//-----------------------------------------------------------------------------
410/**
411 * pre-condition: gp != 0
412 */
413XAresult android_Player_setPlayState(const android::sp<android::GenericPlayer> &gp,
414        SLuint32 playState,
415        AndroidObjectState* pObjState)
416{
417    XAresult result = XA_RESULT_SUCCESS;
418    AndroidObjectState objState = *pObjState;
419
420    switch (playState) {
421     case SL_PLAYSTATE_STOPPED: {
422         SL_LOGV("setting AVPlayer to SL_PLAYSTATE_STOPPED");
423         gp->stop();
424         }
425         break;
426     case SL_PLAYSTATE_PAUSED: {
427         SL_LOGV("setting AVPlayer to SL_PLAYSTATE_PAUSED");
428         switch(objState) {
429         case ANDROID_UNINITIALIZED:
430             *pObjState = ANDROID_PREPARING;
431             gp->prepare();
432             break;
433         case ANDROID_PREPARING:
434             break;
435         case ANDROID_READY:
436             gp->pause();
437             break;
438         default:
439             SL_LOGE("Android object in invalid state");
440             break;
441         }
442         }
443         break;
444     case SL_PLAYSTATE_PLAYING: {
445         SL_LOGV("setting AVPlayer to SL_PLAYSTATE_PLAYING");
446         switch(objState) {
447         case ANDROID_UNINITIALIZED:
448             *pObjState = ANDROID_PREPARING;
449             gp->prepare();
450             // intended fall through
451         case ANDROID_PREPARING:
452             // intended fall through
453         case ANDROID_READY:
454             gp->play();
455             break;
456         default:
457             SL_LOGE("Android object in invalid state");
458             break;
459         }
460         }
461         break;
462     default:
463         // checked by caller, should not happen
464         break;
465     }
466
467    return result;
468}
469
470
471/**
472 * pre-condition: mp != NULL
473 */
474XAresult android_Player_seek(CMediaPlayer *mp, SLmillisecond posMsec) {
475    XAresult result = XA_RESULT_SUCCESS;
476    switch (mp->mAndroidObjType) {
477      case AUDIOVIDEOPLAYER_FROM_URIFD:
478        if (mp->mAVPlayer !=0) {
479            mp->mAVPlayer->seek(posMsec);
480        }
481        break;
482      case AUDIOVIDEOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: // intended fall-through
483      default: {
484          result = XA_RESULT_PARAMETER_INVALID;
485      }
486    }
487    return result;
488}
489
490
491/**
492 * pre-condition: mp != NULL
493 */
494XAresult android_Player_loop(CMediaPlayer *mp, SLboolean loopEnable) {
495    XAresult result = XA_RESULT_SUCCESS;
496    switch (mp->mAndroidObjType) {
497      case AUDIOVIDEOPLAYER_FROM_URIFD:
498        if (mp->mAVPlayer !=0) {
499            mp->mAVPlayer->loop(loopEnable);
500        }
501        break;
502      case AUDIOVIDEOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: // intended fall-through
503      default: {
504          result = XA_RESULT_PARAMETER_INVALID;
505      }
506    }
507    return result;
508}
509
510
511//-----------------------------------------------------------------------------
512void android_Player_androidBufferQueue_registerCallback_l(CMediaPlayer *mp) {
513    if ((mp->mAndroidObjType == AUDIOVIDEOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE)
514            && (mp->mAVPlayer != 0)) {
515        SL_LOGD("android_Player_androidBufferQueue_registerCallback_l");
516        android::StreamPlayer* splr = static_cast<android::StreamPlayer*>(mp->mAVPlayer.get());
517        splr->registerQueueCallback(
518                (const void*)mp, false /*userIsAudioPlayer*/,
519                mp->mAndroidBufferQueue.mContext, (const void*)&(mp->mAndroidBufferQueue.mItf));
520
521    }
522}
523
524
525void android_Player_androidBufferQueue_clear_l(CMediaPlayer *mp) {
526    if ((mp->mAndroidObjType == AUDIOVIDEOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE)
527            && (mp->mAVPlayer != 0)) {
528        android::StreamPlayer* splr = static_cast<android::StreamPlayer*>(mp->mAVPlayer.get());
529        splr->appClear_l();
530    }
531}
532
533
534void android_Player_androidBufferQueue_onRefilled_l(CMediaPlayer *mp) {
535    if ((mp->mAndroidObjType == AUDIOVIDEOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE)
536            && (mp->mAVPlayer != 0)) {
537        android::StreamPlayer* splr = static_cast<android::StreamPlayer*>(mp->mAVPlayer.get());
538        splr->queueRefilled_l();
539    }
540}
541
542
543/*
544 *  pre-conditions:
545 *      mp != NULL
546 *      mp->mAVPlayer != 0 (player is realized)
547 *      nativeWindow can be NULL, but if NULL it is treated as an error
548 */
549SLresult android_Player_setNativeWindow(CMediaPlayer *mp, ANativeWindow *nativeWindow)
550{
551    assert(mp != NULL);
552    assert(mp->mAVPlayer != 0);
553    if (nativeWindow == NULL) {
554        SL_LOGE("ANativeWindow is NULL");
555        return SL_RESULT_PARAMETER_INVALID;
556    }
557    SLresult result;
558    int err;
559    int value;
560    // this could crash if app passes in a bad parameter, but that's OK
561    err = (*nativeWindow->query)(nativeWindow, NATIVE_WINDOW_CONCRETE_TYPE, &value);
562    if (0 != err) {
563        SL_LOGE("Query NATIVE_WINDOW_CONCRETE_TYPE on ANativeWindow * %p failed; "
564                "errno %d", nativeWindow, err);
565        result = SL_RESULT_PARAMETER_INVALID;
566    } else {
567        switch (value) {
568        case NATIVE_WINDOW_SURFACE: {                // Surface
569            SL_LOGV("Displaying on ANativeWindow of type NATIVE_WINDOW_SURFACE");
570            android::sp<android::Surface> nativeSurface(
571                    static_cast<android::Surface *>(nativeWindow));
572            mp->mAVPlayer->setVideoSurface(nativeSurface);
573            result = SL_RESULT_SUCCESS;
574            } break;
575        case NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT: { // SurfaceTextureClient
576            SL_LOGV("Displaying on ANativeWindow of type NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT");
577            android::sp<android::SurfaceTextureClient> surfaceTextureClient(
578                    static_cast<android::SurfaceTextureClient *>(nativeWindow));
579            android::sp<android::ISurfaceTexture> nativeSurfaceTexture(
580                    surfaceTextureClient->getISurfaceTexture());
581            mp->mAVPlayer->setVideoSurfaceTexture(nativeSurfaceTexture);
582            result = SL_RESULT_SUCCESS;
583            } break;
584        case NATIVE_WINDOW_FRAMEBUFFER:              // FramebufferNativeWindow
585            // fall through
586        default:
587            SL_LOGE("ANativeWindow * %p has unknown or unsupported concrete type %d",
588                    nativeWindow, value);
589            result = SL_RESULT_PARAMETER_INVALID;
590            break;
591        }
592    }
593    return result;
594}
595