|  | /* | 
|  | * 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 <utils/Log.h> | 
|  |  | 
|  | #include <inttypes.h> | 
|  | #include <stdint.h> | 
|  | #include <sys/types.h> | 
|  |  | 
|  | #include <binder/Parcel.h> | 
|  | #include <media/IMediaSource.h> | 
|  | #include <media/stagefright/MediaBuffer.h> | 
|  | #include <media/stagefright/MediaBufferGroup.h> | 
|  | #include <media/stagefright/MediaSource.h> | 
|  | #include <media/stagefright/MetaData.h> | 
|  |  | 
|  | namespace android { | 
|  |  | 
|  | enum { | 
|  | START = IBinder::FIRST_CALL_TRANSACTION, | 
|  | STOP, | 
|  | PAUSE, | 
|  | GETFORMAT, | 
|  | READ, | 
|  | RELEASE_BUFFER | 
|  | }; | 
|  |  | 
|  | enum { | 
|  | NULL_BUFFER, | 
|  | SHARED_BUFFER, | 
|  | INLINE_BUFFER | 
|  | }; | 
|  |  | 
|  | class RemoteMediaBufferReleaser : public BBinder { | 
|  | public: | 
|  | RemoteMediaBufferReleaser(MediaBuffer *buf, sp<BnMediaSource> owner) { | 
|  | mBuf = buf; | 
|  | mOwner = owner; | 
|  | } | 
|  | ~RemoteMediaBufferReleaser() { | 
|  | if (mBuf) { | 
|  | ALOGW("RemoteMediaBufferReleaser dtor called while still holding buffer"); | 
|  | mBuf->release(); | 
|  | } | 
|  | } | 
|  | virtual status_t    onTransact( uint32_t code, | 
|  | const Parcel& data, | 
|  | Parcel* reply, | 
|  | uint32_t flags = 0) { | 
|  | if (code == RELEASE_BUFFER) { | 
|  | mBuf->release(); | 
|  | mBuf = NULL; | 
|  | return OK; | 
|  | } else { | 
|  | return BBinder::onTransact(code, data, reply, flags); | 
|  | } | 
|  | } | 
|  | private: | 
|  | MediaBuffer *mBuf; | 
|  | // Keep a ref to ensure MediaBuffer is released before the owner, i.e., BnMediaSource, | 
|  | // because BnMediaSource needs to delete MediaBufferGroup in its dtor and | 
|  | // MediaBufferGroup dtor requires all MediaBuffer's have 0 ref count. | 
|  | sp<BnMediaSource> mOwner; | 
|  | }; | 
|  |  | 
|  |  | 
|  | class RemoteMediaBufferWrapper : public MediaBuffer { | 
|  | public: | 
|  | RemoteMediaBufferWrapper(sp<IMemory> mem, sp<IBinder> source); | 
|  | protected: | 
|  | virtual ~RemoteMediaBufferWrapper(); | 
|  | private: | 
|  | sp<IMemory> mMemory; | 
|  | sp<IBinder> mRemoteSource; | 
|  | }; | 
|  |  | 
|  | RemoteMediaBufferWrapper::RemoteMediaBufferWrapper(sp<IMemory> mem, sp<IBinder> source) | 
|  | : MediaBuffer(mem->pointer(), mem->size()) { | 
|  | mMemory = mem; | 
|  | mRemoteSource = source; | 
|  | } | 
|  |  | 
|  | RemoteMediaBufferWrapper::~RemoteMediaBufferWrapper() { | 
|  | mMemory.clear(); | 
|  | // Explicitly ask the remote side to release the buffer. We could also just clear | 
|  | // mRemoteSource, but that doesn't immediately release the reference on the remote side. | 
|  | Parcel data, reply; | 
|  | mRemoteSource->transact(RELEASE_BUFFER, data, &reply); | 
|  | mRemoteSource.clear(); | 
|  | } | 
|  |  | 
|  | class BpMediaSource : public BpInterface<IMediaSource> { | 
|  | public: | 
|  | BpMediaSource(const sp<IBinder>& impl) | 
|  | : BpInterface<IMediaSource>(impl) | 
|  | { | 
|  | } | 
|  |  | 
|  | 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<MetaData> meta = MetaData::createFromParcel(reply); | 
|  | ALOGW("output:"); | 
|  | meta->dumpToLog(); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | virtual status_t stop() { | 
|  | ALOGV("stop"); | 
|  | Parcel data, reply; | 
|  | data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor()); | 
|  | return remote()->transact(STOP, data, &reply); | 
|  | } | 
|  |  | 
|  | virtual sp<MetaData> 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(MediaBuffer **buffer, const ReadOptions *options) { | 
|  | ALOGV("read"); | 
|  | Parcel data, reply; | 
|  | data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor()); | 
|  | if (options) { | 
|  | data.writeByteArray(sizeof(*options), (uint8_t*) options); | 
|  | } | 
|  | status_t ret = remote()->transact(READ, data, &reply); | 
|  | if (ret != NO_ERROR) { | 
|  | return ret; | 
|  | } | 
|  | // wrap the returned data in a MediaBuffer | 
|  | ret = reply.readInt32(); | 
|  | int32_t buftype = reply.readInt32(); | 
|  | if (buftype == SHARED_BUFFER) { | 
|  | sp<IBinder> remote = reply.readStrongBinder(); | 
|  | sp<IBinder> binder = reply.readStrongBinder(); | 
|  | sp<IMemory> mem = interface_cast<IMemory>(binder); | 
|  | if (mem == NULL) { | 
|  | ALOGE("received NULL IMemory for shared buffer"); | 
|  | } | 
|  | size_t offset = reply.readInt32(); | 
|  | size_t length = reply.readInt32(); | 
|  | MediaBuffer *buf = new RemoteMediaBufferWrapper(mem, remote); | 
|  | buf->set_range(offset, length); | 
|  | buf->meta_data()->updateFromParcel(reply); | 
|  | *buffer = buf; | 
|  | } else if (buftype == NULL_BUFFER) { | 
|  | ALOGV("got status %d and NULL buffer", ret); | 
|  | *buffer = NULL; | 
|  | } else { | 
|  | int32_t len = reply.readInt32(); | 
|  | ALOGV("got status %d and len %d", ret, len); | 
|  | *buffer = new MediaBuffer(len); | 
|  | reply.read((*buffer)->data(), len); | 
|  | (*buffer)->meta_data()->updateFromParcel(reply); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | virtual status_t pause() { | 
|  | ALOGV("pause"); | 
|  | Parcel data, reply; | 
|  | data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor()); | 
|  | return remote()->transact(PAUSE, data, &reply); | 
|  | } | 
|  |  | 
|  | virtual status_t setBuffers(const Vector<MediaBuffer *> & buffers __unused) { | 
|  | ALOGV("setBuffers NOT IMPLEMENTED"); | 
|  | return ERROR_UNSUPPORTED; // default | 
|  | } | 
|  |  | 
|  | private: | 
|  | // 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<MetaData> mMetaData; | 
|  |  | 
|  | }; | 
|  |  | 
|  | IMPLEMENT_META_INTERFACE(MediaSource, "android.media.IMediaSource"); | 
|  |  | 
|  | #undef LOG_TAG | 
|  | #define LOG_TAG "BnMediaSource" | 
|  |  | 
|  | BnMediaSource::BnMediaSource() | 
|  | : mGroup(NULL) { | 
|  | } | 
|  |  | 
|  | BnMediaSource::~BnMediaSource() { | 
|  | delete mGroup; | 
|  | mGroup = NULL; | 
|  | } | 
|  |  | 
|  | 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<MetaData> 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); | 
|  | return stop(); | 
|  | } | 
|  | case PAUSE: { | 
|  | ALOGV("pause"); | 
|  | CHECK_INTERFACE(IMediaSource, data, reply); | 
|  | return pause(); | 
|  | } | 
|  | case GETFORMAT: { | 
|  | ALOGV("getFormat"); | 
|  | CHECK_INTERFACE(IMediaSource, data, reply); | 
|  | sp<MetaData> meta = getFormat(); | 
|  | if (meta != NULL) { | 
|  | meta->writeToParcel(*reply); | 
|  | return NO_ERROR; | 
|  | } | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  | case READ: { | 
|  | ALOGV("read"); | 
|  | CHECK_INTERFACE(IMediaSource, data, reply); | 
|  | status_t ret; | 
|  | MediaBuffer *buf = NULL; | 
|  | ReadOptions opts; | 
|  | uint32_t len; | 
|  | if (data.readUint32(&len) == NO_ERROR && | 
|  | len == sizeof(opts) && data.read((void*)&opts, len) == NO_ERROR) { | 
|  | ret = read(&buf, &opts); | 
|  | } else { | 
|  | ret = read(&buf, NULL); | 
|  | } | 
|  |  | 
|  | reply->writeInt32(ret); | 
|  | if (buf != NULL) { | 
|  | size_t usedSize = buf->range_length(); | 
|  | // 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. | 
|  | if (usedSize >= MediaBuffer::kSharedMemThreshold) { | 
|  | ALOGV("use shared memory: %zu", usedSize); | 
|  |  | 
|  | MediaBuffer *transferBuf = buf; | 
|  | size_t offset = buf->range_offset(); | 
|  | if (transferBuf->mMemory == NULL) { | 
|  | if (mGroup == NULL) { | 
|  | mGroup = new MediaBufferGroup; | 
|  | size_t allocateSize = usedSize; | 
|  | if (usedSize < SIZE_MAX / 3) { | 
|  | allocateSize = usedSize * 3 / 2; | 
|  | } | 
|  | mGroup->add_buffer(new MediaBuffer(allocateSize)); | 
|  | } | 
|  |  | 
|  | ret = mGroup->acquire_buffer( | 
|  | &transferBuf, false /* nonBlocking */, usedSize); | 
|  | if (ret != OK || transferBuf == NULL || transferBuf->mMemory == NULL) { | 
|  | ALOGW("failed to acquire shared memory, ret %d", ret); | 
|  | reply->writeInt32(NULL_BUFFER); | 
|  | return NO_ERROR; | 
|  | } | 
|  | memcpy(transferBuf->data(), (uint8_t*)buf->data() + buf->range_offset(), | 
|  | buf->range_length()); | 
|  | offset = 0; | 
|  | } | 
|  |  | 
|  | reply->writeInt32(SHARED_BUFFER); | 
|  | RemoteMediaBufferReleaser *wrapper = | 
|  | new RemoteMediaBufferReleaser(transferBuf, this); | 
|  | reply->writeStrongBinder(wrapper); | 
|  | reply->writeStrongBinder(IInterface::asBinder(transferBuf->mMemory)); | 
|  | reply->writeInt32(offset); | 
|  | reply->writeInt32(usedSize); | 
|  | buf->meta_data()->writeToParcel(*reply); | 
|  | if (buf->mMemory == NULL) { | 
|  | buf->release(); | 
|  | } | 
|  | } else { | 
|  | // buffer is small: copy it | 
|  | if (buf->mMemory != NULL) { | 
|  | ALOGV("%zu shared mem available, but only %zu used", buf->mMemory->size(), buf->range_length()); | 
|  | } | 
|  | reply->writeInt32(INLINE_BUFFER); | 
|  | reply->writeByteArray(buf->range_length(), (uint8_t*)buf->data() + buf->range_offset()); | 
|  | buf->meta_data()->writeToParcel(*reply); | 
|  | buf->release(); | 
|  | } | 
|  | } else { | 
|  | ALOGV("ret %d, buf %p", ret, buf); | 
|  | reply->writeInt32(NULL_BUFFER); | 
|  | } | 
|  | return NO_ERROR; | 
|  | } | 
|  | default: | 
|  | return BBinder::onTransact(code, data, reply, flags); | 
|  | } | 
|  | } | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | IMediaSource::ReadOptions::ReadOptions() { | 
|  | reset(); | 
|  | } | 
|  |  | 
|  | void IMediaSource::ReadOptions::reset() { | 
|  | mOptions = 0; | 
|  | mSeekTimeUs = 0; | 
|  | mLatenessUs = 0; | 
|  | mNonBlocking = false; | 
|  | } | 
|  |  | 
|  | void IMediaSource::ReadOptions::setNonBlocking() { | 
|  | mNonBlocking = true; | 
|  | } | 
|  |  | 
|  | void IMediaSource::ReadOptions::clearNonBlocking() { | 
|  | mNonBlocking = false; | 
|  | } | 
|  |  | 
|  | bool IMediaSource::ReadOptions::getNonBlocking() const { | 
|  | return mNonBlocking; | 
|  | } | 
|  |  | 
|  | void IMediaSource::ReadOptions::setSeekTo(int64_t time_us, SeekMode mode) { | 
|  | mOptions |= kSeekTo_Option; | 
|  | mSeekTimeUs = time_us; | 
|  | mSeekMode = mode; | 
|  | } | 
|  |  | 
|  | void IMediaSource::ReadOptions::clearSeekTo() { | 
|  | mOptions &= ~kSeekTo_Option; | 
|  | mSeekTimeUs = 0; | 
|  | mSeekMode = SEEK_CLOSEST_SYNC; | 
|  | } | 
|  |  | 
|  | bool IMediaSource::ReadOptions::getSeekTo( | 
|  | int64_t *time_us, SeekMode *mode) const { | 
|  | *time_us = mSeekTimeUs; | 
|  | *mode = mSeekMode; | 
|  | return (mOptions & kSeekTo_Option) != 0; | 
|  | } | 
|  |  | 
|  | void IMediaSource::ReadOptions::setLateBy(int64_t lateness_us) { | 
|  | mLatenessUs = lateness_us; | 
|  | } | 
|  |  | 
|  | int64_t IMediaSource::ReadOptions::getLateBy() const { | 
|  | return mLatenessUs; | 
|  | } | 
|  |  | 
|  |  | 
|  | }  // namespace android | 
|  |  |