Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit baea5247 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Start Codec2.0 Implementation"

parents fc74d65b f29f1f2b
Loading
Loading
Loading
Loading
+42 −0
Original line number Diff line number Diff line
cc_library {
    name: "libstagefright_codec2_hidl@1.0",
    vendor_available: true,
    vndk: {
        enabled: true,
    },

    defaults: ["hidl_defaults"],

    srcs: [
        "Component.cpp",
        "ComponentStore.cpp",
        "Configurable.cpp",
        "types.cpp",
    ],

    shared_libs: [
        "android.hardware.media.bufferpool@1.0",
        "libcutils",
        "libhidlbase",
        "libhidltransport",
        "liblog",
        "libstagefright_codec2",
        "libstagefright_codec2_vndk",
        "libutils",
        "vendor.google.media.c2@1.0",
    ],

    export_include_dirs: [
        "include",
    ],

    export_shared_lib_headers: [
        "libstagefright_codec2",
    ],

    // Private include directories
    header_libs: [
        "libstagefright_codec2_internal",
    ],
}
+245 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "Codec2-Component"
#include <log/log.h>

#include <codec2/hidl/1.0/Component.h>
#include <codec2/hidl/1.0/types.h>

namespace vendor {
namespace google {
namespace media {
namespace c2 {
namespace V1_0 {
namespace implementation {

using namespace ::android;

// Implementation of ConfigurableC2Intf based on C2ComponentInterface
struct CompIntf : public ConfigurableC2Intf {
    CompIntf(const std::shared_ptr<C2ComponentInterface>& intf) :
        ConfigurableC2Intf(intf->getName()),
        mIntf(intf) {
    }

    virtual c2_status_t config(
            const std::vector<C2Param*>& params,
            c2_blocking_t mayBlock,
            std::vector<std::unique_ptr<C2SettingResult>>* const failures
            ) override {
        return mIntf->config_vb(params, mayBlock, failures);
    }

    virtual c2_status_t query(
            const std::vector<C2Param::Index>& indices,
            c2_blocking_t mayBlock,
            std::vector<std::unique_ptr<C2Param>>* const params) override {
        return mIntf->query_vb({}, indices, mayBlock, params);
    }

    virtual c2_status_t querySupportedParams(
            std::vector<std::shared_ptr<C2ParamDescriptor>>* const params
            ) const override {
        return mIntf->querySupportedParams_nb(params);
    }

    virtual c2_status_t querySupportedValues(
            std::vector<C2FieldSupportedValuesQuery>& fields,
            c2_blocking_t mayBlock) const override {
        return mIntf->querySupportedValues_vb(fields, mayBlock);
    }

protected:
    std::shared_ptr<C2ComponentInterface> mIntf;
};

// ComponentInterface
ComponentInterface::ComponentInterface(
        const std::shared_ptr<C2ComponentInterface>& intf,
        const sp<ComponentStore>& store) :
    Configurable(new CachedConfigurable(std::make_unique<CompIntf>(intf))),
    mInterface(intf) {
    mInit = init(store.get());
}

c2_status_t ComponentInterface::status() const {
    return mInit;
}

// ComponentListener wrapper
struct Listener : public C2Component::Listener {
    Listener(const wp<IComponentListener>& listener) : mListener(listener) {
        // TODO: Should we track interface errors? We could reuse onError() or
        // create our own error channel.
    }

    virtual void onError_nb(
            std::weak_ptr<C2Component> /* c2component */,
            uint32_t errorCode) override {
        sp<IComponentListener> listener = mListener.promote();
        if (listener) {
            listener->onError(Status::OK, errorCode);
        }
    }

