// 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 #define DEBUG 0 #if DEBUG # define DDD(...) ALOGD(__VA_ARGS__) #else # define DDD(...) ((void)0) #endif #include #include #include std::mutex sSingletonMutex; std::unique_ptr 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 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 g{mMemoryMutex}; if (mMemoryLotsAvailable[lot] == false) { mMemoryLotsAvailable[lot] = true; } else { ALOGE("Error, cannot twice"); } } private: std::mutex mMemoryMutex; std::vector 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 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 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(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 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; }