// Copyright (C) 2019 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 "host-common/MediaVpxDecoder.h" #include "aemu/base/system/System.h" #include "host-common/MediaVpxDecoderGeneric.h" #include "host-common/VpxPingInfoParser.h" #include #include #include #include #include #define MEDIA_VPX_DEBUG 0 #if MEDIA_VPX_DEBUG #define VPX_DPRINT(fmt, ...) \ fprintf(stderr, "vpx-dec: %s:%d " fmt "\n", __func__, __LINE__, \ ##__VA_ARGS__); #else #define VPX_DPRINT(fmt,...) #endif namespace android { namespace emulation { namespace { class MediaVpxDecoderImpl : MediaVpxDecoder { public: MediaVpxDecoderImpl() = default; virtual ~MediaVpxDecoder() = default; void handlePing(MediaCodecType type, MediaOperation op, void* ptr) override; public: virtual void save(base::Stream* stream) const override; virtual bool load(base::Stream* stream) override; private: std::mutex mMapLock{}; uint64_t mId = 0; std::unordered_map mDecoders; uint64_t readId(void* ptr); // read id from the address void removeDecoder(uint64_t id); void addDecoder(uint64_t key, MediaVpxDecoderPlugin* val); // this just add void updateDecoder(uint64_t key, MediaVpxDecoderPlugin* val); // this will overwrite MediaVpxDecoderPlugin* getDecoder(uint64_t key); }; MediaVpxDecoderPlugin* makeDecoderPlugin(uint64_t pluginid, VpxPingInfoParser parser, MediaCodecType type) { return new MediaVpxDecoderGeneric(parser, type); } }; // namespace uint64_t MediaVpxDecoderImpl::readId(void* ptr) { if (nullptr == ptr) return 0; uint64_t key = VpxPingInfoParser::parseId(ptr); return key; } MediaVpxDecoderPlugin* MediaVpxDecoderImpl::getDecoder(uint64_t key) { { std::lock_guard g(mMapLock); auto iter = mDecoders.find(key); if (iter != mDecoders.end()) { return iter->second; } } VPX_DPRINT("Error: cannot find decoder with key %" PRIx64 "", key); return nullptr; } void MediaVpxDecoderImpl::addDecoder(uint64_t key, MediaVpxDecoderPlugin* val) { { std::lock_guard g(mMapLock); if (mDecoders.find(key) == mDecoders.end()) { mDecoders[key] = val; VPX_DPRINT("added decoder key %" PRIx64 " val: %p", key, val); return; } } VPX_DPRINT("cannot add: already exist"); } void MediaVpxDecoderImpl::removeDecoder(uint64_t key) { { std::lock_guard g(mMapLock); auto iter = mDecoders.find(key); if (iter != mDecoders.end()) { VPX_DPRINT("removed decoder key %" PRIx64 ", val: %p", key, mDecoders[key]); mDecoders.erase(iter); return; } } VPX_DPRINT("error: cannot remove decoder, not in map"); } void MediaVpxDecoderImpl::handlePing(MediaCodecType type, MediaOperation op, void* ptr) { using InitContextParam = VpxPingInfoParser::InitContextParam; using DecodeFrameParam = VpxPingInfoParser::DecodeFrameParam; using GetImageParam = VpxPingInfoParser::GetImageParam; switch (op) { case MediaOperation::InitContext: { VpxPingInfoParser parser{ptr}; InitContextParam param{}; parser.parseInitContextParams(ptr, param); VPX_DPRINT( "handle init decoder context request from guest version %u", parser.version()); uint64_t myid = readId(ptr); MediaVpxDecoderPlugin* mydecoder = makeDecoderPlugin(myid, parser, type); addDecoder(myid, mydecoder); mydecoder->initVpxContext(ptr); VPX_DPRINT("done handling InitContext"); break; } case MediaOperation::DestroyContext: { VPX_DPRINT("handle destroy request from guest %p", ptr); MediaVpxDecoderPlugin* mydecoder = getDecoder(readId(ptr)); if (!mydecoder) return; mydecoder->destroyVpxContext(ptr); delete mydecoder; removeDecoder(readId(ptr)); break; } case MediaOperation::DecodeImage: { VPX_DPRINT("handle decodeimage request from guest %p", ptr); MediaVpxDecoderPlugin* mydecoder = getDecoder(readId(ptr)); if (nullptr == mydecoder) return; mydecoder->decodeFrame(ptr); break; } case MediaOperation::Flush: { VPX_DPRINT("handle flush request from guest %p", ptr); MediaVpxDecoderPlugin* mydecoder = getDecoder(readId(ptr)); if (nullptr == mydecoder) return; mydecoder->flush(ptr); break; } case MediaOperation::GetImage: { VPX_DPRINT("handle getimage request from guest %p", ptr); MediaVpxDecoderPlugin* mydecoder = getDecoder(readId(ptr)); if (nullptr == mydecoder) return; mydecoder->getImage(ptr); break; } case MediaOperation::Reset: { VPX_DPRINT("Reset is not supported %u\n", (unsigned int)op); } default: VPX_DPRINT("Unknown command %u\n", (unsigned int)op); break; } } void MediaVpxDecoderImpl::save(base::Stream* stream) const { int size = mDecoders.size(); stream->putBe32(size); for (auto item : mDecoders) { stream->putBe64(item.first); stream->putBe32(item.second->type()); stream->putBe32(item.second->vpxtype()); stream->putBe32(item.second->version()); item.second->save(stream); } } bool MediaVpxDecoderImpl::load(base::Stream* stream) { VPX_DPRINT("loading .."); int size = stream->getBe32(); for (int i = 0; i < size; ++i) { // this is hacky; but we have to know the plugin type uint64_t id = stream->getBe64(); int type = stream->getBe32(); MediaCodecType vpxtype = stream->getBe32() == 8 ? MediaCodecType::VP8Codec : MediaCodecType::VP9Codec; int version = stream->getBe32(); if (type == MediaVpxDecoderPlugin::PLUGIN_TYPE_GENERIC) { // libvpx MediaVpxDecoderGeneric* decoder = new MediaVpxDecoderGeneric( VpxPingInfoParser(version), vpxtype); mDecoders[id] = decoder; decoder->load(stream); continue; } fprintf(stderr, "Error, un-implemented %s %d\n", __func__, __LINE__); exit(1); } return true; } // static MediaVpxDecoder* MediaVpxDecoder::create() { return new MediaVpxDecoderImpl(); } } // namespace emulation } // namespace android