    virtual void onTripped_nb(
            std::weak_ptr<C2Component> /* c2component */,
            std::vector<std::shared_ptr<C2SettingResult>> c2settingResult
            ) override {
        sp<IComponentListener> listener = mListener.promote();
        if (listener) {
            hidl_vec<SettingResult> settingResults(c2settingResult.size());
            size_t ix = 0;
            for (const std::shared_ptr<C2SettingResult> &c2result :
                    c2settingResult) {
                if (c2result) {
                    objcpy(&settingResults[ix++], *c2result);
                }
            }
            settingResults.resize(ix);
            listener->onTripped(settingResults);
        }
    }

    virtual void onWorkDone_nb(
            std::weak_ptr<C2Component> /* c2component */,
            std::list<std::unique_ptr<C2Work>> c2workItems) override {
        sp<IComponentListener> listener = mListener.promote();
        if (listener) {
            WorkBundle workBundle;

            // TODO: Connect with bufferpool API to send Works & Buffers
            if (objcpy(&workBundle, c2workItems) != Status::OK) {
                ALOGE("onWorkDone() received corrupted work items.");
                return;
            }
            listener->onWorkDone(workBundle);

            // Finish buffer transfers: nothing else to do
        }
    }

protected:
    wp<IComponentListener> mListener;
};

// Component
Component::Component(
        const std::shared_ptr<C2Component>& component,
        const sp<IComponentListener>& listener,
        const sp<ComponentStore>& store) :
    Configurable(new CachedConfigurable(
            std::make_unique<CompIntf>(component->intf()))),
    mComponent(component),
    mInterface(component->intf()),
    mListener(listener) /* , // TODO: Do we need store for anything?
    mStore(store)*/ {
    std::shared_ptr<C2Component::Listener> c2listener =
            std::make_shared<Listener>(listener);
    c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK);
    // Retrieve supported parameters from store
    // TODO: We could cache this per component/interface type
    mInit = init(store.get());
    mInit = mInit != C2_OK ? res : mInit;
}

// Methods from ::android::hardware::media::c2::V1_0::IComponent
Return<Status> Component::queue(const WorkBundle& workBundle) {
    std::list<std::unique_ptr<C2Work>> c2works;

    // TODO: Connect with bufferpool API for buffer transfers
    if (objcpy(&c2works, workBundle) != C2_OK) {
        return Status::CORRUPTED;
    }
    (void)workBundle;
    return static_cast<Status>(mComponent->queue_nb(&c2works));
}

Return<void> Component::flush(flush_cb _hidl_cb) {
    std::list<std::unique_ptr<C2Work>> c2flushedWorks;
    c2_status_t c2res = mComponent->flush_sm(
            C2Component::FLUSH_COMPONENT,
            &c2flushedWorks);
    WorkBundle flushedWorkBundle;

    Status res = static_cast<Status>(c2res);
    if (c2res == C2_OK) {
        // TODO: Connect with bufferpool API for buffer transfers
        res = objcpy(&flushedWorkBundle, c2flushedWorks);
    }
    _hidl_cb(res, flushedWorkBundle);
    return Void();
}

Return<Status> Component::drain(bool withEos) {
    return static_cast<Status>(mComponent->drain_nb(withEos ?
            C2Component::DRAIN_COMPONENT_WITH_EOS :
            C2Component::DRAIN_COMPONENT_NO_EOS));
}

Return<Status> Component::connectToInputSurface(const sp<IInputSurface>& surface) {
    // TODO implement
    (void)surface;
    return Status::OK;
}

Return<Status> Component::connectToOmxInputSurface(
        const sp<::android::hardware::graphics::bufferqueue::V1_0::
        IGraphicBufferProducer>& producer,
        const sp<::android::hardware::media::omx::V1_0::
        IGraphicBufferSource>& source) {
    // TODO implement
    (void)producer;
    (void)source;
    return Status::OK;
}

Return<Status> Component::disconnectFromInputSurface() {
    // TODO implement
    return Status::OK;
}

Return<void> Component::createBlockPool(uint32_t allocatorId, createBlockPool_cb _hidl_cb) {
    // TODO implement
    (void)allocatorId;
    _hidl_cb(Status::OK, 0 /* blockPoolId */, nullptr /* configurable */);
    return Void();
}

Return<Status> Component::start() {
    return static_cast<Status>(mComponent->start());
}

Return<Status> Component::stop() {
    return static_cast<Status>(mComponent->stop());
}

Return<Status> Component::reset() {
    return static_cast<Status>(mComponent->reset());
}

Return<Status> Component::release() {
    return static_cast<Status>(mComponent->release());
}

}  // namespace implementation
}  // namespace V1_0
}  // namespace c2
}  // namespace media
}  // namespace google
}  // namespace vendor
+212 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "Codec2-ComponentStore"
#include <log/log.h>

