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