1 // Copyright 2018 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "goldfish_media_utils.h"
16 
17 #include "goldfish_address_space.h"
18 
19 #include <log/log.h>
20 
21 #define DEBUG 0
22 #if DEBUG
23 #define DDD(...) ALOGD(__VA_ARGS__)
24 #else
25 #define DDD(...) ((void)0)
26 #endif
27 
28 #include <memory>
29 #include <mutex>
30 #include <vector>
31 
32 std::mutex sSingletonMutex;
33 std::unique_ptr<GoldfishMediaTransport> sTransport;
34 
35 class GoldfishMediaTransportImpl : public GoldfishMediaTransport {
36   public:
37     GoldfishMediaTransportImpl();
38     ~GoldfishMediaTransportImpl();
39 
40     virtual void writeParam(__u64 val, unsigned int num,
41                             unsigned int offSetToStartAddr = 0) override;
42     virtual bool sendOperation(MediaCodecType type, MediaOperation op,
43                                unsigned int offSetToStartAddr = 0) override;
44     virtual uint8_t *getBaseAddr() const override;
45     virtual uint8_t *getInputAddr(unsigned int offSet = 0) const override;
46     virtual uint8_t *getOutputAddr() const override;
47     virtual uint8_t *getReturnAddr(unsigned int offSet = 0) const override;
48     virtual __u64 offsetOf(uint64_t addr) const override;
49 
50   public:
51     // each lot has 2 M
getMemorySlot()52     virtual int getMemorySlot() override {
53         std::lock_guard<std::mutex> g{mMemoryMutex};
54         // when there are just 1 decoder, it can pretty
55         // much use all the memory starting from 0;
56         // when there are two, each can use at least half
57         // the total memory, etc.
58         constexpr size_t search_order[] = {
59             0,                              // use 32M
60             16,                             // use 16M
61             8,  24,                         // use 8M
62             4,  12, 20, 28,                 // use 4M
63             2,  6,  10, 12, 18, 22, 26, 30, // use 2M
64             1,  3,  5,  7,  9,  11, 13, 15,
65             17, 19, 21, 23, 25, 27, 29, 31 // use 1M
66         };
67         for (size_t i = 0; i < sizeof(search_order) / sizeof(search_order[0]);
68              ++i) {
69             int slot = search_order[i];
70             if (mMemoryLotsAvailable[slot]) {
71                 mMemoryLotsAvailable[slot] = false;
72                 return slot;
73             }
74         }
75         return -1;
76     }
returnMemorySlot(int lot)77     virtual void returnMemorySlot(int lot) override {
78         if (lot < 0 || lot >= mMemoryLotsAvailable.size()) {
79             return;
80         }
81         std::lock_guard<std::mutex> g{mMemoryMutex};
82         if (mMemoryLotsAvailable[lot] == false) {
83             mMemoryLotsAvailable[lot] = true;
84         } else {
85             ALOGE("Error, cannot twice");
86         }
87     }
88 
89   private:
90     std::mutex mMemoryMutex;
91     std::vector<bool> mMemoryLotsAvailable = std::vector<bool>(32, true);
92 
93     address_space_handle_t mHandle;
94     uint64_t mOffset;
95     uint64_t mPhysAddr;
96     uint64_t mSize;
97     void *mStartPtr = nullptr;
98 
99     // MediaCodecType will be or'd together with the metadata, so the highest
100     // 8-bits will have the type.
101     static __u64 makeMetadata(MediaCodecType type, MediaOperation op,
102                               uint64_t offset);
103 
104     // Chunk size for parameters/return data
105     static constexpr size_t kParamSizeBytes = 4096; // 4K
106     // Chunk size for input
107     static constexpr size_t kInputSizeBytes = 4096 * 4096; // 16M
108     // Chunk size for output
109     static constexpr size_t kOutputSizeBytes = 4096 * 4096; // 16M
110     // Maximum number of parameters that can be passed
111     static constexpr size_t kMaxParams = 32;
112     // Offset from the memory region for return data (8 is size of
113     // a parameter in bytes)
114     static constexpr size_t kReturnOffset = 8 * kMaxParams;
115 };
116 
~GoldfishMediaTransportImpl()117 GoldfishMediaTransportImpl::~GoldfishMediaTransportImpl() {
118     if (mHandle >= 0) {
119         goldfish_address_space_close(mHandle);
120         mHandle = -1;
121     }
122 }
123 
GoldfishMediaTransportImpl()124 GoldfishMediaTransportImpl::GoldfishMediaTransportImpl() {
125     // Allocate host memory; the contiguous memory region will be laid out as
126     // follows:
127     // ========================================================
128     // | kParamSizeBytes | kInputSizeBytes | kOutputSizeBytes |
129     // ========================================================
130     mHandle = goldfish_address_space_open();
131     if (mHandle < 0) {
132         ALOGE("Failed to ping host to allocate memory");
133         abort();
134     }
135     mSize = kParamSizeBytes + kInputSizeBytes + kOutputSizeBytes;
136     bool success =
137         goldfish_address_space_allocate(mHandle, mSize, &mPhysAddr, &mOffset);
138     if (success) {
139         ALOGI("successfully allocated %d bytes in goldfish_address_block",
140               (int)mSize);
141         mStartPtr = goldfish_address_space_map(mHandle, mOffset, mSize);
142         ALOGI("guest address is %p", mStartPtr);
143 
144         struct address_space_ping pingInfo;
145         pingInfo.metadata = GoldfishAddressSpaceSubdeviceType::Media;
146         pingInfo.offset = mOffset;
147         if (goldfish_address_space_ping(mHandle, &pingInfo) == false) {
148             ALOGE("Failed to ping host to allocate memory");
149             abort();
150             return;
151         } else {
152             ALOGI("successfully pinged host to allocate memory");
153         }
154     } else {
155         ALOGE("failed to allocate %d bytes in goldfish_address_block",
156               (int)mSize);
157         abort();
158     }
159 }
160 
161 // static
getInstance()162 GoldfishMediaTransport *GoldfishMediaTransport::getInstance() {
163     std::lock_guard<std::mutex> g{sSingletonMutex};
164     if (sTransport == nullptr) {
165         sTransport.reset(new GoldfishMediaTransportImpl());
166     }
167     return sTransport.get();
168 }
169 
170 // static
makeMetadata(MediaCodecType type,MediaOperation op,uint64_t offset)171 __u64 GoldfishMediaTransportImpl::makeMetadata(MediaCodecType type,
172                                                MediaOperation op,
173                                                uint64_t offset) {
174     // Shift |type| into the highest 8-bits, leaving the lower bits for other
175     // metadata.
176     offset = offset >> 20;
177     if (offset < 0 || offset >= 32) {
178         ALOGE("offset %d is wrong", (int)offset);
179         abort();
180     }
181     return ((__u64)type << (64 - 8)) | (offset << 8) | static_cast<uint8_t>(op);
182 }
183 
getInputAddr(unsigned int offSet) const184 uint8_t *GoldfishMediaTransportImpl::getInputAddr(unsigned int offSet) const {
185     return (uint8_t *)mStartPtr + kParamSizeBytes + offSet;
186 }
187 
getOutputAddr() const188 uint8_t *GoldfishMediaTransportImpl::getOutputAddr() const {
189     return getInputAddr() + kInputSizeBytes;
190 }
191 
getBaseAddr() const192 uint8_t *GoldfishMediaTransportImpl::getBaseAddr() const {
193     return (uint8_t *)mStartPtr;
194 }
195 
getReturnAddr(unsigned int offSet) const196 uint8_t *GoldfishMediaTransportImpl::getReturnAddr(unsigned int offSet) const {
197     return (uint8_t *)mStartPtr + kReturnOffset + offSet;
198 }
199 
offsetOf(uint64_t addr) const200 __u64 GoldfishMediaTransportImpl::offsetOf(uint64_t addr) const {
201     return addr - (uint64_t)mStartPtr;
202 }
203 
writeParam(__u64 val,unsigned int num,unsigned int offSetToStartAddr)204 void GoldfishMediaTransportImpl::writeParam(__u64 val, unsigned int num,
205                                             unsigned int offSetToStartAddr) {
206     uint8_t *p = (uint8_t *)mStartPtr + (offSetToStartAddr);
207     uint64_t *pint = (uint64_t *)(p + 8 * num);
208     *pint = val;
209 }
210 
sendOperation(MediaCodecType type,MediaOperation op,unsigned int offSetToStartAddr)211 bool GoldfishMediaTransportImpl::sendOperation(MediaCodecType type,
212                                                MediaOperation op,
213                                                unsigned int offSetToStartAddr) {
214     struct address_space_ping pingInfo;
215     pingInfo.metadata = makeMetadata(type, op, offSetToStartAddr);
216     pingInfo.offset = mOffset; // + (offSetToStartAddr);
217     if (goldfish_address_space_ping(mHandle, &pingInfo) == false) {
218         ALOGE("failed to ping host");
219         abort();
220         return false;
221     } else {
222         DDD("successfully pinged host for operation type=%d, op=%d", (int)type,
223             (int)op);
224     }
225 
226     return true;
227 }
228