#include <codec2/hidl/1.0/ComponentStore.h>
#include <codec2/hidl/1.0/Component.h>
#include <codec2/hidl/1.0/ConfigurableC2Intf.h>
#include <codec2/hidl/1.0/types.h>

namespace vendor {
namespace google {
namespace media {
namespace c2 {
namespace V1_0 {
namespace implementation {

using namespace ::android;

struct StoreIntf : public ConfigurableC2Intf {
    StoreIntf(const std::shared_ptr<C2ComponentStore>& store) :
        ConfigurableC2Intf(store ? store->getName() : ""),
        mStore(store) {
    }

    c2_status_t config(
            const std::vector<C2Param*> &params,
            c2_blocking_t mayBlock,
            std::vector<std::unique_ptr<C2SettingResult>> *const failures
            ) override {
        // Assume all params are blocking
        // TODO: Filter for supported params
        if (mayBlock == C2_DONT_BLOCK && params.size() != 0) {
            return C2_BLOCKING;
        }
        return mStore->config_sm(params, failures);
    }

    c2_status_t query(
            const std::vector<C2Param::Index> &indices,
            c2_blocking_t mayBlock,
            std::vector<std::unique_ptr<C2Param>> *const params) override {
        // Assume all params are blocking
        // TODO: Filter for supported params
        if (mayBlock == C2_DONT_BLOCK && indices.size() != 0) {
            return C2_BLOCKING;
        }
        return mStore->query_sm({}, indices, params);
    }

    c2_status_t querySupportedParams(
            std::vector<std::shared_ptr<C2ParamDescriptor>> *const params
            ) const override {
        return mStore->querySupportedParams_nb(params);
    }

