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