/* * Copyright (C) 2009 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 "BpMediaSource" #include #include #include #include #include #include #include #include #include #include namespace android { enum { START = IBinder::FIRST_CALL_TRANSACTION, STOP, PAUSE, GETFORMAT, // READ, deprecated READMULTIPLE, RELEASE_BUFFER, SUPPORT_NONBLOCKING_READ, }; enum { NULL_BUFFER, SHARED_BUFFER, INLINE_BUFFER, SHARED_BUFFER_INDEX, }; class RemoteMediaBufferWrapper : public MediaBuffer { public: RemoteMediaBufferWrapper(const sp &mem) : MediaBuffer(mem) { ALOGV("RemoteMediaBufferWrapper: creating %p", this); } protected: virtual ~RemoteMediaBufferWrapper() { // Release our interest in the MediaBuffer's shared memory. int32_t old = addRemoteRefcount(-1); ALOGV("RemoteMediaBufferWrapper: releasing %p, refcount %d", this, old - 1); mMemory.clear(); // don't set the dead object flag. } }; class BpMediaSource : public BpInterface { public: explicit BpMediaSource(const sp& impl) : BpInterface(impl), mBuffersSinceStop(0) { } virtual status_t start(MetaData *params) { ALOGV("start"); Parcel data, reply; data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor()); if (params) { params->writeToParcel(data); } status_t ret = remote()->transact(START, data, &reply); if (ret == NO_ERROR && params) { ALOGW("ignoring potentially modified MetaData from start"); ALOGW("input:"); params->dumpToLog(); sp meta = MetaData::createFromParcel(reply); ALOGW("output:"); meta->dumpToLog(); } return ret; } virtual status_t stop() { ALOGV("stop"); Parcel data, reply; data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor()); status_t status = remote()->transact(STOP, data, &reply); mMemoryCache.reset(); mBuffersSinceStop = 0; return status; } virtual sp getFormat() { ALOGV("getFormat"); Parcel data, reply; data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor()); status_t ret = remote()->transact(GETFORMAT, data, &reply); if (ret == NO_ERROR) { mMetaData = MetaData::createFromParcel(reply); return mMetaData; } return NULL; } virtual status_t read(MediaBufferBase **buffer, const MediaSource::ReadOptions *options) { Vector buffers; status_t ret = readMultiple(&buffers, 1 /* maxNumBuffers */, options); *buffer = buffers.size() == 0 ? nullptr : buffers[0]; ALOGV("read status %d, bufferCount %u, sinceStop %u", ret, *buffer != nullptr, mBuffersSinceStop); return ret; } virtual status_t readMultiple( Vector *buffers, uint32_t maxNumBuffers, const MediaSource::ReadOptions *options) { ALOGV("readMultiple"); if (buffers == NULL || !buffers->isEmpty()) { return BAD_VALUE; } Parcel data, reply; data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor()); data.writeUint32(maxNumBuffers); if (options != nullptr) { data.writeByteArray(sizeof(*options), (uint8_t*) options); } status_t ret = remote()->transact(READMULTIPLE, data, &reply); mMemoryCache.gc(); if (ret != NO_ERROR) { return ret; } // wrap the returned data in a vector of MediaBuffers int32_t buftype; uint32_t bufferCount = 0; while ((buftype = reply.readInt32()) != NULL_BUFFER) { LOG_ALWAYS_FATAL_IF(bufferCount >= maxNumBuffers, "Received %u+ buffers and requested %u buffers", bufferCount + 1, maxNumBuffers); MediaBuffer *buf; if (buftype == SHARED_BUFFER || buftype == SHARED_BUFFER_INDEX) { uint64_t index = reply.readUint64(); ALOGV("Received %s index %llu", buftype == SHARED_BUFFER ? "SHARED_BUFFER" : "SHARED_BUFFER_INDEX", (unsigned long long) index); sp mem; if (buftype == SHARED_BUFFER) { sp binder = reply.readStrongBinder(); mem = interface_cast(binder); LOG_ALWAYS_FATAL_IF(mem.get() == nullptr, "Received NULL IMemory for shared buffer"); mMemoryCache.insert(index, mem); } else { mem = mMemoryCache.lookup(index); LOG_ALWAYS_FATAL_IF(mem.get() == nullptr, "Received invalid IMemory index for shared buffer: %llu", (unsigned long long)index); } size_t offset = reply.readInt32(); size_t length = reply.readInt32(); buf = new RemoteMediaBufferWrapper(mem); buf->set_range(offset, length); buf->meta_data().updateFromParcel(reply); } else { // INLINE_BUFFER int32_t len = reply.readInt32(); ALOGV("INLINE_BUFFER status %d and len %d", ret, len); buf = new MediaBuffer(len); reply.read(buf->data(), len); buf->meta_data().updateFromParcel(reply); } buffers->push_back(buf); ++bufferCount; ++mBuffersSinceStop; } ret = reply.readInt32(); ALOGV("readMultiple status %d, bufferCount %u, sinceStop %u", ret, bufferCount, mBuffersSinceStop); return ret; } // Binder proxy adds readMultiple support. virtual bool supportReadMultiple() { return true; } virtual bool supportNonblockingRead() { ALOGV("supportNonblockingRead"); Parcel data, reply; data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor()); status_t ret = remote()->transact(SUPPORT_NONBLOCKING_READ, data, &reply); if (ret == NO_ERROR) { return reply.readInt32() != 0; } return false; } virtual status_t pause() { ALOGV("pause"); Parcel data, reply; data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor()); return remote()->transact(PAUSE, data, &reply); } private: uint32_t mBuffersSinceStop; // Buffer tracking variable // NuPlayer passes pointers-to-metadata around, so we use this to keep the metadata alive // XXX: could we use this for caching, or does metadata change on the fly? sp mMetaData; // Cache all IMemory objects received from MediaExtractor. // We gc IMemory objects that are no longer active (referenced by a MediaBuffer). struct MemoryCache { sp lookup(uint64_t index) { auto p = mIndexToMemory.find(index); if (p == mIndexToMemory.end()) { ALOGE("cannot find index!"); return nullptr; } return p->second; } void insert(uint64_t index, const sp &mem) { if (mIndexToMemory.find(index) != mIndexToMemory.end()) { ALOGE("index %llu already present", (unsigned long long)index); return; } (void)mIndexToMemory.emplace(index, mem); } void reset() { mIndexToMemory.clear(); } void gc() { for (auto it = mIndexToMemory.begin(); it != mIndexToMemory.end(); ) { if (MediaBuffer::isDeadObject(it->second)) { it = mIndexToMemory.erase(it); } else { ++it; } } } private: // C++14 unordered_map erase on iterator is stable; C++11 has no guarantee. std::map> mIndexToMemory; } mMemoryCache; }; IMPLEMENT_META_INTERFACE(MediaSource, "android.media.IMediaSource"); #undef LOG_TAG #define LOG_TAG "BnMediaSource" BnMediaSource::BnMediaSource() : mBuffersSinceStop(0) , mGroup(new MediaBufferGroup(kBinderMediaBuffers /* growthLimit */)) { } BnMediaSource::~BnMediaSource() { } status_t BnMediaSource::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch (code) { case START: { ALOGV("start"); CHECK_INTERFACE(IMediaSource, data, reply); sp meta; if (data.dataAvail()) { meta = MetaData::createFromParcel(data); } status_t ret = start(meta.get()); if (ret == NO_ERROR && meta != NULL) { meta->writeToParcel(*reply); } return ret; } case STOP: { ALOGV("stop"); CHECK_INTERFACE(IMediaSource, data, reply); mGroup->signalBufferReturned(nullptr); status_t status = stop(); mIndexCache.reset(); mBuffersSinceStop = 0; return status; } case PAUSE: { ALOGV("pause"); CHECK_INTERFACE(IMediaSource, data, reply); mGroup->signalBufferReturned(nullptr); return pause(); } case GETFORMAT: { ALOGV("getFormat"); CHECK_INTERFACE(IMediaSource, data, reply); sp meta = getFormat(); if (meta != NULL) { meta->writeToParcel(*reply); return NO_ERROR; } return UNKNOWN_ERROR; } case READMULTIPLE: { ALOGV("readMultiple"); CHECK_INTERFACE(IMediaSource, data, reply); // Get max number of buffers to read. uint32_t maxNumBuffers; data.readUint32(&maxNumBuffers); if (maxNumBuffers > kMaxNumReadMultiple) { maxNumBuffers = kMaxNumReadMultiple; } // Get read options, if any. MediaSource::ReadOptions opts; uint32_t len; const bool useOptions = data.readUint32(&len) == NO_ERROR && len == sizeof(opts) && data.read((void *)&opts, len) == NO_ERROR; mGroup->signalBufferReturned(nullptr); mIndexCache.gc(); size_t inlineTransferSize = 0; status_t ret = NO_ERROR; uint32_t bufferCount = 0; for (; bufferCount < maxNumBuffers; ++bufferCount, ++mBuffersSinceStop) { MediaBuffer *buf = nullptr; ret = read((MediaBufferBase **)&buf, useOptions ? &opts : nullptr); opts.clearNonPersistent(); // Remove options that only apply to first buffer. if (ret != NO_ERROR || buf == nullptr) { break; } // Even if we're using shared memory, we might not want to use it, since for small // sizes it's faster to copy data through the Binder transaction // On the other hand, if the data size is large enough, it's better to use shared // memory. When data is too large, binder can't handle it. // // TODO: reduce MediaBuffer::kSharedMemThreshold MediaBuffer *transferBuf = nullptr; const size_t length = buf->range_length(); size_t offset = buf->range_offset(); if (length >= (supportNonblockingRead() && buf->mMemory != nullptr ? kTransferSharedAsSharedThreshold : kTransferInlineAsSharedThreshold)) { if (buf->mMemory != nullptr) { ALOGV("Use shared memory: %zu", length); transferBuf = buf; } else { ALOGD("Large buffer %zu without IMemory!", length); ret = mGroup->acquire_buffer( (MediaBufferBase **)&transferBuf, false /* nonBlocking */, length); if (ret != OK || transferBuf == nullptr || transferBuf->mMemory == nullptr) { ALOGW("Failed to acquire shared memory, size %zu, ret %d", length, ret); if (transferBuf != nullptr) { transferBuf->release(); transferBuf = nullptr; } // Current buffer transmit inline; no more additional buffers. maxNumBuffers = 0; } else { memcpy(transferBuf->data(), (uint8_t*)buf->data() + offset, length); offset = 0; if (!mGroup->has_buffers()) { maxNumBuffers = 0; // No more MediaBuffers, stop readMultiple. } } } } if (transferBuf != nullptr) { // Using shared buffers. if (!transferBuf->isObserved() && transferBuf != buf) { // Transfer buffer must be part of a MediaBufferGroup. ALOGV("adding shared memory buffer %p to local group", transferBuf); mGroup->add_buffer(transferBuf); transferBuf->add_ref(); // We have already acquired buffer. } uint64_t index = mIndexCache.lookup(transferBuf->mMemory); if (index == 0) { index = mIndexCache.insert(transferBuf->mMemory); reply->writeInt32(SHARED_BUFFER); reply->writeUint64(index); reply->writeStrongBinder(IInterface::asBinder(transferBuf->mMemory)); ALOGV("SHARED_BUFFER(%p) %llu", transferBuf, (unsigned long long)index); } else { reply->writeInt32(SHARED_BUFFER_INDEX); reply->writeUint64(index); ALOGV("SHARED_BUFFER_INDEX(%p) %llu", transferBuf, (unsigned long long)index); } reply->writeInt32(offset); reply->writeInt32(length); buf->meta_data().writeToParcel(*reply); transferBuf->addRemoteRefcount(1); if (transferBuf != buf) { transferBuf->release(); // release local ref } else if (!supportNonblockingRead()) { maxNumBuffers = 0; // stop readMultiple with one shared buffer. } } else { ALOGV_IF(buf->mMemory != nullptr, "INLINE(%p) %zu shared mem available, but only %zu used", buf, buf->mMemory->size(), length); reply->writeInt32(INLINE_BUFFER); reply->writeByteArray(length, (uint8_t*)buf->data() + offset); buf->meta_data().writeToParcel(*reply); inlineTransferSize += length; if (inlineTransferSize > kInlineMaxTransfer) { maxNumBuffers = 0; // stop readMultiple if inline transfer is too large. } } buf->release(); } reply->writeInt32(NULL_BUFFER); // Indicate no more MediaBuffers. reply->writeInt32(ret); ALOGV("readMultiple status %d, bufferCount %u, sinceStop %u", ret, bufferCount, mBuffersSinceStop); return NO_ERROR; } case SUPPORT_NONBLOCKING_READ: { ALOGV("supportNonblockingRead"); CHECK_INTERFACE(IMediaSource, data, reply); reply->writeInt32((int32_t)supportNonblockingRead()); return NO_ERROR; } default: return BBinder::onTransact(code, data, reply, flags); } } } // namespace android