    c2_status_t querySupportedValues(
            std::vector<C2FieldSupportedValuesQuery> &fields,
            c2_blocking_t mayBlock) const override {
        // Assume all params are blocking
        // TODO: Filter for supported params
        if (mayBlock == C2_DONT_BLOCK && fields.size() != 0) {
            return C2_BLOCKING;
        }
        return mStore->querySupportedValues_sm(fields);
    }

protected:
    std::shared_ptr<C2ComponentStore> mStore;
};


ComponentStore::ComponentStore(const std::shared_ptr<C2ComponentStore>& store) :
    Configurable(new CachedConfigurable(std::make_unique<StoreIntf>(store))),
    mStore(store) {
    // Retrieve struct descriptors
    mParamReflector = mStore->getParamReflector();

    // Retrieve supported parameters from store
    mInit = init(this);
}

c2_status_t ComponentStore::validateSupportedParams(
        const std::vector<std::shared_ptr<C2ParamDescriptor>>& params) {
    c2_status_t res = C2_OK;

    for (const std::shared_ptr<C2ParamDescriptor> &desc : params) {
        if (!desc) {
            // All descriptors should be valid
            res = res ? res : C2_BAD_VALUE;
            continue;
        }
        C2Param::CoreIndex coreIndex = desc->index().coreIndex();
        auto it = mStructDescriptors.find(coreIndex);
        if (it == mStructDescriptors.end()) {
            std::shared_ptr<C2StructDescriptor> structDesc =
                    mParamReflector->describe(coreIndex);
            if (!structDesc) {
                // All supported params must be described
                res = C2_BAD_INDEX;
            }
            mStructDescriptors.insert({ coreIndex, structDesc });
        }
    }
    return res;
}

// Methods from ::android::hardware::media::c2::V1_0::IComponentStore
Return<void> ComponentStore::createComponent(
        const hidl_string& name,
        const sp<IComponentListener>& listener,
        // TODO: Return the pool if the component has it.
        const sp<IClientManager>& /* pool */,
        createComponent_cb _hidl_cb) {
    std::shared_ptr<C2Component> c2component;
    c2_status_t res = mStore->createComponent(name, &c2component);
    sp<IComponent> component;
    if (res == C2_OK) {
        component = new Component(c2component, listener, this);
    }
    _hidl_cb((Status)res, component);
    return Void();
}

Return<void> ComponentStore::createInterface(
        const hidl_string& name,
        createInterface_cb _hidl_cb) {
    std::shared_ptr<C2ComponentInterface> c2interface;
    c2_status_t res = mStore->createInterface(name, &c2interface);
    sp<IComponentInterface> interface;
    if (res == C2_OK) {
        interface = new ComponentInterface(c2interface, this);
    }
    _hidl_cb((Status)res, interface);
    return Void();
}

Return<void> ComponentStore::listComponents(listComponents_cb _hidl_cb) {
    std::vector<std::shared_ptr<const C2Component::Traits>> c2traits =
            mStore->listComponents();
    hidl_vec<IComponentStore::ComponentTraits> traits(c2traits.size());
    size_t ix = 0;
    for (const std::shared_ptr<const C2Component::Traits> &c2trait : c2traits) {
        if (c2trait) {
            objcpy(&traits[ix++], *c2trait);
        }
    }
    traits.resize(ix);
    _hidl_cb(traits);
    return Void();
}

Return<sp<IInputSurface>> ComponentStore::createInputSurface() {
    // TODO implement
    return sp<IInputSurface> {};
}

Return<void> ComponentStore::getStructDescriptors(
        const hidl_vec<uint32_t>& indices,
        getStructDescriptors_cb _hidl_cb) {
    hidl_vec<StructDescriptor> descriptors(indices.size());
    size_t dstIx = 0;
    Status res;
    for (size_t srcIx = 0; srcIx < indices.size(); ++srcIx) {
        const auto item = mStructDescriptors.find(
                C2Param::CoreIndex(indices[srcIx]).coreIndex());
        if (item == mStructDescriptors.end()) {
            res = Status::NOT_FOUND;
        } else if (item->second) {
            objcpy(&descriptors[dstIx++], *item->second);
        } else {
            res = Status::NO_MEMORY;
        }
    }
    descriptors.resize(dstIx);
    _hidl_cb(res, descriptors);
    return Void();
}

Return<sp<IClientManager>> ComponentStore::getPoolClientManager() {
    // TODO implement
    return sp<IClientManager> {};
}

Return<Status> ComponentStore::copyBuffer(const Buffer& src, const Buffer& dst) {
    // TODO implement
    (void)src;
    (void)dst;
    return Status {};
}

}  // namespace implementation
}  // namespace V1_0
}  // namespace c2
}  // namespace media
}  // namespace google
}  // namespace vendor
+167 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "Codec2-Configurable"
#include <log/log.h>

#include <codec2/hidl/1.0/Configurable.h>
#include <codec2/hidl/1.0/ComponentStore.h>
#include <codec2/hidl/1.0/types.h>
#include <C2ParamInternal.h>

