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 "BpMediaExtractor"
19#include <utils/Log.h>
20
21#include <stdint.h>
22#include <sys/types.h>
23
24#include <binder/IPCThreadState.h>
25#include <binder/Parcel.h>
26#include <media/IMediaExtractor.h>
27#include <media/stagefright/MetaData.h>
28
29namespace android {
30
31enum {
32    COUNTTRACKS = IBinder::FIRST_CALL_TRANSACTION,
33    GETTRACK,
34    GETTRACKMETADATA,
35    GETMETADATA,
36    FLAGS,
37    SETDRMFLAG,
38    GETDRMFLAG,
39    GETDRMTRACKINFO,
40    SETUID,
41    NAME
42};
43
44class BpMediaExtractor : public BpInterface<IMediaExtractor> {
45public:
46    BpMediaExtractor(const sp<IBinder>& impl)
47        : BpInterface<IMediaExtractor>(impl)
48    {
49    }
50
51    virtual size_t countTracks() {
52        ALOGV("countTracks");
53        Parcel data, reply;
54        data.writeInterfaceToken(BpMediaExtractor::getInterfaceDescriptor());
55        status_t ret = remote()->transact(COUNTTRACKS, data, &reply);
56        size_t numTracks = 0;
57        if (ret == NO_ERROR) {
58            numTracks = reply.readUint32();
59        }
60        return numTracks;
61    }
62    virtual sp<IMediaSource> getTrack(size_t index) {
63        ALOGV("getTrack(%zu)", index);
64        Parcel data, reply;
65        data.writeInterfaceToken(BpMediaExtractor::getInterfaceDescriptor());
66        data.writeUint32(index);
67        status_t ret = remote()->transact(GETTRACK, data, &reply);
68        if (ret == NO_ERROR) {
69            return interface_cast<IMediaSource>(reply.readStrongBinder());
70        }
71        return NULL;
72    }
73
74    virtual sp<MetaData> getTrackMetaData(
75            size_t index, uint32_t flags) {
76        ALOGV("getTrackMetaData(%zu, %u)", index, flags);
77        Parcel data, reply;
78        data.writeInterfaceToken(BpMediaExtractor::getInterfaceDescriptor());
79        data.writeUint32(index);
80        data.writeUint32(flags);
81        status_t ret = remote()->transact(GETTRACKMETADATA, data, &reply);
82        if (ret == NO_ERROR) {
83            return MetaData::createFromParcel(reply);
84        }
85        return NULL;
86    }
87
88    virtual sp<MetaData> getMetaData() {
89        ALOGV("getMetaData");
90        Parcel data, reply;
91        data.writeInterfaceToken(BpMediaExtractor::getInterfaceDescriptor());
92        status_t ret = remote()->transact(GETMETADATA, data, &reply);
93        if (ret == NO_ERROR) {
94            return MetaData::createFromParcel(reply);
95        }
96        return NULL;
97    }
98
99    virtual uint32_t flags() const {
100        ALOGV("flags NOT IMPLEMENTED");
101        return 0;
102    }
103
104    virtual void setDrmFlag(bool flag __unused) {
105        ALOGV("setDrmFlag NOT IMPLEMENTED");
106    }
107    virtual bool getDrmFlag() {
108        ALOGV("getDrmFlag NOT IMPLEMENTED");
109       return false;
110    }
111    virtual char* getDrmTrackInfo(size_t trackID __unused, int *len __unused) {
112        ALOGV("getDrmTrackInfo NOT IMPLEMENTED");
113        return NULL;
114    }
115    virtual void setUID(uid_t uid __unused) {
116        ALOGV("setUID NOT IMPLEMENTED");
117    }
118
119    virtual const char * name() {
120        ALOGV("name NOT IMPLEMENTED");
121        return NULL;
122    }
123};
124
125IMPLEMENT_META_INTERFACE(MediaExtractor, "android.media.IMediaExtractor");
126
127#undef LOG_TAG
128#define LOG_TAG "BnMediaExtractor"
129
130status_t BnMediaExtractor::onTransact(
131    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
132{
133    switch (code) {
134        case COUNTTRACKS: {
135            ALOGV("countTracks");
136            CHECK_INTERFACE(IMediaExtractor, data, reply);
137            size_t numTracks = countTracks();
138            if (numTracks > INT32_MAX) {
139                numTracks = 0;
140            }
141            reply->writeUint32(uint32_t(numTracks));
142            return NO_ERROR;
143        }
144        case GETTRACK: {
145            ALOGV("getTrack()");
146            CHECK_INTERFACE(IMediaExtractor, data, reply);
147            uint32_t idx;
148            if (data.readUint32(&idx) == NO_ERROR) {
149                const sp<IMediaSource> track = getTrack(size_t(idx));
150                registerMediaSource(this, track);
151                return reply->writeStrongBinder(IInterface::asBinder(track));
152            }
153            return UNKNOWN_ERROR;
154        }
155        case GETTRACKMETADATA: {
156            ALOGV("getTrackMetaData");
157            CHECK_INTERFACE(IMediaExtractor, data, reply);
158            uint32_t idx;
159            uint32_t flags;
160            if (data.readUint32(&idx) == NO_ERROR &&
161                    data.readUint32(&flags) == NO_ERROR) {
162                sp<MetaData> meta = getTrackMetaData(idx, flags);
163                meta->writeToParcel(*reply);
164                return NO_ERROR;
165            }
166            return UNKNOWN_ERROR;
167        }
168        case GETMETADATA: {
169            ALOGV("getMetaData");
170            CHECK_INTERFACE(IMediaExtractor, data, reply);
171            sp<MetaData> meta = getMetaData();
172            if (meta != NULL) {
173                meta->writeToParcel(*reply);
174                return NO_ERROR;
175            }
176            return UNKNOWN_ERROR;
177        }
178        default:
179            return BBinder::onTransact(code, data, reply, flags);
180    }
181}
182
183typedef struct {
184    String8 mime;
185    String8 name;
186    String8 sourceDescription;
187    pid_t owner;
188    wp<IMediaExtractor> extractor;
189    Vector<wp<IMediaSource>> tracks;
190    Vector<String8> trackDescriptions;
191    String8 toString() const;
192} ExtractorInstance;
193
194String8 ExtractorInstance::toString() const {
195    String8 str = name;
196    str.append(" for mime ");
197    str.append(mime);
198    str.append(", source ");
199    str.append(sourceDescription);
200    str.append(String8::format(", pid %d: ", owner));
201    if (extractor.promote() == NULL) {
202        str.append("deleted\n");
203    } else {
204        str.append("active\n");
205    }
206    for (size_t i = 0; i < tracks.size(); i++) {
207        const String8 desc = trackDescriptions.itemAt(i);
208        str.appendFormat("    track {%s} ", desc.string());
209        const sp<IMediaSource> source = tracks.itemAt(i).promote();
210        if (source == NULL) {
211            str.append(": deleted\n");
212        } else {
213            str.appendFormat(": active\n");
214        }
215    }
216    return str;
217}
218
219static Vector<ExtractorInstance> sExtractors;
220static Mutex sExtractorsLock;
221
222void registerMediaSource(
223        const sp<IMediaExtractor> &ex,
224        const sp<IMediaSource> &source) {
225    Mutex::Autolock lock(sExtractorsLock);
226    for (size_t i = 0; i < sExtractors.size(); i++) {
227        ExtractorInstance &instance = sExtractors.editItemAt(i);
228        sp<IMediaExtractor> extractor = instance.extractor.promote();
229        if (extractor != NULL && extractor == ex) {
230            if (instance.tracks.size() > 5) {
231                instance.tracks.resize(5);
232            }
233            instance.tracks.push_front(source);
234            instance.trackDescriptions.add(source->getFormat()->toString());
235            break;
236        }
237    }
238}
239
240void registerMediaExtractor(
241        const sp<IMediaExtractor> &extractor,
242        const sp<DataSource> &source,
243        const char *mime) {
244    ExtractorInstance ex;
245    ex.mime = mime == NULL ? "NULL" : mime;
246    ex.name = extractor->name();
247    ex.sourceDescription = source->toString();
248    ex.owner = IPCThreadState::self()->getCallingPid();
249    ex.extractor = extractor;
250
251    {
252        Mutex::Autolock lock(sExtractorsLock);
253        if (sExtractors.size() > 10) {
254            sExtractors.resize(10);
255        }
256        sExtractors.push_front(ex);
257    }
258}
259
260status_t dumpExtractors(int fd, const Vector<String16>&) {
261    String8 out;
262    out.append("Recent extractors, most recent first:\n");
263    {
264        Mutex::Autolock lock(sExtractorsLock);
265        for (size_t i = 0; i < sExtractors.size(); i++) {
266            const ExtractorInstance &instance = sExtractors.itemAt(i);
267            out.append("  ");
268            out.append(instance.toString());
269        }
270    }
271    write(fd, out.string(), out.size());
272    return OK;
273}
274
275
276}  // namespace android
277
278