IMediaSource.cpp revision b65990f4a0cf01db0b9f21c68fcf8824ae03a178
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17//#define LOG_NDEBUG 0 18#define LOG_TAG "BpMediaSource" 19#include <utils/Log.h> 20 21#include <inttypes.h> 22#include <stdint.h> 23#include <sys/types.h> 24 25#include <binder/Parcel.h> 26#include <media/IMediaSource.h> 27#include <media/stagefright/MediaBuffer.h> 28#include <media/stagefright/MediaSource.h> 29#include <media/stagefright/MetaData.h> 30 31namespace android { 32 33enum { 34 START = IBinder::FIRST_CALL_TRANSACTION, 35 STOP, 36 PAUSE, 37 GETFORMAT, 38 READ, 39 RELEASE_BUFFER 40}; 41 42enum { 43 NULL_BUFFER, 44 SHARED_BUFFER, 45 INLINE_BUFFER 46}; 47 48class RemoteMediaBufferReleaser : public BBinder { 49public: 50 RemoteMediaBufferReleaser(MediaBuffer *buf) { 51 mBuf = buf; 52 } 53 ~RemoteMediaBufferReleaser() { 54 if (mBuf) { 55 ALOGW("RemoteMediaBufferReleaser dtor called while still holding buffer"); 56 mBuf->release(); 57 } 58 } 59 virtual status_t onTransact( uint32_t code, 60 const Parcel& data, 61 Parcel* reply, 62 uint32_t flags = 0) { 63 if (code == RELEASE_BUFFER) { 64 mBuf->release(); 65 mBuf = NULL; 66 return OK; 67 } else { 68 return BBinder::onTransact(code, data, reply, flags); 69 } 70 } 71private: 72 MediaBuffer *mBuf; 73}; 74 75 76class RemoteMediaBufferWrapper : public MediaBuffer { 77public: 78 RemoteMediaBufferWrapper(sp<IMemory> mem, sp<IBinder> source); 79protected: 80 virtual ~RemoteMediaBufferWrapper(); 81private: 82 sp<IMemory> mMemory; 83 sp<IBinder> mRemoteSource; 84}; 85 86RemoteMediaBufferWrapper::RemoteMediaBufferWrapper(sp<IMemory> mem, sp<IBinder> source) 87: MediaBuffer(mem->pointer(), mem->size()) { 88 mMemory = mem; 89 mRemoteSource = source; 90} 91 92RemoteMediaBufferWrapper::~RemoteMediaBufferWrapper() { 93 mMemory.clear(); 94 // Explicitly ask the remote side to release the buffer. We could also just clear 95 // mRemoteSource, but that doesn't immediately release the reference on the remote side. 96 Parcel data, reply; 97 mRemoteSource->transact(RELEASE_BUFFER, data, &reply); 98 mRemoteSource.clear(); 99} 100 101class BpMediaSource : public BpInterface<IMediaSource> { 102public: 103 BpMediaSource(const sp<IBinder>& impl) 104 : BpInterface<IMediaSource>(impl) 105 { 106 } 107 108 virtual status_t start(MetaData *params) { 109 ALOGV("start"); 110 Parcel data, reply; 111 data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor()); 112 if (params) { 113 params->writeToParcel(data); 114 } 115 status_t ret = remote()->transact(START, data, &reply); 116 if (ret == NO_ERROR && params) { 117 ALOGW("ignoring potentially modified MetaData from start"); 118 ALOGW("input:"); 119 params->dumpToLog(); 120 sp<MetaData> meta = MetaData::createFromParcel(reply); 121 ALOGW("output:"); 122 meta->dumpToLog(); 123 } 124 return ret; 125 } 126 127 virtual status_t stop() { 128 ALOGV("stop"); 129 Parcel data, reply; 130 data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor()); 131 return remote()->transact(STOP, data, &reply); 132 } 133 134 virtual sp<MetaData> getFormat() { 135 ALOGV("getFormat"); 136 Parcel data, reply; 137 data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor()); 138 status_t ret = remote()->transact(GETFORMAT, data, &reply); 139 if (ret == NO_ERROR) { 140 mMetaData = MetaData::createFromParcel(reply); 141 return mMetaData; 142 } 143 return NULL; 144 } 145 146 virtual status_t read(MediaBuffer **buffer, const ReadOptions *options) { 147 ALOGV("read"); 148 Parcel data, reply; 149 data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor()); 150 if (options) { 151 data.writeByteArray(sizeof(*options), (uint8_t*) options); 152 } 153 status_t ret = remote()->transact(READ, data, &reply); 154 if (ret != NO_ERROR) { 155 return ret; 156 } 157 // wrap the returned data in a MediaBuffer 158 ret = reply.readInt32(); 159 int32_t buftype = reply.readInt32(); 160 if (buftype == SHARED_BUFFER) { 161 sp<IBinder> remote = reply.readStrongBinder(); 162 sp<IBinder> binder = reply.readStrongBinder(); 163 sp<IMemory> mem = interface_cast<IMemory>(binder); 164 if (mem == NULL) { 165 ALOGE("received NULL IMemory for shared buffer"); 166 } 167 size_t offset = reply.readInt32(); 168 size_t length = reply.readInt32(); 169 MediaBuffer *buf = new RemoteMediaBufferWrapper(mem, remote); 170 buf->set_range(offset, length); 171 buf->meta_data()->updateFromParcel(reply); 172 *buffer = buf; 173 } else if (buftype == NULL_BUFFER) { 174 ALOGV("got status %d and NULL buffer", ret); 175 *buffer = NULL; 176 } else { 177 int32_t len = reply.readInt32(); 178 ALOGV("got status %d and len %d", ret, len); 179 *buffer = new MediaBuffer(len); 180 reply.read((*buffer)->data(), len); 181 (*buffer)->meta_data()->updateFromParcel(reply); 182 } 183 return ret; 184 } 185 186 virtual status_t pause() { 187 ALOGV("pause"); 188 Parcel data, reply; 189 data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor()); 190 return remote()->transact(PAUSE, data, &reply); 191 } 192 193 virtual status_t setBuffers(const Vector<MediaBuffer *> & buffers __unused) { 194 ALOGV("setBuffers NOT IMPLEMENTED"); 195 return ERROR_UNSUPPORTED; // default 196 } 197 198private: 199 // NuPlayer passes pointers-to-metadata around, so we use this to keep the metadata alive 200 // XXX: could we use this for caching, or does metadata change on the fly? 201 sp<MetaData> mMetaData; 202 203}; 204 205IMPLEMENT_META_INTERFACE(MediaSource, "android.media.IMediaSource"); 206 207#undef LOG_TAG 208#define LOG_TAG "BnMediaSource" 209 210status_t BnMediaSource::onTransact( 211 uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) 212{ 213 switch (code) { 214 case START: { 215 ALOGV("start"); 216 CHECK_INTERFACE(IMediaSource, data, reply); 217 sp<MetaData> meta; 218 if (data.dataAvail()) { 219 meta = MetaData::createFromParcel(data); 220 } 221 status_t ret = start(meta.get()); 222 if (ret == NO_ERROR && meta != NULL) { 223 meta->writeToParcel(*reply); 224 } 225 return ret; 226 } 227 case STOP: { 228 ALOGV("stop"); 229 CHECK_INTERFACE(IMediaSource, data, reply); 230 return stop(); 231 } 232 case PAUSE: { 233 ALOGV("pause"); 234 CHECK_INTERFACE(IMediaSource, data, reply); 235 return pause(); 236 } 237 case GETFORMAT: { 238 ALOGV("getFormat"); 239 CHECK_INTERFACE(IMediaSource, data, reply); 240 sp<MetaData> meta = getFormat(); 241 if (meta != NULL) { 242 meta->writeToParcel(*reply); 243 return NO_ERROR; 244 } 245 return UNKNOWN_ERROR; 246 } 247 case READ: { 248 ALOGV("read"); 249 CHECK_INTERFACE(IMediaSource, data, reply); 250 status_t ret; 251 MediaBuffer *buf = NULL; 252 ReadOptions opts; 253 uint32_t len; 254 if (data.readUint32(&len) == NO_ERROR && 255 len == sizeof(opts) && data.read((void*)&opts, len) == NO_ERROR) { 256 ret = read(&buf, &opts); 257 } else { 258 ret = read(&buf, NULL); 259 } 260 261 reply->writeInt32(ret); 262 if (buf != NULL) { 263 size_t usedSize = buf->range_length(); 264 // even if we're using shared memory, we might not want to use it, since for small 265 // sizes it's faster to copy data through the Binder transaction 266 if (buf->mMemory != NULL && usedSize >= 64 * 1024) { 267 ALOGV("buffer is using shared memory: %zu", usedSize); 268 reply->writeInt32(SHARED_BUFFER); 269 RemoteMediaBufferReleaser *wrapper = new RemoteMediaBufferReleaser(buf); 270 reply->writeStrongBinder(wrapper); 271 reply->writeStrongBinder(IInterface::asBinder(buf->mMemory)); 272 reply->writeInt32(buf->range_offset()); 273 reply->writeInt32(usedSize); 274 buf->meta_data()->writeToParcel(*reply); 275 } else { 276 // buffer is not in shared memory, or is small: copy it 277 if (buf->mMemory != NULL) { 278 ALOGV("%zu shared mem available, but only %zu used", buf->mMemory->size(), buf->range_length()); 279 } 280 reply->writeInt32(INLINE_BUFFER); 281 reply->writeByteArray(buf->range_length(), (uint8_t*)buf->data() + buf->range_offset()); 282 buf->meta_data()->writeToParcel(*reply); 283 buf->release(); 284 } 285 } else { 286 ALOGV("ret %d, buf %p", ret, buf); 287 reply->writeInt32(NULL_BUFFER); 288 } 289 return NO_ERROR; 290 } 291 default: 292 return BBinder::onTransact(code, data, reply, flags); 293 } 294} 295 296//////////////////////////////////////////////////////////////////////////////// 297 298IMediaSource::ReadOptions::ReadOptions() { 299 reset(); 300} 301 302void IMediaSource::ReadOptions::reset() { 303 mOptions = 0; 304 mSeekTimeUs = 0; 305 mLatenessUs = 0; 306 mNonBlocking = false; 307} 308 309void IMediaSource::ReadOptions::setNonBlocking() { 310 mNonBlocking = true; 311} 312 313void IMediaSource::ReadOptions::clearNonBlocking() { 314 mNonBlocking = false; 315} 316 317bool IMediaSource::ReadOptions::getNonBlocking() const { 318 return mNonBlocking; 319} 320 321void IMediaSource::ReadOptions::setSeekTo(int64_t time_us, SeekMode mode) { 322 mOptions |= kSeekTo_Option; 323 mSeekTimeUs = time_us; 324 mSeekMode = mode; 325} 326 327void IMediaSource::ReadOptions::clearSeekTo() { 328 mOptions &= ~kSeekTo_Option; 329 mSeekTimeUs = 0; 330 mSeekMode = SEEK_CLOSEST_SYNC; 331} 332 333bool IMediaSource::ReadOptions::getSeekTo( 334 int64_t *time_us, SeekMode *mode) const { 335 *time_us = mSeekTimeUs; 336 *mode = mSeekMode; 337 return (mOptions & kSeekTo_Option) != 0; 338} 339 340void IMediaSource::ReadOptions::setLateBy(int64_t lateness_us) { 341 mLatenessUs = lateness_us; 342} 343 344int64_t IMediaSource::ReadOptions::getLateBy() const { 345 return mLatenessUs; 346} 347 348 349} // namespace android 350 351