IMediaSource.cpp revision 91afc2297dd1c39302dba6ff68e9839aae27d39a
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 <utils/CallStack.h>
22
23#include <inttypes.h>
24#include <stdint.h>
25#include <sys/types.h>
26
27#include <binder/Parcel.h>
28#include <media/IMediaSource.h>
29#include <media/stagefright/MediaBuffer.h>
30#include <media/stagefright/MediaBufferGroup.h>
31#include <media/stagefright/MediaSource.h>
32#include <media/stagefright/MetaData.h>
33
34namespace android {
35
36enum {
37    START = IBinder::FIRST_CALL_TRANSACTION,
38    STOP,
39    PAUSE,
40    GETFORMAT,
41    READ,
42    RELEASE_BUFFER
43};
44
45enum {
46    NULL_BUFFER,
47    SHARED_BUFFER,
48    INLINE_BUFFER
49};
50
51class RemoteMediaBufferReleaser : public BBinder {
52public:
53    RemoteMediaBufferReleaser(MediaBuffer *buf, sp<BnMediaSource> owner) {
54        mBuf = buf;
55        mOwner = owner;
56    }
57    ~RemoteMediaBufferReleaser() {
58        if (mBuf) {
59            ALOGW("RemoteMediaBufferReleaser dtor called while still holding buffer");
60            mBuf->release();
61        }
62    }
63    virtual status_t    onTransact( uint32_t code,
64                                    const Parcel& data,
65                                    Parcel* reply,
66                                    uint32_t flags = 0) {
67        if (code == RELEASE_BUFFER) {
68            mBuf->release();
69            mBuf = NULL;
70            return OK;
71        } else {
72            return BBinder::onTransact(code, data, reply, flags);
73        }
74    }
75private:
76    MediaBuffer *mBuf;
77    // Keep a ref to ensure MediaBuffer is released before the owner, i.e., BnMediaSource,
78    // because BnMediaSource needs to delete MediaBufferGroup in its dtor and
79    // MediaBufferGroup dtor requires all MediaBuffer's have 0 ref count.
80    sp<BnMediaSource> mOwner;
81};
82
83
84class RemoteMediaBufferWrapper : public MediaBuffer {
85public:
86    RemoteMediaBufferWrapper(sp<IMemory> mem, sp<IBinder> source);
87protected:
88    virtual ~RemoteMediaBufferWrapper();
89private:
90    sp<IMemory> mMemory;
91    sp<IBinder> mRemoteSource;
92};
93
94RemoteMediaBufferWrapper::RemoteMediaBufferWrapper(sp<IMemory> mem, sp<IBinder> source)
95: MediaBuffer(mem->pointer(), mem->size()) {
96    mMemory = mem;
97    mRemoteSource = source;
98}
99
100RemoteMediaBufferWrapper::~RemoteMediaBufferWrapper() {
101    mMemory.clear();
102    // Explicitly ask the remote side to release the buffer. We could also just clear
103    // mRemoteSource, but that doesn't immediately release the reference on the remote side.
104    Parcel data, reply;
105    mRemoteSource->transact(RELEASE_BUFFER, data, &reply);
106    mRemoteSource.clear();
107}
108
109class BpMediaSource : public BpInterface<IMediaSource> {
110public:
111    BpMediaSource(const sp<IBinder>& impl)
112        : BpInterface<IMediaSource>(impl)
113    {
114        mStarted = false;
115    }
116
117    virtual status_t start(MetaData *params) {
118        if (mStarted) {
119            ALOGD("Source was started previously from:");
120            mStartStack.log(LOG_TAG);
121            ALOGD("Now from:");
122            CallStack stack(LOG_TAG);
123        }
124        ALOGV("start");
125        Parcel data, reply;
126        data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor());
127        if (params) {
128            params->writeToParcel(data);
129        }
130        status_t ret = remote()->transact(START, data, &reply);
131        if (ret == NO_ERROR) {
132            mStarted = true;
133            mStartStack.update();
134        }
135        if (ret == NO_ERROR && params) {
136            ALOGW("ignoring potentially modified MetaData from start");
137            ALOGW("input:");
138            params->dumpToLog();
139            sp<MetaData> meta = MetaData::createFromParcel(reply);
140            ALOGW("output:");
141            meta->dumpToLog();
142        }
143        return ret;
144    }
145
146    virtual status_t stop() {
147        mStarted = false;
148        ALOGV("stop");
149        Parcel data, reply;
150        data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor());
151        return remote()->transact(STOP, data, &reply);
152    }
153
154    virtual sp<MetaData> getFormat() {
155        ALOGV("getFormat");
156        Parcel data, reply;
157        data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor());
158        status_t ret = remote()->transact(GETFORMAT, data, &reply);
159        if (ret == NO_ERROR) {
160            mMetaData = MetaData::createFromParcel(reply);
161            return mMetaData;
162        }
163        return NULL;
164    }
165
166    virtual status_t read(MediaBuffer **buffer, const ReadOptions *options) {
167        ALOGV("read");
168        Parcel data, reply;
169        data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor());
170        if (options) {
171            data.writeByteArray(sizeof(*options), (uint8_t*) options);
172        }
173        status_t ret = remote()->transact(READ, data, &reply);
174        if (ret != NO_ERROR) {
175            return ret;
176        }
177        // wrap the returned data in a MediaBuffer
178        ret = reply.readInt32();
179        int32_t buftype = reply.readInt32();
180        if (buftype == SHARED_BUFFER) {
181            sp<IBinder> remote = reply.readStrongBinder();
182            sp<IBinder> binder = reply.readStrongBinder();
183            sp<IMemory> mem = interface_cast<IMemory>(binder);
184            if (mem == NULL) {
185                ALOGE("received NULL IMemory for shared buffer");
186            }
187            size_t offset = reply.readInt32();
188            size_t length = reply.readInt32();
189            MediaBuffer *buf = new RemoteMediaBufferWrapper(mem, remote);
190            buf->set_range(offset, length);
191            buf->meta_data()->updateFromParcel(reply);
192            *buffer = buf;
193        } else if (buftype == NULL_BUFFER) {
194            ALOGV("got status %d and NULL buffer", ret);
195            *buffer = NULL;
196        } else {
197            int32_t len = reply.readInt32();
198            ALOGV("got status %d and len %d", ret, len);
199            *buffer = new MediaBuffer(len);
200            reply.read((*buffer)->data(), len);
201            (*buffer)->meta_data()->updateFromParcel(reply);
202        }
203        return ret;
204    }
205
206    virtual status_t pause() {
207        ALOGV("pause");
208        Parcel data, reply;
209        data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor());
210        return remote()->transact(PAUSE, data, &reply);
211    }
212
213    virtual status_t setBuffers(const Vector<MediaBuffer *> & buffers __unused) {
214        ALOGV("setBuffers NOT IMPLEMENTED");
215        return ERROR_UNSUPPORTED; // default
216    }
217
218private:
219    // NuPlayer passes pointers-to-metadata around, so we use this to keep the metadata alive
220    // XXX: could we use this for caching, or does metadata change on the fly?
221    sp<MetaData> mMetaData;
222    bool mStarted;
223    CallStack mStartStack;
224};
225
226IMPLEMENT_META_INTERFACE(MediaSource, "android.media.IMediaSource");
227
228#undef LOG_TAG
229#define LOG_TAG "BnMediaSource"
230
231BnMediaSource::BnMediaSource()
232    : mGroup(NULL) {
233}
234
235BnMediaSource::~BnMediaSource() {
236    delete mGroup;
237    mGroup = NULL;
238}
239
240status_t BnMediaSource::onTransact(
241    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
242{
243    switch (code) {
244        case START: {
245            ALOGV("start");
246            CHECK_INTERFACE(IMediaSource, data, reply);
247            sp<MetaData> meta;
248            if (data.dataAvail()) {
249                meta = MetaData::createFromParcel(data);
250            }
251            status_t ret = start(meta.get());
252            if (ret == NO_ERROR && meta != NULL) {
253                meta->writeToParcel(*reply);
254            }
255            return ret;
256        }
257        case STOP: {
258            ALOGV("stop");
259            CHECK_INTERFACE(IMediaSource, data, reply);
260            return stop();
261        }
262        case PAUSE: {
263            ALOGV("pause");
264            CHECK_INTERFACE(IMediaSource, data, reply);
265            return pause();
266        }
267        case GETFORMAT: {
268            ALOGV("getFormat");
269            CHECK_INTERFACE(IMediaSource, data, reply);
270            sp<MetaData> meta = getFormat();
271            if (meta != NULL) {
272                meta->writeToParcel(*reply);
273                return NO_ERROR;
274            }
275            return UNKNOWN_ERROR;
276        }
277        case READ: {
278            ALOGV("read");
279            CHECK_INTERFACE(IMediaSource, data, reply);
280            status_t ret;
281            MediaBuffer *buf = NULL;
282            ReadOptions opts;
283            uint32_t len;
284            if (data.readUint32(&len) == NO_ERROR &&
285                    len == sizeof(opts) && data.read((void*)&opts, len) == NO_ERROR) {
286                ret = read(&buf, &opts);
287            } else {
288                ret = read(&buf, NULL);
289            }
290
291            reply->writeInt32(ret);
292            if (buf != NULL) {
293                size_t usedSize = buf->range_length();
294                // even if we're using shared memory, we might not want to use it, since for small
295                // sizes it's faster to copy data through the Binder transaction
296                // On the other hand, if the data size is large enough, it's better to use shared
297                // memory. When data is too large, binder can't handle it.
298                if (usedSize >= MediaBuffer::kSharedMemThreshold) {
299                    ALOGV("use shared memory: %zu", usedSize);
300
301                    MediaBuffer *transferBuf = buf;
302                    size_t offset = buf->range_offset();
303                    if (transferBuf->mMemory == NULL) {
304                        if (mGroup == NULL) {
305                            mGroup = new MediaBufferGroup;
306                            size_t allocateSize = usedSize;
307                            if (usedSize < SIZE_MAX / 3) {
308                                allocateSize = usedSize * 3 / 2;
309                            }
310                            mGroup->add_buffer(new MediaBuffer(allocateSize));
311                        }
312
313                        ret = mGroup->acquire_buffer(
314                                &transferBuf, false /* nonBlocking */, usedSize);
315                        if (ret != OK || transferBuf == NULL || transferBuf->mMemory == NULL) {
316                            ALOGW("failed to acquire shared memory, ret %d", ret);
317                            reply->writeInt32(NULL_BUFFER);
318                            return NO_ERROR;
319                        }
320                        memcpy(transferBuf->data(), (uint8_t*)buf->data() + buf->range_offset(),
321                                buf->range_length());
322                        offset = 0;
323                    }
324
325                    reply->writeInt32(SHARED_BUFFER);
326                    RemoteMediaBufferReleaser *wrapper =
327                        new RemoteMediaBufferReleaser(transferBuf, this);
328                    reply->writeStrongBinder(wrapper);
329                    reply->writeStrongBinder(IInterface::asBinder(transferBuf->mMemory));
330                    reply->writeInt32(offset);
331                    reply->writeInt32(usedSize);
332                    buf->meta_data()->writeToParcel(*reply);
333                    if (buf->mMemory == NULL) {
334                        buf->release();
335                    }
336                } else {
337                    // buffer is small: copy it
338                    if (buf->mMemory != NULL) {
339                        ALOGV("%zu shared mem available, but only %zu used", buf->mMemory->size(), buf->range_length());
340                    }
341                    reply->writeInt32(INLINE_BUFFER);
342                    reply->writeByteArray(buf->range_length(), (uint8_t*)buf->data() + buf->range_offset());
343                    buf->meta_data()->writeToParcel(*reply);
344                    buf->release();
345                }
346            } else {
347                ALOGV("ret %d, buf %p", ret, buf);
348                reply->writeInt32(NULL_BUFFER);
349            }
350            return NO_ERROR;
351        }
352        default:
353            return BBinder::onTransact(code, data, reply, flags);
354    }
355}
356
357////////////////////////////////////////////////////////////////////////////////
358
359IMediaSource::ReadOptions::ReadOptions() {
360    reset();
361}
362
363void IMediaSource::ReadOptions::reset() {
364    mOptions = 0;
365    mSeekTimeUs = 0;
366    mLatenessUs = 0;
367    mNonBlocking = false;
368}
369
370void IMediaSource::ReadOptions::setNonBlocking() {
371    mNonBlocking = true;
372}
373
374void IMediaSource::ReadOptions::clearNonBlocking() {
375    mNonBlocking = false;
376}
377
378bool IMediaSource::ReadOptions::getNonBlocking() const {
379    return mNonBlocking;
380}
381
382void IMediaSource::ReadOptions::setSeekTo(int64_t time_us, SeekMode mode) {
383    mOptions |= kSeekTo_Option;
384    mSeekTimeUs = time_us;
385    mSeekMode = mode;
386}
387
388void IMediaSource::ReadOptions::clearSeekTo() {
389    mOptions &= ~kSeekTo_Option;
390    mSeekTimeUs = 0;
391    mSeekMode = SEEK_CLOSEST_SYNC;
392}
393
394bool IMediaSource::ReadOptions::getSeekTo(
395        int64_t *time_us, SeekMode *mode) const {
396    *time_us = mSeekTimeUs;
397    *mode = mSeekMode;
398    return (mOptions & kSeekTo_Option) != 0;
399}
400
401void IMediaSource::ReadOptions::setLateBy(int64_t lateness_us) {
402    mLatenessUs = lateness_us;
403}
404
405int64_t IMediaSource::ReadOptions::getLateBy() const {
406    return mLatenessUs;
407}
408
409
410}  // namespace android
411
412