namespace vendor {
namespace google {
namespace media {
namespace c2 {
namespace V1_0 {
namespace implementation {

using namespace ::android;

CachedConfigurable::CachedConfigurable(
        std::unique_ptr<ConfigurableC2Intf>&& intf) :
    mIntf(std::move(intf)) {
}

c2_status_t CachedConfigurable::init(ComponentStore* store) {
    // Retrieve supported parameters from store
    c2_status_t init = mIntf->querySupportedParams(&mSupportedParams);
    c2_status_t validate = store->validateSupportedParams(mSupportedParams);
    return init == C2_OK ? C2_OK : validate;
}

// Methods from ::android::hardware::media::c2::V1_0::IConfigurable follow.
Return<void> CachedConfigurable::getName(getName_cb _hidl_cb) {
    _hidl_cb(mIntf->getName());
    return Void();
}

Return<void> CachedConfigurable::query(
        const hidl_vec<uint32_t>& indices,
        bool mayBlock,
        query_cb _hidl_cb) {
    typedef C2Param::Index Index;
    std::vector<Index> c2heapParamIndices(
            (Index*)indices.data(),
            (Index*)indices.data() + indices.size());
    std::vector<std::unique_ptr<C2Param>> c2heapParams;
    c2_status_t c2res = mIntf->query(
            c2heapParamIndices,
            mayBlock ? C2_MAY_BLOCK : C2_DONT_BLOCK,
            &c2heapParams);

    hidl_vec<uint8_t> params;
    createParamsBlob(&params, c2heapParams);
    _hidl_cb(static_cast<Status>(c2res), params);

    return Void();
}

Return<void> CachedConfigurable::config(
        const hidl_vec<uint8_t>& inParams,
        bool mayBlock,
        config_cb _hidl_cb) {
    std::vector<C2Param*> c2params;
    if (parseParamsBlob(&c2params, inParams) != C2_OK) {
        _hidl_cb(Status::CORRUPTED,
                hidl_vec<SettingResult>(),
                hidl_vec<uint8_t>());
        return Void();
    }
    // TODO: check if blob was invalid
    std::vector<std::unique_ptr<C2SettingResult>> c2failures;
    c2_status_t c2res = mIntf->config(
            c2params,
            mayBlock ? C2_MAY_BLOCK : C2_DONT_BLOCK,
            &c2failures);
    hidl_vec<SettingResult> failures(c2failures.size());
    {
        size_t ix = 0;
        for (const std::unique_ptr<C2SettingResult>& c2result : c2failures) {
            if (c2result) {
                objcpy(&failures[ix++], *c2result);
            }
        }
        failures.resize(ix);
    }
    hidl_vec<uint8_t> outParams;
    createParamsBlob(&outParams, c2params);
    _hidl_cb((Status)c2res, failures, outParams);
    return Void();
}

Return<void> CachedConfigurable::querySupportedParams(
        uint32_t start,
        uint32_t count,
        querySupportedParams_cb _hidl_cb) {
    C2LinearRange request = C2LinearCapacity(mSupportedParams.size()).range(
            start, count);
    hidl_vec<ParamDescriptor> params(request.size());
    Status res = Status::OK;
    size_t dstIx = 0;
    for (size_t srcIx = request.offset(); srcIx < request.endOffset(); ++srcIx) {
        if (mSupportedParams[srcIx]) {
            objcpy(&params[dstIx++], *mSupportedParams[srcIx]);
        } else {
            res = Status::CORRUPTED;
        }
    }
    params.resize(dstIx);
    _hidl_cb(res, params);
    return Void();
}

Return<void> CachedConfigurable::querySupportedValues(
        const hidl_vec<FieldSupportedValuesQuery>& inFields,
        bool mayBlock,
        querySupportedValues_cb _hidl_cb) {
    std::vector<C2FieldSupportedValuesQuery> c2fields;
    {
        // C2FieldSupportedValuesQuery objects are restricted in that some
        // members are const.
        // C2ParamField - required for its constructor - has no constructors
        // from fields. Use C2ParamInspector.
        for (const FieldSupportedValuesQuery &query : inFields) {
            c2fields.emplace_back(_C2ParamInspector::CreateParamField(
                    query.field.index,
                    query.field.fieldId.offset,
                    query.field.fieldId.size),
                    query.type == FieldSupportedValuesQuery::Type::POSSIBLE ?
                    C2FieldSupportedValuesQuery::POSSIBLE :
                    C2FieldSupportedValuesQuery::CURRENT);
        }
    }
    c2_status_t c2res = mIntf->querySupportedValues(
            c2fields,
            mayBlock ? C2_MAY_BLOCK : C2_DONT_BLOCK);
    hidl_vec<FieldSupportedValuesQueryResult> outFields(inFields.size());
    {
        size_t ix = 0;
        for (const C2FieldSupportedValuesQuery &result : c2fields) {
            objcpy(&outFields[ix++], result);
        }
    }
    _hidl_cb((Status)c2res, outFields);
    return Void();
}

}  // namespace implementation
}  // namespace V1_0
}  // namespace c2
}  // namespace media
}  // namespace google
}  // namespace vendor
+85 −0
Original line number Diff line number Diff line
#ifndef VENDOR_GOOGLE_MEDIA_C2_V1_0_COMPONENT_H
#define VENDOR_GOOGLE_MEDIA_C2_V1_0_COMPONENT_H

