| // Copyright 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. |
| |
| #include "goldfish_media_utils.h" |
| |
| #include "goldfish_address_space.h" |
| |
| #include <log/log.h> |
| |
| #define DEBUG 0 |
| #if DEBUG |
| # define DDD(...) ALOGD(__VA_ARGS__) |
| #else |
| # define DDD(...) ((void)0) |
| #endif |
| |
| #include <memory> |
| #include <vector> |
| #include <mutex> |
| |
| |
| |
| std::mutex sSingletonMutex; |
| std::unique_ptr<GoldfishMediaTransport> sTransport; |
| |
| class GoldfishMediaTransportImpl : public GoldfishMediaTransport { |
| public: |
| GoldfishMediaTransportImpl(); |
| ~GoldfishMediaTransportImpl(); |
| |
| virtual void writeParam(__u64 val, unsigned int num, unsigned int offSetToStartAddr = 0) override; |
| virtual bool sendOperation(MediaCodecType type, MediaOperation op, unsigned int offSetToStartAddr = 0) override; |
| virtual uint8_t* getBaseAddr() const override; |
| virtual uint8_t* getInputAddr(unsigned int offSet = 0) const override; |
| virtual uint8_t* getOutputAddr() const override; |
| virtual uint8_t* getReturnAddr(unsigned int offSet = 0) const override; |
| virtual __u64 offsetOf(uint64_t addr) const override; |
| |
| public: |
| // each lot has 8 M |
| virtual int getMemorySlot() override { |
| std::lock_guard<std::mutex> g{mMemoryMutex}; |
| for (int i = mMemoryLotsAvailable.size() - 1; i >=0 ; --i) { |
| if (mMemoryLotsAvailable[i]) { |
| mMemoryLotsAvailable[i] = false; |
| return i; |
| } |
| } |
| return -1; |
| } |
| virtual void returnMemorySlot(int lot) override { |
| if (lot < 0 || lot >= mMemoryLotsAvailable.size()) { |
| return; |
| } |
| std::lock_guard<std::mutex> g{mMemoryMutex}; |
| if (mMemoryLotsAvailable[lot] == false) { |
| mMemoryLotsAvailable[lot] = true; |
| } else { |
| ALOGE("Error, cannot twice"); |
| } |
| } |
| private: |
| std::mutex mMemoryMutex; |
| std::vector<bool> mMemoryLotsAvailable = {true, true, true, true}; |
| |
| address_space_handle_t mHandle; |
| uint64_t mOffset; |
| uint64_t mPhysAddr; |
| uint64_t mSize; |
| void* mStartPtr = nullptr; |
| |
| // MediaCodecType will be or'd together with the metadata, so the highest 8-bits |
| // will have the type. |
| static __u64 makeMetadata(MediaCodecType type, |
| MediaOperation op, uint64_t offset); |
| |
| // Chunk size for parameters/return data |
| static constexpr size_t kParamSizeBytes = 4096; // 4K |
| // Chunk size for input |
| static constexpr size_t kInputSizeBytes = 4096 * 4096; // 16M |
| // Chunk size for output |
| static constexpr size_t kOutputSizeBytes = 4096 * 4096; // 16M |
| // Maximum number of parameters that can be passed |
| static constexpr size_t kMaxParams = 32; |
| // Offset from the memory region for return data (8 is size of |
| // a parameter in bytes) |
| static constexpr size_t kReturnOffset = 8 * kMaxParams; |
| }; |
| |
| GoldfishMediaTransportImpl::~GoldfishMediaTransportImpl() { |
| if(mHandle >= 0) { |
| goldfish_address_space_close(mHandle); |
| mHandle = -1; |
| } |
| } |
| |
| GoldfishMediaTransportImpl::GoldfishMediaTransportImpl() { |
| // Allocate host memory; the contiguous memory region will be laid out as |
| // follows: |
| // ======================================================== |
| // | kParamSizeBytes | kInputSizeBytes | kOutputSizeBytes | |
| // ======================================================== |
| mHandle = goldfish_address_space_open(); |
| if (mHandle < 0) { |
| ALOGE("Failed to ping host to allocate memory"); |
| abort(); |
| } |
| mSize = kParamSizeBytes + kInputSizeBytes + kOutputSizeBytes; |
| bool success = goldfish_address_space_allocate(mHandle, mSize, &mPhysAddr, &mOffset); |
| if (success) { |
| ALOGI("successfully allocated %d bytes in goldfish_address_block", (int)mSize); |
| mStartPtr = goldfish_address_space_map(mHandle, mOffset, mSize); |
| ALOGI("guest address is %p", mStartPtr); |
| |
| struct goldfish_address_space_ping pingInfo; |
| pingInfo.metadata = GoldfishAddressSpaceSubdeviceType::Media; |
| pingInfo.offset = mOffset; |
| if (goldfish_address_space_ping(mHandle, &pingInfo) == false) { |
| ALOGE("Failed to ping host to allocate memory"); |
| abort(); |
| return; |
| } else { |
| ALOGI("successfully pinged host to allocate memory"); |
| } |
| } else { |
| ALOGE("failed to allocate %d bytes in goldfish_address_block", (int)mSize); |
| abort(); |
| } |
| } |
| |
| // static |
| GoldfishMediaTransport* GoldfishMediaTransport::getInstance() { |
| std::lock_guard<std::mutex> g{sSingletonMutex}; |
| if (sTransport == nullptr) { |
| sTransport.reset(new GoldfishMediaTransportImpl()); |
| } |
| return sTransport.get(); |
| } |
| |
| // static |
| __u64 GoldfishMediaTransportImpl::makeMetadata(MediaCodecType type, |
| MediaOperation op, uint64_t offset) { |
| // Shift |type| into the highest 8-bits, leaving the lower bits for other |
| // metadata. |
| offset = offset >> 20; |
| return ((__u64)type << (64 - 8)) | (offset << 8) | static_cast<uint8_t>(op); |
| } |
| |
| uint8_t* GoldfishMediaTransportImpl::getInputAddr(unsigned int offSet) const { |
| return (uint8_t*)mStartPtr + kParamSizeBytes + offSet; |
| } |
| |
| uint8_t* GoldfishMediaTransportImpl::getOutputAddr() const { |
| return getInputAddr() + kInputSizeBytes; |
| } |
| |
| uint8_t* GoldfishMediaTransportImpl::getBaseAddr() const { |
| return (uint8_t*)mStartPtr; |
| } |
| |
| uint8_t* GoldfishMediaTransportImpl::getReturnAddr(unsigned int offSet) const { |
| return (uint8_t*)mStartPtr + kReturnOffset + offSet; |
| } |
| |
| __u64 GoldfishMediaTransportImpl::offsetOf(uint64_t addr) const { |
| return addr - (uint64_t)mStartPtr; |
| } |
| |
| void GoldfishMediaTransportImpl::writeParam(__u64 val, unsigned int num, unsigned int offSetToStartAddr) { |
| uint8_t* p = (uint8_t*)mStartPtr + (offSetToStartAddr); |
| uint64_t* pint = (uint64_t*)(p + 8 * num); |
| *pint = val; |
| } |
| |
| bool GoldfishMediaTransportImpl::sendOperation(MediaCodecType type, |
| MediaOperation op, unsigned int offSetToStartAddr) { |
| struct goldfish_address_space_ping pingInfo; |
| pingInfo.metadata = makeMetadata(type, op, offSetToStartAddr); |
| pingInfo.offset = mOffset; // + (offSetToStartAddr); |
| if (goldfish_address_space_ping(mHandle, &pingInfo) == false) { |
| ALOGE("failed to ping host"); |
| abort(); |
| return false; |
| } else { |
| DDD("successfully pinged host for operation type=%d, op=%d", (int)type, (int)op); |
| } |
| |
| return true; |
| } |