IEngine.c 55.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * 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.
 */

/* Engine implementation */

19 20
#include "sles_allinclusive.h"

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69

/* Utility functions */

static SLresult initializeBufferQueueMembers(CAudioPlayer *ap) {
    // inline allocation of circular mArray, up to a typical max
    if (BUFFER_HEADER_TYPICAL >= ap->mBufferQueue.mNumBuffers) {
        ap->mBufferQueue.mArray = ap->mBufferQueue.mTypical;
    } else {
        // Avoid possible integer overflow during multiplication; this arbitrary
        // maximum is big enough to not interfere with real applications, but
        // small enough to not overflow.
        if (ap->mBufferQueue.mNumBuffers >= 256) {
            return SL_RESULT_MEMORY_FAILURE;
        }
        ap->mBufferQueue.mArray = (BufferHeader *)
                malloc((ap->mBufferQueue.mNumBuffers + 1) * sizeof(BufferHeader));
        if (NULL == ap->mBufferQueue.mArray) {
            return SL_RESULT_MEMORY_FAILURE;
        }
    }
    ap->mBufferQueue.mFront = ap->mBufferQueue.mArray;
    ap->mBufferQueue.mRear = ap->mBufferQueue.mArray;
    return SL_RESULT_SUCCESS;
}

#ifdef ANDROID
static SLresult initializeAndroidBufferQueueMembers(CAudioPlayer *ap) {
    // Avoid possible integer overflow during multiplication; this arbitrary
    // maximum is big enough to not interfere with real applications, but
    // small enough to not overflow.
    if (ap->mAndroidBufferQueue.mNumBuffers >= 256) {
        return SL_RESULT_MEMORY_FAILURE;
    }
    ap->mAndroidBufferQueue.mBufferArray = (AdvancedBufferHeader *)
            malloc( (ap->mAndroidBufferQueue.mNumBuffers + 1) * sizeof(AdvancedBufferHeader));
    if (NULL == ap->mAndroidBufferQueue.mBufferArray) {
        return SL_RESULT_MEMORY_FAILURE;
    } else {

        // initialize ABQ buffer type
        // assert below has been checked in android_audioPlayer_checkSourceSink
        assert(SL_DATAFORMAT_MIME == ap->mDataSource.mFormat.mFormatType);
        switch(ap->mDataSource.mFormat.mMIME.containerType) {
          case SL_CONTAINERTYPE_MPEG_TS:
            ap->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeMpeg2Ts;
            break;
          case SL_CONTAINERTYPE_AAC:
          case SL_CONTAINERTYPE_RAW: {
            const char* mime = (char*)ap->mDataSource.mFormat.mMIME.mimeType;
70
            if ((mime != NULL) && !(strcasecmp(mime, (const char *)SL_ANDROID_MIME_AACADTS) &&
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
                    strcasecmp(mime, ANDROID_MIME_AACADTS_ANDROID_FRAMEWORK))) {
                ap->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeAacadts;
            } else {
                ap->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeInvalid;
                SL_LOGE("CreateAudioPlayer: Invalid buffer type in Android Buffer Queue");
                return SL_RESULT_CONTENT_UNSUPPORTED;
            }
          } break;
          default:
            ap->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeInvalid;
            SL_LOGE("CreateAudioPlayer: Invalid buffer type in Android Buffer Queue");
            return SL_RESULT_CONTENT_UNSUPPORTED;
        }

        ap->mAndroidBufferQueue.mFront = ap->mAndroidBufferQueue.mBufferArray;
        ap->mAndroidBufferQueue.mRear  = ap->mAndroidBufferQueue.mBufferArray;
    }

    return SL_RESULT_SUCCESS;
}
#endif