#include <codec2/hidl/1.0/Configurable.h>

#include <vendor/google/media/c2/1.0/IComponentListener.h>
#include <vendor/google/media/c2/1.0/IComponentStore.h>
#include <vendor/google/media/c2/1.0/IComponent.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>

#include <C2Component.h>
#include <C2.h>

namespace vendor {
namespace google {
namespace media {
namespace c2 {
namespace V1_0 {
namespace implementation {

using ::android::hardware::hidl_array;
using ::android::hardware::hidl_memory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;

struct ComponentStore;

struct ComponentInterface : public Configurable<IComponentInterface> {
    ComponentInterface(
            const std::shared_ptr<C2ComponentInterface>& interface,
            const sp<ComponentStore>& store);
    c2_status_t status() const;

protected:
    c2_status_t mInit;
    std::shared_ptr<C2ComponentInterface> mInterface;
    // sp<ComponentStore> mStore; // TODO needed?
};

struct Component : public Configurable<IComponent> {
    Component(
            const std::shared_ptr<C2Component>&,
            const sp<IComponentListener>& listener,
            const sp<ComponentStore>& store);

    // Methods from gIComponent follow.
    virtual Return<Status> queue(const WorkBundle& workBundle) override;
    virtual Return<void> flush(flush_cb _hidl_cb) override;
    virtual Return<Status> drain(bool withEos) override;
    virtual Return<Status> connectToInputSurface(
            const sp<IInputSurface>& surface) override;
    virtual Return<Status> connectToOmxInputSurface(
            const sp<::android::hardware::graphics::bufferqueue::V1_0::
            IGraphicBufferProducer>& producer,
            const sp<::android::hardware::media::omx::V1_0::
            IGraphicBufferSource>& source) override;
    virtual Return<Status> disconnectFromInputSurface() override;
    virtual Return<void> createBlockPool(
            uint32_t allocatorId,
            createBlockPool_cb _hidl_cb) override;
    virtual Return<Status> start() override;
    virtual Return<Status> stop() override;
    virtual Return<Status> reset() override;
    virtual Return<Status> release() override;

protected:
    c2_status_t mInit;
    std::shared_ptr<C2Component> mComponent;
    std::shared_ptr<C2ComponentInterface> mInterface;
    sp<IComponentListener> mListener;
    // sp<ComponentStore> mStore; // TODO needed?
};

}  // namespace implementation
}  // namespace V1_0
}  // namespace c2
}  // namespace media
}  // namespace google
}  // namespace vendor

#endif  // VENDOR_GOOGLE_MEDIA_C2_V1_0_COMPONENT_H
Loading