1/*
2 * Copyright (C) 2009 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 <inttypes.h>
18
19//#define LOG_NDEBUG 0
20#define LOG_TAG "OMX"
21#include <utils/Log.h>
22
23#include <dlfcn.h>
24
25#include "../include/OMX.h"
26
27#include "../include/OMXNodeInstance.h"
28
29#include <binder/IMemory.h>
30#include <media/stagefright/foundation/ADebug.h>
31#include <utils/threads.h>
32
33#include "OMXMaster.h"
34
35#include <OMX_Component.h>
36
37namespace android {
38
39////////////////////////////////////////////////////////////////////////////////
40
41// This provides the underlying Thread used by CallbackDispatcher.
42// Note that deriving CallbackDispatcher from Thread does not work.
43
44struct OMX::CallbackDispatcherThread : public Thread {
45    CallbackDispatcherThread(CallbackDispatcher *dispatcher)
46        : mDispatcher(dispatcher) {
47    }
48
49private:
50    CallbackDispatcher *mDispatcher;
51
52    bool threadLoop();
53
54    CallbackDispatcherThread(const CallbackDispatcherThread &);
55    CallbackDispatcherThread &operator=(const CallbackDispatcherThread &);
56};
57
58////////////////////////////////////////////////////////////////////////////////
59
60struct OMX::CallbackDispatcher : public RefBase {
61    CallbackDispatcher(OMXNodeInstance *owner);
62
63    void post(const omx_message &msg);
64
65    bool loop();
66
67protected:
68    virtual ~CallbackDispatcher();
69
70private:
71    Mutex mLock;
72
73    OMXNodeInstance *mOwner;
74    bool mDone;
75    Condition mQueueChanged;
76    List<omx_message> mQueue;
77
78    sp<CallbackDispatcherThread> mThread;
79
80    void dispatch(const omx_message &msg);
81
82    CallbackDispatcher(const CallbackDispatcher &);
83    CallbackDispatcher &operator=(const CallbackDispatcher &);
84};
85
86OMX::CallbackDispatcher::CallbackDispatcher(OMXNodeInstance *owner)
87    : mOwner(owner),
88      mDone(false) {
89    mThread = new CallbackDispatcherThread(this);
90    mThread->run("OMXCallbackDisp", ANDROID_PRIORITY_FOREGROUND);
91}
92
93OMX::CallbackDispatcher::~CallbackDispatcher() {
94    {
95        Mutex::Autolock autoLock(mLock);
96
97        mDone = true;
98        mQueueChanged.signal();
99    }
100
101    // A join on self can happen if the last ref to CallbackDispatcher
102    // is released within the CallbackDispatcherThread loop
103    status_t status = mThread->join();
104    if (status != WOULD_BLOCK) {
105        // Other than join to self, the only other error return codes are
106        // whatever readyToRun() returns, and we don't override that
107        CHECK_EQ(status, (status_t)NO_ERROR);
108    }
109}
110
111void OMX::CallbackDispatcher::post(const omx_message &msg) {
112    Mutex::Autolock autoLock(mLock);
113
114    mQueue.push_back(msg);
115    mQueueChanged.signal();
116}
117
118void OMX::CallbackDispatcher::dispatch(const omx_message &msg) {
119    if (mOwner == NULL) {
120        ALOGV("Would have dispatched a message to a node that's already gone.");
121        return;
122    }
123    mOwner->onMessage(msg);
124}
125
126bool OMX::CallbackDispatcher::loop() {
127    for (;;) {
128        omx_message msg;
129
130        {
131            Mutex::Autolock autoLock(mLock);
132            while (!mDone && mQueue.empty()) {
133                mQueueChanged.wait(mLock);
134            }
135
136            if (mDone) {
137                break;
138            }
139
140            msg = *mQueue.begin();
141            mQueue.erase(mQueue.begin());
142        }
143
144        dispatch(msg);
145    }
146
147    return false;
148}
149
150////////////////////////////////////////////////////////////////////////////////
151
152bool OMX::CallbackDispatcherThread::threadLoop() {
153    return mDispatcher->loop();
154}
155
156////////////////////////////////////////////////////////////////////////////////
157
158OMX::OMX()
159    : mMaster(new OMXMaster),
160      mNodeCounter(0) {
161}
162
163OMX::~OMX() {
164    delete mMaster;
165    mMaster = NULL;
166}
167
168void OMX::binderDied(const wp<IBinder> &the_late_who) {
169    OMXNodeInstance *instance;
170
171    {
172        Mutex::Autolock autoLock(mLock);
173
174        ssize_t index = mLiveNodes.indexOfKey(the_late_who);
175        CHECK(index >= 0);
176
177        instance = mLiveNodes.editValueAt(index);
178        mLiveNodes.removeItemsAt(index);
179
180        index = mDispatchers.indexOfKey(instance->nodeID());
181        CHECK(index >= 0);
182        mDispatchers.removeItemsAt(index);
183
184        invalidateNodeID_l(instance->nodeID());
185    }
186
187    instance->onObserverDied(mMaster);
188}
189
190bool OMX::livesLocally(node_id /* node */, pid_t pid) {
191    return pid == getpid();
192}
193
194status_t OMX::listNodes(List<ComponentInfo> *list) {
195    list->clear();
196
197    OMX_U32 index = 0;
198    char componentName[256];
199    while (mMaster->enumerateComponents(
200                componentName, sizeof(componentName), index) == OMX_ErrorNone) {
201        list->push_back(ComponentInfo());
202        ComponentInfo &info = *--list->end();
203
204        info.mName = componentName;
205
206        Vector<String8> roles;
207        OMX_ERRORTYPE err =
208            mMaster->getRolesOfComponent(componentName, &roles);
209
210        if (err == OMX_ErrorNone) {
211            for (OMX_U32 i = 0; i < roles.size(); ++i) {
212                info.mRoles.push_back(roles[i]);
213            }
214        }
215
216        ++index;
217    }
218
219    return OK;
220}
221
222status_t OMX::allocateNode(
223        const char *name, const sp<IOMXObserver> &observer, node_id *node) {
224    Mutex::Autolock autoLock(mLock);
225
226    *node = 0;
227
228    OMXNodeInstance *instance = new OMXNodeInstance(this, observer, name);
229
230    OMX_COMPONENTTYPE *handle;
231    OMX_ERRORTYPE err = mMaster->makeComponentInstance(
232            name, &OMXNodeInstance::kCallbacks,
233            instance, &handle);
234
235    if (err != OMX_ErrorNone) {
236        ALOGE("FAILED to allocate omx component '%s'", name);
237
238        instance->onGetHandleFailed();
239
240        return UNKNOWN_ERROR;
241    }
242
243    *node = makeNodeID(instance);
244    mDispatchers.add(*node, new CallbackDispatcher(instance));
245
246    instance->setHandle(*node, handle);
247
248    mLiveNodes.add(observer->asBinder(), instance);
249    observer->asBinder()->linkToDeath(this);
250
251    return OK;
252}
253
254status_t OMX::freeNode(node_id node) {
255    OMXNodeInstance *instance = findInstance(node);
256
257    {
258        Mutex::Autolock autoLock(mLock);
259        ssize_t index = mLiveNodes.indexOfKey(instance->observer()->asBinder());
260        if (index < 0) {
261            // This could conceivably happen if the observer dies at roughly the
262            // same time that a client attempts to free the node explicitly.
263            return OK;
264        }
265        mLiveNodes.removeItemsAt(index);
266    }
267
268    instance->observer()->asBinder()->unlinkToDeath(this);
269
270    status_t err = instance->freeNode(mMaster);
271
272    {
273        Mutex::Autolock autoLock(mLock);
274        ssize_t index = mDispatchers.indexOfKey(node);
275        CHECK(index >= 0);
276        mDispatchers.removeItemsAt(index);
277    }
278
279    return err;
280}
281
282status_t OMX::sendCommand(
283        node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) {
284    return findInstance(node)->sendCommand(cmd, param);
285}
286
287status_t OMX::getParameter(
288        node_id node, OMX_INDEXTYPE index,
289        void *params, size_t size) {
290    ALOGV("getParameter(%u %#x %p %zd)", node, index, params, size);
291    return findInstance(node)->getParameter(
292            index, params, size);
293}
294
295status_t OMX::setParameter(
296        node_id node, OMX_INDEXTYPE index,
297        const void *params, size_t size) {
298    ALOGV("setParameter(%u %#x %p %zd)", node, index, params, size);
299    return findInstance(node)->setParameter(
300            index, params, size);
301}
302
303status_t OMX::getConfig(
304        node_id node, OMX_INDEXTYPE index,
305        void *params, size_t size) {
306    return findInstance(node)->getConfig(
307            index, params, size);
308}
309
310status_t OMX::setConfig(
311        node_id node, OMX_INDEXTYPE index,
312        const void *params, size_t size) {
313    return findInstance(node)->setConfig(
314            index, params, size);
315}
316
317status_t OMX::getState(
318        node_id node, OMX_STATETYPE* state) {
319    return findInstance(node)->getState(
320            state);
321}
322
323status_t OMX::enableGraphicBuffers(
324        node_id node, OMX_U32 port_index, OMX_BOOL enable) {
325    return findInstance(node)->enableGraphicBuffers(port_index, enable);
326}
327
328status_t OMX::getGraphicBufferUsage(
329        node_id node, OMX_U32 port_index, OMX_U32* usage) {
330    return findInstance(node)->getGraphicBufferUsage(port_index, usage);
331}
332
333status_t OMX::storeMetaDataInBuffers(
334        node_id node, OMX_U32 port_index, OMX_BOOL enable) {
335    return findInstance(node)->storeMetaDataInBuffers(port_index, enable);
336}
337
338status_t OMX::prepareForAdaptivePlayback(
339        node_id node, OMX_U32 portIndex, OMX_BOOL enable,
340        OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) {
341    return findInstance(node)->prepareForAdaptivePlayback(
342            portIndex, enable, maxFrameWidth, maxFrameHeight);
343}
344
345status_t OMX::configureVideoTunnelMode(
346        node_id node, OMX_U32 portIndex, OMX_BOOL tunneled,
347        OMX_U32 audioHwSync, native_handle_t **sidebandHandle) {
348    return findInstance(node)->configureVideoTunnelMode(
349            portIndex, tunneled, audioHwSync, sidebandHandle);
350}
351
352status_t OMX::useBuffer(
353        node_id node, OMX_U32 port_index, const sp<IMemory> &params,
354        buffer_id *buffer) {
355    return findInstance(node)->useBuffer(
356            port_index, params, buffer);
357}
358
359status_t OMX::useGraphicBuffer(
360        node_id node, OMX_U32 port_index,
361        const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer) {
362    return findInstance(node)->useGraphicBuffer(
363            port_index, graphicBuffer, buffer);
364}
365
366status_t OMX::updateGraphicBufferInMeta(
367        node_id node, OMX_U32 port_index,
368        const sp<GraphicBuffer> &graphicBuffer, buffer_id buffer) {
369    return findInstance(node)->updateGraphicBufferInMeta(
370            port_index, graphicBuffer, buffer);
371}
372
373status_t OMX::createInputSurface(
374        node_id node, OMX_U32 port_index,
375        sp<IGraphicBufferProducer> *bufferProducer) {
376    return findInstance(node)->createInputSurface(
377            port_index, bufferProducer);
378}
379
380status_t OMX::signalEndOfInputStream(node_id node) {
381    return findInstance(node)->signalEndOfInputStream();
382}
383
384status_t OMX::allocateBuffer(
385        node_id node, OMX_U32 port_index, size_t size,
386        buffer_id *buffer, void **buffer_data) {
387    return findInstance(node)->allocateBuffer(
388            port_index, size, buffer, buffer_data);
389}
390
391status_t OMX::allocateBufferWithBackup(
392        node_id node, OMX_U32 port_index, const sp<IMemory> &params,
393        buffer_id *buffer) {
394    return findInstance(node)->allocateBufferWithBackup(
395            port_index, params, buffer);
396}
397
398status_t OMX::freeBuffer(node_id node, OMX_U32 port_index, buffer_id buffer) {
399    return findInstance(node)->freeBuffer(
400            port_index, buffer);
401}
402
403status_t OMX::fillBuffer(node_id node, buffer_id buffer) {
404    return findInstance(node)->fillBuffer(buffer);
405}
406
407status_t OMX::emptyBuffer(
408        node_id node,
409        buffer_id buffer,
410        OMX_U32 range_offset, OMX_U32 range_length,
411        OMX_U32 flags, OMX_TICKS timestamp) {
412    return findInstance(node)->emptyBuffer(
413            buffer, range_offset, range_length, flags, timestamp);
414}
415
416status_t OMX::getExtensionIndex(
417        node_id node,
418        const char *parameter_name,
419        OMX_INDEXTYPE *index) {
420    return findInstance(node)->getExtensionIndex(
421            parameter_name, index);
422}
423
424status_t OMX::setInternalOption(
425        node_id node,
426        OMX_U32 port_index,
427        InternalOptionType type,
428        const void *data,
429        size_t size) {
430    return findInstance(node)->setInternalOption(port_index, type, data, size);
431}
432
433OMX_ERRORTYPE OMX::OnEvent(
434        node_id node,
435        OMX_IN OMX_EVENTTYPE eEvent,
436        OMX_IN OMX_U32 nData1,
437        OMX_IN OMX_U32 nData2,
438        OMX_IN OMX_PTR /* pEventData */) {
439    ALOGV("OnEvent(%d, %" PRIu32", %" PRIu32 ")", eEvent, nData1, nData2);
440
441    // Forward to OMXNodeInstance.
442    findInstance(node)->onEvent(eEvent, nData1, nData2);
443
444    omx_message msg;
445    msg.type = omx_message::EVENT;
446    msg.node = node;
447    msg.u.event_data.event = eEvent;
448    msg.u.event_data.data1 = nData1;
449    msg.u.event_data.data2 = nData2;
450
451    findDispatcher(node)->post(msg);
452
453    return OMX_ErrorNone;
454}
455
456OMX_ERRORTYPE OMX::OnEmptyBufferDone(
457        node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
458    ALOGV("OnEmptyBufferDone buffer=%p", pBuffer);
459
460    omx_message msg;
461    msg.type = omx_message::EMPTY_BUFFER_DONE;
462    msg.node = node;
463    msg.u.buffer_data.buffer = buffer;
464
465    findDispatcher(node)->post(msg);
466
467    return OMX_ErrorNone;
468}
469
470OMX_ERRORTYPE OMX::OnFillBufferDone(
471        node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
472    ALOGV("OnFillBufferDone buffer=%p", pBuffer);
473
474    omx_message msg;
475    msg.type = omx_message::FILL_BUFFER_DONE;
476    msg.node = node;
477    msg.u.extended_buffer_data.buffer = buffer;
478    msg.u.extended_buffer_data.range_offset = pBuffer->nOffset;
479    msg.u.extended_buffer_data.range_length = pBuffer->nFilledLen;
480    msg.u.extended_buffer_data.flags = pBuffer->nFlags;
481    msg.u.extended_buffer_data.timestamp = pBuffer->nTimeStamp;
482
483    findDispatcher(node)->post(msg);
484
485    return OMX_ErrorNone;
486}
487
488OMX::node_id OMX::makeNodeID(OMXNodeInstance *instance) {
489    // mLock is already held.
490
491    node_id node = (node_id)++mNodeCounter;
492    mNodeIDToInstance.add(node, instance);
493
494    return node;
495}
496
497OMXNodeInstance *OMX::findInstance(node_id node) {
498    Mutex::Autolock autoLock(mLock);
499
500    ssize_t index = mNodeIDToInstance.indexOfKey(node);
501
502    return index < 0 ? NULL : mNodeIDToInstance.valueAt(index);
503}
504
505sp<OMX::CallbackDispatcher> OMX::findDispatcher(node_id node) {
506    Mutex::Autolock autoLock(mLock);
507
508    ssize_t index = mDispatchers.indexOfKey(node);
509
510    return index < 0 ? NULL : mDispatchers.valueAt(index);
511}
512
513void OMX::invalidateNodeID(node_id node) {
514    Mutex::Autolock autoLock(mLock);
515    invalidateNodeID_l(node);
516}
517
518void OMX::invalidateNodeID_l(node_id node) {
519    // mLock is held.
520    mNodeIDToInstance.removeItem(node);
521}
522
523}  // namespace android
524