94 95
static SLresult IEngine_CreateLEDDevice(SLEngineItf self, SLObjectItf *pDevice, SLuint32 deviceID,
    SLuint32 numInterfaces, const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
96
{
Glenn Kasten's avatar
Glenn Kasten committed
97 98
    SL_ENTER_INTERFACE

99
#if USE_PROFILES & USE_PROFILES_OPTIONAL
100
    if ((NULL == pDevice) || (SL_DEFAULTDEVICEID_LED != deviceID)) {
Glenn Kasten's avatar
Glenn Kasten committed
101 102 103 104 105
        result = SL_RESULT_PARAMETER_INVALID;
    } else {
        *pDevice = NULL;
        unsigned exposedMask;
        const ClassTable *pCLEDDevice_class = objectIDtoClass(SL_OBJECTID_LEDDEVICE);
106 107 108 109
        if (NULL == pCLEDDevice_class) {
            result = SL_RESULT_FEATURE_UNSUPPORTED;
        } else {
            result = checkInterfaces(pCLEDDevice_class, numInterfaces, pInterfaceIds,
110
                pInterfaceRequired, &exposedMask, NULL);
111
        }
Glenn Kasten's avatar
Glenn Kasten committed
112
        if (SL_RESULT_SUCCESS == result) {
113 114
            CLEDDevice *thiz = (CLEDDevice *) construct(pCLEDDevice_class, exposedMask, self);
            if (NULL == thiz) {
Glenn Kasten's avatar
Glenn Kasten committed
115 116
                result = SL_RESULT_MEMORY_FAILURE;
            } else {
117 118
                thiz->mDeviceID = deviceID;
                IObject_Publish(&thiz->mObject);
119
                // return the new LED object
120
                *pDevice = &thiz->mObject.mItf;
Glenn Kasten's avatar
Glenn Kasten committed
121 122 123
            }
        }
    }
124 125 126
#else
    result = SL_RESULT_FEATURE_UNSUPPORTED;
#endif
Glenn Kasten's avatar
Glenn Kasten committed
127 128

    SL_LEAVE_INTERFACE
129 130
}

Glenn Kasten's avatar
Glenn Kasten committed
131

132 133
static SLresult IEngine_CreateVibraDevice(SLEngineItf self, SLObjectItf *pDevice, SLuint32 deviceID,
    SLuint32 numInterfaces, const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
134
{
Glenn Kasten's avatar
Glenn Kasten committed
135 136
    SL_ENTER_INTERFACE

137
#if USE_PROFILES & USE_PROFILES_OPTIONAL
138
    if ((NULL == pDevice) || (SL_DEFAULTDEVICEID_VIBRA != deviceID)) {
Glenn Kasten's avatar
Glenn Kasten committed
139 140 141 142 143
        result = SL_RESULT_PARAMETER_INVALID;
    } else {
        *pDevice = NULL;
        unsigned exposedMask;
        const ClassTable *pCVibraDevice_class = objectIDtoClass(SL_OBJECTID_VIBRADEVICE);
144 145 146 147
        if (NULL == pCVibraDevice_class) {
            result = SL_RESULT_FEATURE_UNSUPPORTED;
        } else {
            result = checkInterfaces(pCVibraDevice_class, numInterfaces,
148
                pInterfaceIds, pInterfaceRequired, &exposedMask, NULL);
149
        }
Glenn Kasten's avatar
Glenn Kasten committed
150
        if (SL_RESULT_SUCCESS == result) {
151 152
            CVibraDevice *thiz = (CVibraDevice *) construct(pCVibraDevice_class, exposedMask, self);
            if (NULL == thiz) {
Glenn Kasten's avatar
Glenn Kasten committed
153 154
                result = SL_RESULT_MEMORY_FAILURE;
            } else {
155 156
                thiz->mDeviceID = deviceID;
                IObject_Publish(&thiz->mObject);
157
                // return the new vibra object
158
                *pDevice = &thiz->mObject.mItf;
Glenn Kasten's avatar
Glenn Kasten committed
159 160 161
            }
        }
    }
162 163 164
#else
    result = SL_RESULT_FEATURE_UNSUPPORTED;
#endif
Glenn Kasten's avatar
Glenn Kasten committed
165 166

    SL_LEAVE_INTERFACE
167 168
}

Glenn Kasten's avatar
Glenn Kasten committed
169

170 171 172 173
static SLresult IEngine_CreateAudioPlayer(SLEngineItf self, SLObjectItf *pPlayer,
    SLDataSource *pAudioSrc, SLDataSink *pAudioSnk, SLuint32 numInterfaces,
    const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
{
Glenn Kasten's avatar
Glenn Kasten committed
174 175 176 177 178 179
    SL_ENTER_INTERFACE

    if (NULL == pPlayer) {
       result = SL_RESULT_PARAMETER_INVALID;
    } else {
        *pPlayer = NULL;
180
        unsigned exposedMask, requiredMask;
Glenn Kasten's avatar
Glenn Kasten committed
181
        const ClassTable *pCAudioPlayer_class = objectIDtoClass(SL_OBJECTID_AUDIOPLAYER);
182
        assert(NULL != pCAudioPlayer_class);
Glenn Kasten's avatar
Glenn Kasten committed
183
        result = checkInterfaces(pCAudioPlayer_class, numInterfaces,
184
            pInterfaceIds, pInterfaceRequired, &exposedMask, &requiredMask);
Glenn Kasten's avatar
Glenn Kasten committed
185 186 187
        if (SL_RESULT_SUCCESS == result) {

            // Construct our new AudioPlayer instance
188 189
            CAudioPlayer *thiz = (CAudioPlayer *) construct(pCAudioPlayer_class, exposedMask, self);
            if (NULL == thiz) {
Glenn Kasten's avatar
Glenn Kasten committed
190 191
                result = SL_RESULT_MEMORY_FAILURE;
            } else {
192

Glenn Kasten's avatar
Glenn Kasten committed
193
                do {
194

Glenn Kasten's avatar
Glenn Kasten committed
195
                    // Initialize private fields not associated with an interface
196 197

                    // Default data source in case of failure in checkDataSource
198 199
                    thiz->mDataSource.mLocator.mLocatorType = SL_DATALOCATOR_NULL;
                    thiz->mDataSource.mFormat.mFormatType = SL_DATAFORMAT_NULL;
200 201

                    // Default data sink in case of failure in checkDataSink
202 203
                    thiz->mDataSink.mLocator.mLocatorType = SL_DATALOCATOR_NULL;
                    thiz->mDataSink.mFormat.mFormatType = SL_DATAFORMAT_NULL;
204 205

                    // Default is no per-channel mute or solo
206 207
                    thiz->mMuteMask = 0;
                    thiz->mSoloMask = 0;
208

209 210
                    // Will be set soon for PCM buffer queues, or later by platform-specific code
                    // during Realize or Prefetch
211 212
                    thiz->mNumChannels = UNKNOWN_NUMCHANNELS;
                    thiz->mSampleRateMilliHz = UNKNOWN_SAMPLERATE;
213

214
                    // More default values, in case destructor needs to be called early
215
                    thiz->mDirectLevel = 0; // no attenuation
216
#ifdef USE_OUTPUTMIXEXT
217 218 219 220
                    thiz->mTrack = NULL;
                    thiz->mGains[0] = 1.0f;
                    thiz->mGains[1] = 1.0f;
                    thiz->mDestroyRequested = SL_BOOLEAN_FALSE;
221 222
#endif
#ifdef USE_SNDFILE
223 224 225 226 227 228 229
                    thiz->mSndFile.mPathname = NULL;
                    thiz->mSndFile.mSNDFILE = NULL;
                    memset(&thiz->mSndFile.mSfInfo, 0, sizeof(SF_INFO));
                    memset(&thiz->mSndFile.mMutex, 0, sizeof(pthread_mutex_t));
                    thiz->mSndFile.mEOF = SL_BOOLEAN_FALSE;
                    thiz->mSndFile.mWhich = 0;
                    memset(thiz->mSndFile.mBuffer, 0, sizeof(thiz->mSndFile.mBuffer));
230 231 232
#endif
#ifdef ANDROID
                    // placement new (explicit constructor)
233 234
                    // FIXME unnecessary once those fields are encapsulated in one class, rather
                    //   than a structure
235
                    (void) new (&thiz->mAudioTrack) android::sp<android::AudioTrack>();
236 237
                    (void) new (&thiz->mCallbackProtector)
                            android::sp<android::CallbackProtector>();
238
                    (void) new (&thiz->mAuxEffect) android::sp<android::AudioEffect>();
239
                    (void) new (&thiz->mAPlayer) android::sp<android::GenericPlayer>();
240 241
                    // Android-specific POD fields are initialized in android_audioPlayer_create,
                    // and assume calloc or memset 0 during allocation
242 243
#endif

Glenn Kasten's avatar
Glenn Kasten committed
244 245 246
                    // Check the source and sink parameters against generic constraints,
                    // and make a local copy of all parameters in case other application threads
                    // change memory concurrently.
247

248
                    result = checkDataSource("pAudioSrc", pAudioSrc, &thiz->mDataSource,
249 250 251 252
                            DATALOCATOR_MASK_URI | DATALOCATOR_MASK_ADDRESS |
                            DATALOCATOR_MASK_BUFFERQUEUE
#ifdef ANDROID
                            | DATALOCATOR_MASK_ANDROIDFD | DATALOCATOR_MASK_ANDROIDSIMPLEBUFFERQUEUE
253
                            | DATALOCATOR_MASK_ANDROIDBUFFERQUEUE
254 255
#endif
                            , DATAFORMAT_MASK_MIME | DATAFORMAT_MASK_PCM);
256

257 258 259 260
                    if (SL_RESULT_SUCCESS != result) {
                        break;
                    }

261
                    result = checkDataSink("pAudioSnk", pAudioSnk, &thiz->mDataSink,
262 263 264 265 266 267 268 269 270 271
                            DATALOCATOR_MASK_OUTPUTMIX                  // for playback
#ifdef ANDROID
                            | DATALOCATOR_MASK_ANDROIDSIMPLEBUFFERQUEUE // for decode to a BQ
                            | DATALOCATOR_MASK_BUFFERQUEUE              // for decode to a BQ
#endif
                            , DATAFORMAT_MASK_NULL
#ifdef ANDROID
                            | DATAFORMAT_MASK_PCM                       // for decode to PCM
#endif
                            );
272
                    if (SL_RESULT_SUCCESS != result) {
Glenn Kasten's avatar
Glenn Kasten committed
273
                        break;
274
                    }
275

276 277 278 279
                    // It would be unsafe to ever refer to the application pointers again
                    pAudioSrc = NULL;
                    pAudioSnk = NULL;

Glenn Kasten's avatar
Glenn Kasten committed
280 281
                    // Check that the requested interfaces are compatible with data source and sink
                    result = checkSourceSinkVsInterfacesCompatibility(&thiz->mDataSource,
282
                            &thiz->mDataSink, pCAudioPlayer_class, requiredMask);
283
                    if (SL_RESULT_SUCCESS != result) {
Glenn Kasten's avatar
Glenn Kasten committed
284
                        break;
285
                    }
286

287 288
                    // copy the buffer queue count from source locator (for playback) / from the
                    // sink locator (for decode on ANDROID build) to the buffer queue interface
Glenn Kasten's avatar
Glenn Kasten committed
289
                    // we have already range-checked the value down to a smaller width
290
                    SLuint16 nbBuffers = 0;
291
                    bool usesAdvancedBufferHeaders = false;
292 293 294
                    bool usesSimpleBufferQueue = false;
                    // creating an AudioPlayer which decodes AAC ADTS buffers to a PCM buffer queue
                    //  will cause usesAdvancedBufferHeaders and usesSimpleBufferQueue to be true
295
                    switch (thiz->mDataSource.mLocator.mLocatorType) {
296 297 298 299
                    case SL_DATALOCATOR_BUFFERQUEUE:
#ifdef ANDROID
                    case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
#endif
300
                        usesSimpleBufferQueue = true;
301
                        nbBuffers = (SLuint16) thiz->mDataSource.mLocator.mBufferQueue.numBuffers;
302 303 304
                        assert(SL_DATAFORMAT_PCM == thiz->mDataSource.mFormat.mFormatType);
                        thiz->mNumChannels = thiz->mDataSource.mFormat.mPCM.numChannels;
                        thiz->mSampleRateMilliHz = thiz->mDataSource.mFormat.mPCM.samplesPerSec;
305
                        break;
306 307 308
#ifdef ANDROID
                    case SL_DATALOCATOR_ANDROIDBUFFERQUEUE:
                        usesAdvancedBufferHeaders = true;
309
                        nbBuffers = (SLuint16) thiz->mDataSource.mLocator.mABQ.numBuffers;
310 311 312
                        thiz->mAndroidBufferQueue.mNumBuffers = nbBuffers;
                        break;
#endif
313
                    default:
314
                        nbBuffers = 0;
315 316
                        break;
                    }
317 318 319 320
#ifdef ANDROID
                    switch(thiz->mDataSink.mLocator.mLocatorType) {
                    case SL_DATALOCATOR_BUFFERQUEUE:
                    case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
321
                        usesSimpleBufferQueue = true;
322 323
                        nbBuffers = thiz->mDataSink.mLocator.mBufferQueue.numBuffers;
                        assert(SL_DATAFORMAT_PCM == thiz->mDataSink.mFormat.mFormatType);
324 325 326 327 328 329 330 331
                        // FIXME The values specified by the app are meaningless. We get the
                        // real values from the decoder.  But the data sink checks currently require
                        // that the app specify these useless values.  Needs doc/fix.
                        // Instead use the "unknown" values, as needed by prepare completion.
                        // thiz->mNumChannels = thiz->mDataSink.mFormat.mPCM.numChannels;
                        // thiz->mSampleRateMilliHz = thiz->mDataSink.mFormat.mPCM.samplesPerSec;
                        thiz->mNumChannels = UNKNOWN_NUMCHANNELS;
                        thiz->mSampleRateMilliHz = UNKNOWN_SAMPLERATE;
332 333 334 335 336 337 338
                        break;
                    default:
                        // leave nbBuffers unchanged
                        break;
                    }
#endif
                    thiz->mBufferQueue.mNumBuffers = nbBuffers;
339

Glenn Kasten's avatar
Glenn Kasten committed
340
                    // check the audio source and sink parameters against platform support
341
#ifdef ANDROID
342
                    result = android_audioPlayer_checkSourceSink(thiz);
343
                    if (SL_RESULT_SUCCESS != result) {
Glenn Kasten's avatar
Glenn Kasten committed
344
                        break;
345
                    }
346
#endif
347

348
#ifdef USE_SNDFILE
349
                    result = SndFile_checkAudioPlayerSourceSink(thiz);
350
                    if (SL_RESULT_SUCCESS != result) {
Glenn Kasten's avatar
Glenn Kasten committed
351
                        break;
352
                    }
353 354 355
#endif

#ifdef USE_OUTPUTMIXEXT
356
                    result = IOutputMixExt_checkAudioPlayerSourceSink(thiz);
357
                    if (SL_RESULT_SUCCESS != result) {
Glenn Kasten's avatar
Glenn Kasten committed
358
                        break;
359
                    }
360
#endif
361

Glenn Kasten's avatar
Glenn Kasten committed
362
                    // Allocate memory for buffer queue
363
                    if (usesAdvancedBufferHeaders) {
364
#ifdef ANDROID
365
                        // locator is SL_DATALOCATOR_ANDROIDBUFFERQUEUE
366
                        result = initializeAndroidBufferQueueMembers(thiz);
367 368 369
#else
                        assert(false);
#endif
370 371 372
                    }

                    if (usesSimpleBufferQueue) {
373 374
                        // locator is SL_DATALOCATOR_BUFFERQUEUE
                        //         or SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE
375
                        result = initializeBufferQueueMembers(thiz);
376
                    }
Glenn Kasten's avatar
Glenn Kasten committed
377

378 379
                    // used to store the data source of our audio player
                    thiz->mDynamicSource.mDataSource = &thiz->mDataSource.u.mSource;
Glenn Kasten's avatar
Glenn Kasten committed
380

381
                    // platform-specific initialization
Glenn Kasten's avatar
Glenn Kasten committed
382
#ifdef ANDROID
383
                    android_audioPlayer_create(thiz);
Glenn Kasten's avatar
Glenn Kasten committed
384
#endif
385

Glenn Kasten's avatar
Glenn Kasten committed
386
                } while (0);
387

388
                if (SL_RESULT_SUCCESS != result) {
389
                    IObject_Destroy(&thiz->mObject.mItf);
390
                } else {
391
                    IObject_Publish(&thiz->mObject);
392
                    // return the new audio player object
393
                    *pPlayer = &thiz->mObject.mItf;
394
                }
395

Glenn Kasten's avatar
Glenn Kasten committed
396 397
            }
        }
398

Glenn Kasten's avatar
Glenn Kasten committed
399 400 401
    }

    SL_LEAVE_INTERFACE
402 403
}

Glenn Kasten's avatar
Glenn Kasten committed
404

405 406 407
static SLresult IEngine_CreateAudioRecorder(SLEngineItf self, SLObjectItf *pRecorder,
    SLDataSource *pAudioSrc, SLDataSink *pAudioSnk, SLuint32 numInterfaces,
    const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
408
{
Glenn Kasten's avatar
Glenn Kasten committed
409 410
    SL_ENTER_INTERFACE

411
#if (USE_PROFILES & USE_PROFILES_OPTIONAL) || defined(ANDROID)
Glenn Kasten's avatar
Glenn Kasten committed
412 413 414 415 416 417
    if (NULL == pRecorder) {
        result = SL_RESULT_PARAMETER_INVALID;
    } else {
        *pRecorder = NULL;
        unsigned exposedMask;
        const ClassTable *pCAudioRecorder_class = objectIDtoClass(SL_OBJECTID_AUDIORECORDER);
418 419 420 421
        if (NULL == pCAudioRecorder_class) {
            result = SL_RESULT_FEATURE_UNSUPPORTED;
        } else {
            result = checkInterfaces(pCAudioRecorder_class, numInterfaces,
422
                    pInterfaceIds, pInterfaceRequired, &exposedMask, NULL);
423
        }
424

Glenn Kasten's avatar
Glenn Kasten committed
425 426 427
        if (SL_RESULT_SUCCESS == result) {

            // Construct our new AudioRecorder instance
428
            CAudioRecorder *thiz = (CAudioRecorder *) construct(pCAudioRecorder_class, exposedMask,
429
                    self);
430
            if (NULL == thiz) {
Glenn Kasten's avatar
Glenn Kasten committed
431 432 433 434
                result = SL_RESULT_MEMORY_FAILURE;
            } else {

                do {
435 436 437

                    // Initialize fields not associated with any interface

438
                    // Default data source in case of failure in checkDataSource
439 440
                    thiz->mDataSource.mLocator.mLocatorType = SL_DATALOCATOR_NULL;
                    thiz->mDataSource.mFormat.mFormatType = SL_DATAFORMAT_NULL;
441 442

                    // Default data sink in case of failure in checkDataSink
443 444
                    thiz->mDataSink.mLocator.mLocatorType = SL_DATALOCATOR_NULL;
                    thiz->mDataSink.mFormat.mFormatType = SL_DATAFORMAT_NULL;
445

446 447 448
                    // These fields are set to real values by
                    // android_audioRecorder_checkSourceSinkSupport.  Note that the data sink is
                    // always PCM buffer queue, so we know the channel count and sample rate early.
449 450
                    thiz->mNumChannels = UNKNOWN_NUMCHANNELS;
                    thiz->mSampleRateMilliHz = UNKNOWN_SAMPLERATE;
451
#ifdef ANDROID
452 453 454 455
                    // placement new (explicit constructor)
                    // FIXME unnecessary once those fields are encapsulated in one class, rather
                    //   than a structure
                    (void) new (&thiz->mAudioRecord) android::sp<android::AudioRecord>();
456 457
                    (void) new (&thiz->mCallbackProtector)
                            android::sp<android::CallbackProtector>();
458
                    thiz->mRecordSource = AUDIO_SOURCE_DEFAULT;
459
#endif
460

Glenn Kasten's avatar
Glenn Kasten committed
461
                    // Check the source and sink parameters, and make a local copy of all parameters
462
                    result = checkDataSource("pAudioSrc", pAudioSrc, &thiz->mDataSource,
463
                            DATALOCATOR_MASK_IODEVICE, DATAFORMAT_MASK_NULL);
464
                    if (SL_RESULT_SUCCESS != result) {
Glenn Kasten's avatar
Glenn Kasten committed
465
                        break;
466
                    }
467
                    result = checkDataSink("pAudioSnk", pAudioSnk, &thiz->mDataSink,
468 469 470 471 472 473
                            DATALOCATOR_MASK_URI
#ifdef ANDROID
                            | DATALOCATOR_MASK_ANDROIDSIMPLEBUFFERQUEUE
#endif
                            , DATAFORMAT_MASK_MIME | DATAFORMAT_MASK_PCM
                    );
474
                    if (SL_RESULT_SUCCESS != result) {
Glenn Kasten's avatar
Glenn Kasten committed
475
                        break;
476
                    }
Glenn Kasten's avatar
Glenn Kasten committed
477

478 479 480 481
                    // It would be unsafe to ever refer to the application pointers again
                    pAudioSrc = NULL;
                    pAudioSnk = NULL;

482 483
                    // check the audio source and sink parameters against platform support
#ifdef ANDROID
484
                    result = android_audioRecorder_checkSourceSinkSupport(thiz);
485
                    if (SL_RESULT_SUCCESS != result) {
Glenn Kasten's avatar
Glenn Kasten committed
486
                        SL_LOGE("Cannot create AudioRecorder: invalid source or sink");
487 488 489 490
                        break;
                    }
#endif

491
#ifdef ANDROID
492
                    // Allocate memory for buffer queue
493
                    SLuint32 locatorType = thiz->mDataSink.mLocator.mLocatorType;
494
                    if (locatorType == SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE) {
495 496
                        thiz->mBufferQueue.mNumBuffers =
                            thiz->mDataSink.mLocator.mBufferQueue.numBuffers;
497
                        // inline allocation of circular Buffer Queue mArray, up to a typical max
498 499
                        if (BUFFER_HEADER_TYPICAL >= thiz->mBufferQueue.mNumBuffers) {
                            thiz->mBufferQueue.mArray = thiz->mBufferQueue.mTypical;
500 501 502 503
                        } else {
                            // Avoid possible integer overflow during multiplication; this arbitrary
                            // maximum is big enough to not interfere with real applications, but
                            // small enough to not overflow.
504
                            if (thiz->mBufferQueue.mNumBuffers >= 256) {
505 506 507
                                result = SL_RESULT_MEMORY_FAILURE;
                                break;
                            }
508
                            thiz->mBufferQueue.mArray = (BufferHeader *) malloc((thiz->mBufferQueue.
509
                                    mNumBuffers + 1) * sizeof(BufferHeader));
510
                            if (NULL == thiz->mBufferQueue.mArray) {
511 512 513 514
                                result = SL_RESULT_MEMORY_FAILURE;
                                break;
                            }
                        }
515 516
                        thiz->mBufferQueue.mFront = thiz->mBufferQueue.mArray;
                        thiz->mBufferQueue.mRear = thiz->mBufferQueue.mArray;
517
                    }
518
#endif
519 520 521

                    // platform-specific initialization
#ifdef ANDROID
522
                    android_audioRecorder_create(thiz);
523
#endif
Glenn Kasten's avatar
Glenn Kasten committed
524 525 526

                } while (0);

527
                if (SL_RESULT_SUCCESS != result) {
528
                    IObject_Destroy(&thiz->mObject.mItf);
529
                } else {
530
                    IObject_Publish(&thiz->mObject);
531
                    // return the new audio recorder object
532
                    *pRecorder = &thiz->mObject.mItf;
533
                }
Glenn Kasten's avatar
Glenn Kasten committed
534 535 536 537 538
            }

        }

    }
539 540 541
#else
    result = SL_RESULT_FEATURE_UNSUPPORTED;
#endif
Glenn Kasten's avatar
Glenn Kasten committed
542 543

    SL_LEAVE_INTERFACE
544 545
}

Glenn Kasten's avatar
Glenn Kasten committed
546

547 548 549 550 551
static SLresult IEngine_CreateMidiPlayer(SLEngineItf self, SLObjectItf *pPlayer,
    SLDataSource *pMIDISrc, SLDataSource *pBankSrc, SLDataSink *pAudioOutput,
    SLDataSink *pVibra, SLDataSink *pLEDArray, SLuint32 numInterfaces,
    const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
{
Glenn Kasten's avatar
Glenn Kasten committed
552 553
    SL_ENTER_INTERFACE

554
#if USE_PROFILES & (USE_PROFILES_GAME | USE_PROFILES_PHONE)
555
    if ((NULL == pPlayer) || (NULL == pMIDISrc) || (NULL == pAudioOutput)) {
Glenn Kasten's avatar
Glenn Kasten committed
556 557 558 559 560
        result = SL_RESULT_PARAMETER_INVALID;
    } else {
        *pPlayer = NULL;
        unsigned exposedMask;
        const ClassTable *pCMidiPlayer_class = objectIDtoClass(SL_OBJECTID_MIDIPLAYER);
561 562 563 564
        if (NULL == pCMidiPlayer_class) {
            result = SL_RESULT_FEATURE_UNSUPPORTED;
        } else {
            result = checkInterfaces(pCMidiPlayer_class, numInterfaces,
565
                pInterfaceIds, pInterfaceRequired, &exposedMask, NULL);
566
        }
Glenn Kasten's avatar
Glenn Kasten committed
567
        if (SL_RESULT_SUCCESS == result) {
568 569
            CMidiPlayer *thiz = (CMidiPlayer *) construct(pCMidiPlayer_class, exposedMask, self);
            if (NULL == thiz) {
Glenn Kasten's avatar
Glenn Kasten committed
570 571
                result = SL_RESULT_MEMORY_FAILURE;
            } else {
572 573 574 575 576 577 578
#if 0
                "pMIDISrc", pMIDISrc, URI | MIDIBUFFERQUEUE, NONE
                "pBankSrc", pBanksrc, NULL | URI | ADDRESS, NULL
                "pAudioOutput", pAudioOutput, OUTPUTMIX, NULL
                "pVibra", pVibra, NULL | IODEVICE, NULL
                "pLEDArray", pLEDArray, NULL | IODEVICE, NULL
#endif
579
                // a fake value - why not use value from IPlay_init? what does CT check for?
580 581
                thiz->mPlay.mDuration = 0;
                IObject_Publish(&thiz->mObject);
582
                // return the new MIDI player object
583
                *pPlayer = &thiz->mObject.mItf;
Glenn Kasten's avatar
Glenn Kasten committed
584 585 586
            }
        }
    }
587 588 589
#else
    result = SL_RESULT_FEATURE_UNSUPPORTED;
#endif
Glenn Kasten's avatar
Glenn Kasten committed
590 591

    SL_LEAVE_INTERFACE
592 593
}

Glenn Kasten's avatar
Glenn Kasten committed
594

595
static SLresult IEngine_CreateListener(SLEngineItf self, SLObjectItf *pListener,
596
    SLuint32 numInterfaces, const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
597
{
Glenn Kasten's avatar
Glenn Kasten committed
598 599
    SL_ENTER_INTERFACE

600
#if USE_PROFILES & USE_PROFILES_GAME
Glenn Kasten's avatar
Glenn Kasten committed
601 602 603 604 605 606
    if (NULL == pListener) {
        result = SL_RESULT_PARAMETER_INVALID;
    } else {
        *pListener = NULL;
        unsigned exposedMask;
        const ClassTable *pCListener_class = objectIDtoClass(SL_OBJECTID_LISTENER);
607 608 609 610
        if (NULL == pCListener_class) {
            result = SL_RESULT_FEATURE_UNSUPPORTED;
        } else {
            result = checkInterfaces(pCListener_class, numInterfaces,
611
                pInterfaceIds, pInterfaceRequired, &exposedMask, NULL);
612
        }
Glenn Kasten's avatar
Glenn Kasten committed
613
        if (SL_RESULT_SUCCESS == result) {
614 615
            CListener *thiz = (CListener *) construct(pCListener_class, exposedMask, self);
            if (NULL == thiz) {
Glenn Kasten's avatar
Glenn Kasten committed
616 617
                result = SL_RESULT_MEMORY_FAILURE;
            } else {
618
                IObject_Publish(&thiz->mObject);
619
                // return the new 3D listener object
620
                *pListener = &thiz->mObject.mItf;
Glenn Kasten's avatar
Glenn Kasten committed
621 622 623
            }
        }
    }
624 625 626
#else
    result = SL_RESULT_FEATURE_UNSUPPORTED;
#endif
Glenn Kasten's avatar
Glenn Kasten committed
627 628

    SL_LEAVE_INTERFACE
629 630
}

Glenn Kasten's avatar
Glenn Kasten committed
631

632 633
static SLresult IEngine_Create3DGroup(SLEngineItf self, SLObjectItf *pGroup, SLuint32 numInterfaces,
    const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
634
{
Glenn Kasten's avatar
Glenn Kasten committed
635 636
    SL_ENTER_INTERFACE

637
#if USE_PROFILES & USE_PROFILES_GAME
Glenn Kasten's avatar
Glenn Kasten committed
638 639 640 641 642 643
    if (NULL == pGroup) {
        result = SL_RESULT_PARAMETER_INVALID;
    } else {
        *pGroup = NULL;
        unsigned exposedMask;
        const ClassTable *pC3DGroup_class = objectIDtoClass(SL_OBJECTID_3DGROUP);
644 645 646 647
        if (NULL == pC3DGroup_class) {
            result = SL_RESULT_FEATURE_UNSUPPORTED;
        } else {
            result = checkInterfaces(pC3DGroup_class, numInterfaces,
648
                pInterfaceIds, pInterfaceRequired, &exposedMask, NULL);
649
        }
Glenn Kasten's avatar
Glenn Kasten committed
650
        if (SL_RESULT_SUCCESS == result) {
651 652
            C3DGroup *thiz = (C3DGroup *) construct(pC3DGroup_class, exposedMask, self);
            if (NULL == thiz) {
Glenn Kasten's avatar
Glenn Kasten committed
653 654
                result = SL_RESULT_MEMORY_FAILURE;
            } else {
655 656
                thiz->mMemberMask = 0;
                IObject_Publish(&thiz->mObject);
657
                // return the new 3D group object
658
                *pGroup = &thiz->mObject.mItf;
Glenn Kasten's avatar
Glenn Kasten committed
659 660 661
            }
        }
    }
662 663 664
#else
    result = SL_RESULT_FEATURE_UNSUPPORTED;
#endif
Glenn Kasten's avatar
Glenn Kasten committed
665 666

    SL_LEAVE_INTERFACE
667 668
}

Glenn Kasten's avatar
Glenn Kasten committed
669

Glenn Kasten's avatar
Glenn Kasten committed
670 671
static SLresult IEngine_CreateOutputMix(SLEngineItf self, SLObjectItf *pMix, SLuint32 numInterfaces,
    const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
672
{
Glenn Kasten's avatar
Glenn Kasten committed
673 674 675 676 677 678 679 680
    SL_ENTER_INTERFACE

    if (NULL == pMix) {
        result = SL_RESULT_PARAMETER_INVALID;
    } else {
        *pMix = NULL;
        unsigned exposedMask;
        const ClassTable *pCOutputMix_class = objectIDtoClass(SL_OBJECTID_OUTPUTMIX);
681
        assert(NULL != pCOutputMix_class);
Glenn Kasten's avatar
Glenn Kasten committed
682
        result = checkInterfaces(pCOutputMix_class, numInterfaces,
683
            pInterfaceIds, pInterfaceRequired, &exposedMask, NULL);
Glenn Kasten's avatar
Glenn Kasten committed
684
        if (SL_RESULT_SUCCESS == result) {
685 686
            COutputMix *thiz = (COutputMix *) construct(pCOutputMix_class, exposedMask, self);
            if (NULL == thiz) {
Glenn Kasten's avatar
Glenn Kasten committed
687 688
                result = SL_RESULT_MEMORY_FAILURE;
            } else {
689
#ifdef ANDROID
690
                android_outputMix_create(thiz);
691
#endif
692
#ifdef USE_SDL
693
                IEngine *thisEngine = &thiz->mObject.mEngine->mEngine;
694 695 696
                interface_lock_exclusive(thisEngine);
                bool unpause = false;
                if (NULL == thisEngine->mOutputMix) {
697
                    thisEngine->mOutputMix = thiz;
698 699 700 701
                    unpause = true;
                }
                interface_unlock_exclusive(thisEngine);
#endif
702
                IObject_Publish(&thiz->mObject);
703 704 705 706 707 708 709
#ifdef USE_SDL
                if (unpause) {
                    // Enable SDL_callback to be called periodically by SDL's internal thread
                    SDL_PauseAudio(0);
                }
#endif
                // return the new output mix object
710
                *pMix = &thiz->mObject.mItf;
Glenn Kasten's avatar
Glenn Kasten committed
711 712 713 714 715
            }
        }
    }

    SL_LEAVE_INTERFACE
716 717
}

Glenn Kasten's avatar
Glenn Kasten committed
718

719 720
static SLresult IEngine_CreateMetadataExtractor(SLEngineItf self, SLObjectItf *pMetadataExtractor,
    SLDataSource *pDataSource, SLuint32 numInterfaces, const SLInterfaceID *pInterfaceIds,
721 722
    const SLboolean *pInterfaceRequired)
{
Glenn Kasten's avatar
Glenn Kasten committed
723 724
    SL_ENTER_INTERFACE

725
#if USE_PROFILES & (USE_PROFILES_GAME | USE_PROFILES_MUSIC)
Glenn Kasten's avatar
Glenn Kasten committed
726 727 728 729 730 731 732
    if (NULL == pMetadataExtractor) {
        result = SL_RESULT_PARAMETER_INVALID;
    } else {
        *pMetadataExtractor = NULL;
        unsigned exposedMask;
        const ClassTable *pCMetadataExtractor_class =
            objectIDtoClass(SL_OBJECTID_METADATAEXTRACTOR);
733 734 735 736
        if (NULL == pCMetadataExtractor_class) {
            result = SL_RESULT_FEATURE_UNSUPPORTED;
        } else {
            result = checkInterfaces(pCMetadataExtractor_class, numInterfaces,
737
                pInterfaceIds, pInterfaceRequired, &exposedMask, NULL);
738
        }
Glenn Kasten's avatar
Glenn Kasten committed
739
        if (SL_RESULT_SUCCESS == result) {
740
            CMetadataExtractor *thiz = (CMetadataExtractor *)
Glenn Kasten's avatar
Glenn Kasten committed
741
                construct(pCMetadataExtractor_class, exposedMask, self);
742
            if (NULL == thiz) {
Glenn Kasten's avatar
Glenn Kasten committed
743 744
                result = SL_RESULT_MEMORY_FAILURE;
            } else {
745 746 747
#if 0
                "pDataSource", pDataSource, NONE, NONE
#endif
748
                IObject_Publish(&thiz->mObject);
749
                // return the new metadata extractor object
750
                *pMetadataExtractor = &thiz->mObject.mItf;
Glenn Kasten's avatar
Glenn Kasten committed
751 752 753 754
                result = SL_RESULT_SUCCESS;
            }
        }
    }
755 756 757
#else
    result = SL_RESULT_FEATURE_UNSUPPORTED;
#endif
Glenn Kasten's avatar
Glenn Kasten committed
758 759

    SL_LEAVE_INTERFACE
760 761
}

Glenn Kasten's avatar
Glenn Kasten committed
762

763 764 765
static SLresult IEngine_CreateExtensionObject(SLEngineItf self, SLObjectItf *pObject,
    void *pParameters, SLuint32 objectID, SLuint32 numInterfaces,
    const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
766
{
Glenn Kasten's avatar
Glenn Kasten committed
767 768 769 770 771 772 773 774 775 776
    SL_ENTER_INTERFACE

    if (NULL == pObject) {
        result = SL_RESULT_PARAMETER_INVALID;
    } else {
        *pObject = NULL;
        result = SL_RESULT_FEATURE_UNSUPPORTED;
    }

    SL_LEAVE_INTERFACE
777 778
}

Glenn Kasten's avatar
Glenn Kasten committed
779

780 781 782
static SLresult IEngine_QueryNumSupportedInterfaces(SLEngineItf self,
    SLuint32 objectID, SLuint32 *pNumSupportedInterfaces)
{
Glenn Kasten's avatar
Glenn Kasten committed
783 784 785 786 787
    SL_ENTER_INTERFACE

    if (NULL == pNumSupportedInterfaces) {
        result = SL_RESULT_PARAMETER_INVALID;
    } else {
788 789
        const ClassTable *clazz = objectIDtoClass(objectID);
        if (NULL == clazz) {
Glenn Kasten's avatar
Glenn Kasten committed
790 791 792 793
            result = SL_RESULT_FEATURE_UNSUPPORTED;
        } else {
            SLuint32 count = 0;
            SLuint32 i;
794 795
            for (i = 0; i < clazz->mInterfaceCount; ++i) {
                switch (clazz->mInterfaces[i].mInterface) {
Glenn Kasten's avatar
Glenn Kasten committed
796
                case INTERFACE_IMPLICIT:
Glenn Kasten's avatar
Glenn Kasten committed
797
                case INTERFACE_IMPLICIT_PREREALIZE:
Glenn Kasten's avatar
Glenn Kasten committed
798
                case INTERFACE_EXPLICIT:
799
                case INTERFACE_EXPLICIT_PREREALIZE:
Glenn Kasten's avatar
Glenn Kasten committed
800
                case INTERFACE_DYNAMIC:
Glenn Kasten's avatar
Glenn Kasten committed
801
                    ++count;
Glenn Kasten's avatar
Glenn Kasten committed
802 803 804 805 806 807 808 809
                    break;
                case INTERFACE_UNAVAILABLE:
                    break;
                default:
                    assert(false);
                    break;
                }
            }
Glenn Kasten's avatar
Glenn Kasten committed
810 811 812 813 814 815
            *pNumSupportedInterfaces = count;
            result = SL_RESULT_SUCCESS;
        }
    }

    SL_LEAVE_INTERFACE;
816 817
}

Glenn Kasten's avatar
Glenn Kasten committed
818

819 820 821
static SLresult IEngine_QuerySupportedInterfaces(SLEngineItf self,
    SLuint32 objectID, SLuint32 index, SLInterfaceID *pInterfaceId)
{
Glenn Kasten's avatar
Glenn Kasten committed
822 823 824 825 826 827
    SL_ENTER_INTERFACE

    if (NULL == pInterfaceId) {
        result = SL_RESULT_PARAMETER_INVALID;
    } else {
        *pInterfaceId = NULL;
828 829
        const ClassTable *clazz = objectIDtoClass(objectID);
        if (NULL == clazz) {
Glenn Kasten's avatar
Glenn Kasten committed
830 831 832 833
            result = SL_RESULT_FEATURE_UNSUPPORTED;
        } else {
            result = SL_RESULT_PARAMETER_INVALID; // will be reset later
            SLuint32 i;
834 835
            for (i = 0; i < clazz->mInterfaceCount; ++i) {
                switch (clazz->mInterfaces[i].mInterface) {
Glenn Kasten's avatar
Glenn Kasten committed
836
                case INTERFACE_IMPLICIT:
Glenn Kasten's avatar
Glenn Kasten committed
837
                case INTERFACE_IMPLICIT_PREREALIZE:
Glenn Kasten's avatar
Glenn Kasten committed
838
                case INTERFACE_EXPLICIT:
839
                case INTERFACE_EXPLICIT_PREREALIZE:
Glenn Kasten's avatar
Glenn Kasten committed
840 841 842
                case INTERFACE_DYNAMIC:
                    break;
                case INTERFACE_UNAVAILABLE:
Glenn Kasten's avatar
Glenn Kasten committed
843
                    continue;
Glenn Kasten's avatar
Glenn Kasten committed
844 845 846 847
                default:
                    assert(false);
                    break;
                }
Glenn Kasten's avatar
Glenn Kasten committed
848
                if (index == 0) {
849
                    *pInterfaceId = &SL_IID_array[clazz->mInterfaces[i].mMPH];
Glenn Kasten's avatar
Glenn Kasten committed
850 851 852 853 854
                    result = SL_RESULT_SUCCESS;
                    break;
                }
                --index;
            }
Glenn Kasten's avatar
Glenn Kasten committed
855 856
        }
    }
Glenn Kasten's avatar
Glenn Kasten committed
857 858

    SL_LEAVE_INTERFACE
859 860
};

Glenn Kasten's avatar
Glenn Kasten committed
861

862 863
static const char * const extensionNames[] = {
#ifdef ANDROID
864 865 866 867 868
#define _(n) #n
#define __(n) _(n)
    "ANDROID_SDK_LEVEL_" __(PLATFORM_SDK_VERSION),
#undef _
#undef __
869 870 871 872 873 874
#else
    "WILHELM_DESKTOP",
#endif
};


875
static SLresult IEngine_QueryNumSupportedExtensions(SLEngineItf self, SLuint32 *pNumExtensions)
876
{
Glenn Kasten's avatar
Glenn Kasten committed
877 878 879 880 881
    SL_ENTER_INTERFACE

    if (NULL == pNumExtensions) {
        result = SL_RESULT_PARAMETER_INVALID;
    } else {
882
        *pNumExtensions = sizeof(extensionNames) / sizeof(extensionNames[0]);
Glenn Kasten's avatar
Glenn Kasten committed
883 884 885 886
        result = SL_RESULT_SUCCESS;
    }

    SL_LEAVE_INTERFACE
887 888
}

Glenn Kasten's avatar
Glenn Kasten committed
889

890 891 892
static SLresult IEngine_QuerySupportedExtension(SLEngineItf self,
    SLuint32 index, SLchar *pExtensionName, SLint16 *pNameLength)
{
Glenn Kasten's avatar
Glenn Kasten committed
893 894
    SL_ENTER_INTERFACE

895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926
    if (NULL == pNameLength) {
        result = SL_RESULT_PARAMETER_INVALID;
    } else {
        size_t actualNameLength;
        unsigned numExtensions = sizeof(extensionNames) / sizeof(extensionNames[0]);
        if (index >= numExtensions) {
            actualNameLength = 0;
            result = SL_RESULT_PARAMETER_INVALID;
        } else {
            const char *extensionName = extensionNames[index];
            actualNameLength = strlen(extensionName) + 1;
            if (NULL == pExtensionName) {
                // application is querying the name length in order to allocate a buffer
                result = SL_RESULT_SUCCESS;
            } else {
                SLint16 availableNameLength = *pNameLength;
                if (0 >= availableNameLength) {
                    // there is not even room for the terminating NUL
                    result = SL_RESULT_BUFFER_INSUFFICIENT;
                } else if (actualNameLength > (size_t) availableNameLength) {
                    // "no invalid strings are written. That is, the null-terminator always exists"
                    memcpy(pExtensionName, extensionName, (size_t) availableNameLength - 1);
                    pExtensionName[(size_t) availableNameLength - 1] = '\0';
                    result = SL_RESULT_BUFFER_INSUFFICIENT;
                } else {
                    memcpy(pExtensionName, extensionName, actualNameLength);
                    result = SL_RESULT_SUCCESS;
                }
            }
        }
        *pNameLength = actualNameLength;
    }
Glenn Kasten's avatar
Glenn Kasten committed
927 928

    SL_LEAVE_INTERFACE
929 930
}

Glenn Kasten's avatar
Glenn Kasten committed
931

932 933 934
static SLresult IEngine_IsExtensionSupported(SLEngineItf self,
    const SLchar *pExtensionName, SLboolean *pSupported)
{
Glenn Kasten's avatar
Glenn Kasten committed
935 936
    SL_ENTER_INTERFACE

937
    if (NULL == pSupported) {
Glenn Kasten's avatar
Glenn Kasten committed
938 939
        result = SL_RESULT_PARAMETER_INVALID;
    } else {
940 941 942 943 944 945 946 947 948 949 950 951 952 953 954
        SLboolean isSupported = SL_BOOLEAN_FALSE;
        if (NULL == pExtensionName) {
            result = SL_RESULT_PARAMETER_INVALID;
        } else {
            unsigned numExtensions = sizeof(extensionNames) / sizeof(extensionNames[0]);
            unsigned i;
            for (i = 0; i < numExtensions; ++i) {
                if (!strcmp((const char *) pExtensionName, extensionNames[i])) {
                    isSupported = SL_BOOLEAN_TRUE;
                    break;
                }
            }
            result = SL_RESULT_SUCCESS;
        }
        *pSupported = isSupported;
Glenn Kasten's avatar
Glenn Kasten committed
955 956 957
    }

    SL_LEAVE_INTERFACE
958 959
}

Glenn Kasten's avatar
Glenn Kasten committed
960

961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980
static const struct SLEngineItf_ IEngine_Itf = {
    IEngine_CreateLEDDevice,
    IEngine_CreateVibraDevice,
    IEngine_CreateAudioPlayer,
    IEngine_CreateAudioRecorder,
    IEngine_CreateMidiPlayer,
    IEngine_CreateListener,
    IEngine_Create3DGroup,
    IEngine_CreateOutputMix,
    IEngine_CreateMetadataExtractor,
    IEngine_CreateExtensionObject,
    IEngine_QueryNumSupportedInterfaces,
    IEngine_QuerySupportedInterfaces,
    IEngine_QueryNumSupportedExtensions,
    IEngine_QuerySupportedExtension,
    IEngine_IsExtensionSupported
};

void IEngine_init(void *self)
{
981 982
    IEngine *thiz = (IEngine *) self;
    thiz->mItf = &IEngine_Itf;
983
    // mLossOfControlGlobal is initialized in slCreateEngine
Glenn Kasten's avatar
Glenn Kasten committed
984
#ifdef USE_SDL
985
    thiz->mOutputMix = NULL;
Glenn Kasten's avatar
Glenn Kasten committed
986
#endif
987 988 989
    thiz->mInstanceCount = 1; // ourself
    thiz->mInstanceMask = 0;
    thiz->mChangedMask = 0;
990
    unsigned i;
991
    for (i = 0; i < MAX_INSTANCE; ++i) {
992
        thiz->mInstances[i] = NULL;
993
    }
994 995
    thiz->mShutdown = SL_BOOLEAN_FALSE;
    thiz->mShutdownAck = SL_BOOLEAN_FALSE;
996
}
997 998 999 1000

void IEngine_deinit(void *self)
{
}
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011


// OpenMAX AL Engine


static XAresult IEngine_CreateCameraDevice(XAEngineItf self, XAObjectItf *pDevice,
        XAuint32 deviceID, XAuint32 numInterfaces, const XAInterfaceID *pInterfaceIds,
        const XAboolean *pInterfaceRequired)
{
    XA_ENTER_INTERFACE

1012
    //IXAEngine *thiz = (IXAEngine *) self;
1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
    result = SL_RESULT_FEATURE_UNSUPPORTED;

    XA_LEAVE_INTERFACE
}


static XAresult IEngine_CreateRadioDevice(XAEngineItf self, XAObjectItf *pDevice,
        XAuint32 numInterfaces, const XAInterfaceID *pInterfaceIds,
        const XAboolean *pInterfaceRequired)
{
    XA_ENTER_INTERFACE

1025
    //IXAEngine *thiz = (IXAEngine *) self;
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061
    result = SL_RESULT_FEATURE_UNSUPPORTED;

    XA_LEAVE_INTERFACE
}


static XAresult IXAEngine_CreateLEDDevice(XAEngineItf self, XAObjectItf *pDevice, XAuint32 deviceID,
        XAuint32 numInterfaces, const XAInterfaceID *pInterfaceIds,
        const XAboolean *pInterfaceRequired)
{
    // forward to OpenSL ES
    return IEngine_CreateLEDDevice(&((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf,
            (SLObjectItf *) pDevice, deviceID, numInterfaces, (const SLInterfaceID *) pInterfaceIds,
            (const SLboolean *) pInterfaceRequired);
}


static XAresult IXAEngine_CreateVibraDevice(XAEngineItf self, XAObjectItf *pDevice,
        XAuint32 deviceID, XAuint32 numInterfaces, const XAInterfaceID *pInterfaceIds,
        const XAboolean *pInterfaceRequired)
{
    // forward to OpenSL ES
    return IEngine_CreateVibraDevice(&((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf,
            (SLObjectItf *) pDevice, deviceID, numInterfaces, (const SLInterfaceID *) pInterfaceIds,
            (const SLboolean *) pInterfaceRequired);
}


static XAresult IEngine_CreateMediaPlayer(XAEngineItf self, XAObjectItf *pPlayer,
        XADataSource *pDataSrc, XADataSource *pBankSrc, XADataSink *pAudioSnk,
        XADataSink *pImageVideoSnk, XADataSink *pVibra, XADataSink *pLEDArray,
        XAuint32 numInterfaces, const XAInterfaceID *pInterfaceIds,
        const XAboolean *pInterfaceRequired)
{
    XA_ENTER_INTERFACE

1062 1063 1064 1065 1066 1067 1068 1069
    if (NULL == pPlayer) {
        result = XA_RESULT_PARAMETER_INVALID;
    } else {
        *pPlayer = NULL;
        unsigned exposedMask;
        const ClassTable *pCMediaPlayer_class = objectIDtoClass(XA_OBJECTID_MEDIAPLAYER);
        assert(NULL != pCMediaPlayer_class);
        result = checkInterfaces(pCMediaPlayer_class, numInterfaces,
1070
                (const SLInterfaceID *) pInterfaceIds, pInterfaceRequired, &exposedMask, NULL);
1071 1072 1073
        if (XA_RESULT_SUCCESS == result) {

            // Construct our new MediaPlayer instance
1074
            CMediaPlayer *thiz = (CMediaPlayer *) construct(pCMediaPlayer_class, exposedMask,
1075
                    &((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf);
1076
            if (NULL == thiz) {
1077 1078 1079 1080 1081 1082 1083
                result = XA_RESULT_MEMORY_FAILURE;
            } else {

                do {

                    // Initialize private fields not associated with an interface

1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
                    // Default data source in case of failure in checkDataSource
                    thiz->mDataSource.mLocator.mLocatorType = SL_DATALOCATOR_NULL;
                    thiz->mDataSource.mFormat.mFormatType = XA_DATAFORMAT_NULL;

                    // Default andio and image sink in case of failure in checkDataSink
                    thiz->mAudioSink.mLocator.mLocatorType = XA_DATALOCATOR_NULL;
                    thiz->mAudioSink.mFormat.mFormatType = XA_DATAFORMAT_NULL;
                    thiz->mImageVideoSink.mLocator.mLocatorType = XA_DATALOCATOR_NULL;
                    thiz->mImageVideoSink.mFormat.mFormatType = XA_DATAFORMAT_NULL;

                    // More default values, in case destructor needs to be called early
Glenn Kasten's avatar
Glenn Kasten committed
1095
                    thiz->mNumChannels = UNKNOWN_NUMCHANNELS;
1096

1097
#ifdef ANDROID
1098
                    // placement new (explicit constructor)
1099 1100
                    // FIXME unnecessary once those fields are encapsulated in one class, rather
                    //   than a structure
1101
                    (void) new (&thiz->mAVPlayer) android::sp<android::GenericPlayer>();
1102 1103
                    (void) new (&thiz->mCallbackProtector)
                            android::sp<android::CallbackProtector>();
1104 1105
                    // Android-specific POD fields are initialized in android_Player_create,
                    // and assume calloc or memset 0 during allocation
1106
#endif
1107 1108 1109

                    // Check the source and sink parameters against generic constraints

1110
                    result = checkDataSource("pDataSrc", (const SLDataSource *) pDataSrc,
1111
                            &thiz->mDataSource, DATALOCATOR_MASK_URI
1112 1113
#ifdef ANDROID
                            | DATALOCATOR_MASK_ANDROIDFD
1114
                            | DATALOCATOR_MASK_ANDROIDBUFFERQUEUE
1115 1116
#endif
                            , DATAFORMAT_MASK_MIME);
1117 1118 1119 1120
                    if (XA_RESULT_SUCCESS != result) {
                        break;
                    }

1121
                    result = checkDataSource("pBankSrc", (const SLDataSource *) pBankSrc,
1122
                            &thiz->mBankSource, DATALOCATOR_MASK_NULL | DATALOCATOR_MASK_URI |
1123
                            DATALOCATOR_MASK_ADDRESS, DATAFORMAT_MASK_NULL);
1124 1125 1126 1127
                    if (XA_RESULT_SUCCESS != result) {
                        break;
                    }

1128
                    result = checkDataSink("pAudioSnk", (const SLDataSink *) pAudioSnk,
1129
                            &thiz->mAudioSink, DATALOCATOR_MASK_OUTPUTMIX, DATAFORMAT_MASK_NULL);
1130 1131 1132 1133
                    if (XA_RESULT_SUCCESS != result) {
                        break;
                    }

1134
                    result = checkDataSink("pImageVideoSnk", (const SLDataSink *) pImageVideoSnk,
1135 1136
                            &thiz->mImageVideoSink,
                            DATALOCATOR_MASK_NULL | DATALOCATOR_MASK_NATIVEDISPLAY,
1137
                            DATAFORMAT_MASK_NULL);
1138 1139 1140 1141
                    if (XA_RESULT_SUCCESS != result) {
                        break;
                    }

1142
                    result = checkDataSink("pVibra", (const SLDataSink *) pVibra, &thiz->mVibraSink,
1143 1144
                            DATALOCATOR_MASK_NULL | DATALOCATOR_MASK_IODEVICE,
                            DATAFORMAT_MASK_NULL);
1145 1146 1147 1148
                    if (XA_RESULT_SUCCESS != result) {
                        break;
                    }

1149
                    result = checkDataSink("pLEDArray", (const SLDataSink *) pLEDArray,
1150
                            &thiz->mLEDArraySink, DATALOCATOR_MASK_NULL | DATALOCATOR_MASK_IODEVICE,
1151
                            DATAFORMAT_MASK_NULL);
1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164
                    if (XA_RESULT_SUCCESS != result) {
                        break;
                    }

                    // Unsafe to ever refer to application pointers again
                    pDataSrc = NULL;
                    pBankSrc = NULL;
                    pAudioSnk = NULL;
                    pImageVideoSnk = NULL;
                    pVibra = NULL;
                    pLEDArray = NULL;

                    // Check that the requested interfaces are compatible with the data source
1165
                    // FIXME implement
1166 1167 1168

                    // check the source and sink parameters against platform support
#ifdef ANDROID
1169
                    result = android_Player_checkSourceSink(thiz);
1170 1171 1172 1173 1174
                    if (XA_RESULT_SUCCESS != result) {
                        break;
                    }
#endif

1175
#ifdef ANDROID
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187
                    // AndroidBufferQueue-specific initialization
                    if (XA_DATALOCATOR_ANDROIDBUFFERQUEUE ==
                            thiz->mDataSource.mLocator.mLocatorType) {
                        XAuint16 nbBuffers = (XAuint16) thiz->mDataSource.mLocator.mABQ.numBuffers;

                        // Avoid possible integer overflow during multiplication; this arbitrary
                        // maximum is big enough to not interfere with real applications, but
                        // small enough to not overflow.
                        if (nbBuffers >= 256) {
                            result = SL_RESULT_MEMORY_FAILURE;
                            break;
                        }
1188 1189 1190 1191 1192 1193 1194

                        // initialize ABQ buffer type
                        // assert below has been checked in android_audioPlayer_checkSourceSink
                        assert(XA_DATAFORMAT_MIME == thiz->mDataSource.mFormat.mFormatType);
                        if (XA_CONTAINERTYPE_MPEG_TS ==
                                thiz->mDataSource.mFormat.mMIME.containerType) {
                            thiz->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeMpeg2Ts;
1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205

                            // Set the container type for the StreamInformation interface
                            XAMediaContainerInformation *containerInfo =
                                    (XAMediaContainerInformation*)
                                        // always storing container info at index 0, as per spec
                                        &(thiz->mStreamInfo.mStreamInfoTable.itemAt(0).
                                                containerInfo);
                            containerInfo->containerType = XA_CONTAINERTYPE_MPEG_TS;
                            // there are no streams at this stage
                            containerInfo->numStreams = 0;

1206 1207 1208 1209 1210 1211 1212
                        } else {
                            thiz->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeInvalid;
                            SL_LOGE("Invalid buffer type in Android Buffer Queue");
                            result = SL_RESULT_CONTENT_UNSUPPORTED;
                        }

                        // initialize ABQ memory
1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227
                        thiz->mAndroidBufferQueue.mBufferArray = (AdvancedBufferHeader *)
                                    malloc( (nbBuffers + 1) * sizeof(AdvancedBufferHeader));
                        if (NULL == thiz->mAndroidBufferQueue.mBufferArray) {
                            result = SL_RESULT_MEMORY_FAILURE;
                            break;
                        } else {
                            thiz->mAndroidBufferQueue.mFront =
                                    thiz->mAndroidBufferQueue.mBufferArray;
                            thiz->mAndroidBufferQueue.mRear =
                                    thiz->mAndroidBufferQueue.mBufferArray;
                        }

                        thiz->mAndroidBufferQueue.mNumBuffers = nbBuffers;

                    }
1228
#endif
1229 1230 1231 1232

                    // used to store the data source of our audio player
                    thiz->mDynamicSource.mDataSource = &thiz->mDataSource.u.mSource;

1233 1234
                    // platform-specific initialization
#ifdef ANDROID
1235
                    android_Player_create(thiz);
1236 1237 1238 1239 1240
#endif

                } while (0);

                if (XA_RESULT_SUCCESS != result) {
1241
                    IObject_Destroy(&thiz->mObject.mItf);
1242
                } else {
1243
                    IObject_Publish(&thiz->mObject);
1244
                    // return the new media player object
1245
                    *pPlayer = (XAObjectItf) &thiz->mObject.mItf;
1246 1247 1248 1249 1250 1251
                }

            }
        }

    }
1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263

    XA_LEAVE_INTERFACE
}


static XAresult IEngine_CreateMediaRecorder(XAEngineItf self, XAObjectItf *pRecorder,
        XADataSource *pAudioSrc, XADataSource *pImageVideoSrc,
        XADataSink *pDataSnk, XAuint32 numInterfaces, const XAInterfaceID *pInterfaceIds,
        const XAboolean *pInterfaceRequired)
{
    XA_ENTER_INTERFACE

1264
    //IXAEngine *thiz = (IXAEngine *) self;
1265 1266
    result = SL_RESULT_FEATURE_UNSUPPORTED;

1267 1268 1269 1270 1271 1272
#if 0
    "pAudioSrc", pAudioSrc,
    "pImageVideoSrc", pImageVideoSrc,
    "pDataSink", pDataSnk,
#endif

1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314
    XA_LEAVE_INTERFACE
}


static XAresult IXAEngine_CreateOutputMix(XAEngineItf self, XAObjectItf *pMix,
        XAuint32 numInterfaces, const XAInterfaceID *pInterfaceIds,
        const XAboolean *pInterfaceRequired)
{
    // forward to OpenSL ES
    return IEngine_CreateOutputMix(&((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf,
            (SLObjectItf *) pMix, numInterfaces, (const SLInterfaceID *) pInterfaceIds,
            (const SLboolean *) pInterfaceRequired);
}


static XAresult IXAEngine_CreateMetadataExtractor(XAEngineItf self, XAObjectItf *pMetadataExtractor,
            XADataSource *pDataSource, XAuint32 numInterfaces,
            const XAInterfaceID *pInterfaceIds, const XAboolean *pInterfaceRequired)
{
    // forward to OpenSL ES
    return IEngine_CreateMetadataExtractor(&((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf,
            (SLObjectItf *) pMetadataExtractor, (SLDataSource *) pDataSource, numInterfaces,
            (const SLInterfaceID *) pInterfaceIds, (const SLboolean *) pInterfaceRequired);
}


static XAresult IXAEngine_CreateExtensionObject(XAEngineItf self, XAObjectItf *pObject,
            void *pParameters, XAuint32 objectID, XAuint32 numInterfaces,
            const XAInterfaceID *pInterfaceIds, const XAboolean *pInterfaceRequired)
{
    // forward to OpenSL ES
    return IEngine_CreateExtensionObject(&((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf,
            (SLObjectItf *) pObject, pParameters, objectID, numInterfaces,
            (const SLInterfaceID *) pInterfaceIds, (const SLboolean *) pInterfaceRequired);
}


static XAresult IEngine_GetImplementationInfo(XAEngineItf self, XAuint32 *pMajor, XAuint32 *pMinor,
        XAuint32 *pStep, /* XAuint32 nImplementationTextSize, */ const XAchar *pImplementationText)
{
    XA_ENTER_INTERFACE

1315
    //IXAEngine *thiz = (IXAEngine *) self;
1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330
    result = SL_RESULT_FEATURE_UNSUPPORTED;

    XA_LEAVE_INTERFACE
}


static XAresult IXAEngine_QuerySupportedProfiles(XAEngineItf self, XAint16 *pProfilesSupported)
{
    XA_ENTER_INTERFACE

    if (NULL == pProfilesSupported) {
        result = XA_RESULT_PARAMETER_INVALID;
    } else {
#if 1
        *pProfilesSupported = 0;
1331
        // the code below was copied from OpenSL ES and needs to be adapted for OpenMAX AL.
1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436
#else
        // The generic implementation doesn't implement any of the profiles, they shouldn't be
        // declared as supported. Also exclude the fake profiles BASE and OPTIONAL.
        *pProfilesSupported = USE_PROFILES &
                (USE_PROFILES_GAME | USE_PROFILES_MUSIC | USE_PROFILES_PHONE);
#endif
        result = XA_RESULT_SUCCESS;
    }

    XA_LEAVE_INTERFACE
}


static XAresult IXAEngine_QueryNumSupportedInterfaces(XAEngineItf self, XAuint32 objectID,
        XAuint32 *pNumSupportedInterfaces)
{
    // forward to OpenSL ES
    return IEngine_QueryNumSupportedInterfaces(
            &((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf, objectID,
            pNumSupportedInterfaces);
}


static XAresult IXAEngine_QuerySupportedInterfaces(XAEngineItf self, XAuint32 objectID,
        XAuint32 index, XAInterfaceID *pInterfaceId)
{
    // forward to OpenSL ES
    return IEngine_QuerySupportedInterfaces(
            &((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf, objectID, index,
            (SLInterfaceID *) pInterfaceId);
}


static XAresult IXAEngine_QueryNumSupportedExtensions(XAEngineItf self, XAuint32 *pNumExtensions)
{
    // forward to OpenSL ES
    return IEngine_QueryNumSupportedExtensions(
            &((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf, pNumExtensions);
}


static XAresult IXAEngine_QuerySupportedExtension(XAEngineItf self, XAuint32 index,
        XAchar *pExtensionName, XAint16 *pNameLength)
{
    // forward to OpenSL ES
    return IEngine_QuerySupportedExtension(&((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf,
            index, pExtensionName, (SLint16 *) pNameLength);
}


static XAresult IXAEngine_IsExtensionSupported(XAEngineItf self, const XAchar *pExtensionName,
        XAboolean *pSupported)
{
    // forward to OpenSL ES
    return IEngine_IsExtensionSupported(&((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf,
            pExtensionName, pSupported);
}


static XAresult IXAEngine_QueryLEDCapabilities(XAEngineItf self, XAuint32 *pIndex,
        XAuint32 *pLEDDeviceID, XALEDDescriptor *pDescriptor)
{
    // forward to OpenSL ES EngineCapabilities
    return (XAresult) IEngineCapabilities_QueryLEDCapabilities(
            &((CEngine *) ((IXAEngine *) self)->mThis)->mEngineCapabilities.mItf, pIndex,
            pLEDDeviceID, (SLLEDDescriptor *) pDescriptor);
}


static XAresult IXAEngine_QueryVibraCapabilities(XAEngineItf self, XAuint32 *pIndex,
        XAuint32 *pVibraDeviceID, XAVibraDescriptor *pDescriptor)
{
    // forward to OpenSL ES EngineCapabilities
    return (XAresult) IEngineCapabilities_QueryVibraCapabilities(
            &((CEngine *) ((IXAEngine *) self)->mThis)->mEngineCapabilities.mItf, pIndex,
            pVibraDeviceID, (SLVibraDescriptor *) pDescriptor);
}


// OpenMAX AL engine v-table

static const struct XAEngineItf_ IXAEngine_Itf = {
    IEngine_CreateCameraDevice,
    IEngine_CreateRadioDevice,
    IXAEngine_CreateLEDDevice,
    IXAEngine_CreateVibraDevice,
    IEngine_CreateMediaPlayer,
    IEngine_CreateMediaRecorder,
    IXAEngine_CreateOutputMix,
    IXAEngine_CreateMetadataExtractor,
    IXAEngine_CreateExtensionObject,
    IEngine_GetImplementationInfo,
    IXAEngine_QuerySupportedProfiles,
    IXAEngine_QueryNumSupportedInterfaces,
    IXAEngine_QuerySupportedInterfaces,
    IXAEngine_QueryNumSupportedExtensions,
    IXAEngine_QuerySupportedExtension,
    IXAEngine_IsExtensionSupported,
    IXAEngine_QueryLEDCapabilities,
    IXAEngine_QueryVibraCapabilities
};


void IXAEngine_init(void *self)
{
1437 1438
    IXAEngine *thiz = (IXAEngine *) self;
    thiz->mItf = &IXAEngine_Itf;
1439
}
Glenn Kasten's avatar
Glenn Kasten committed
1440 1441 1442 1443 1444


void IXAEngine_deinit(void *self)
{
}