IMediaMetadataRetriever.cpp revision b53fc4ef8da47df8d5f580e8ee6135866ffebc33
1/*
2**
3** Copyright (C) 2008 The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include <stdint.h>
19#include <sys/types.h>
20#include <binder/Parcel.h>
21#include <media/IMediaHTTPService.h>
22#include <media/IMediaMetadataRetriever.h>
23#include <utils/String8.h>
24#include <utils/KeyedVector.h>
25
26// The binder is supposed to propagate the scheduler group across
27// the binder interface so that remote calls are executed with
28// the same priority as local calls. This is currently not working
29// so this change puts in a temporary hack to fix the issue with
30// metadata retrieval which can be a huge CPU hit if done on a
31// foreground thread.
32#ifndef DISABLE_GROUP_SCHEDULE_HACK
33
34#undef LOG_TAG
35#define LOG_TAG "IMediaMetadataRetriever"
36#include <utils/Log.h>
37#include <cutils/sched_policy.h>
38
39namespace android {
40
41static void sendSchedPolicy(Parcel& data)
42{
43    SchedPolicy policy;
44    get_sched_policy(gettid(), &policy);
45    data.writeInt32(policy);
46}
47
48static void setSchedPolicy(const Parcel& data)
49{
50    SchedPolicy policy = (SchedPolicy) data.readInt32();
51    set_sched_policy(gettid(), policy);
52}
53static void restoreSchedPolicy()
54{
55    set_sched_policy(gettid(), SP_FOREGROUND);
56}
57}; // end namespace android
58#endif
59
60namespace android {
61
62enum {
63    DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
64    SET_DATA_SOURCE_URL,
65    SET_DATA_SOURCE_FD,
66    GET_FRAME_AT_TIME,
67    EXTRACT_ALBUM_ART,
68    EXTRACT_METADATA,
69};
70
71class BpMediaMetadataRetriever: public BpInterface<IMediaMetadataRetriever>
72{
73public:
74    BpMediaMetadataRetriever(const sp<IBinder>& impl)
75        : BpInterface<IMediaMetadataRetriever>(impl)
76    {
77    }
78
79    // disconnect from media metadata retriever service
80    void disconnect()
81    {
82        Parcel data, reply;
83        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
84        remote()->transact(DISCONNECT, data, &reply);
85    }
86
87    status_t setDataSource(
88            const sp<IMediaHTTPService> &httpService,
89            const char *srcUrl,
90            const KeyedVector<String8, String8> *headers)
91    {
92        Parcel data, reply;
93        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
94        data.writeInt32(httpService != NULL);
95        if (httpService != NULL) {
96            data.writeStrongBinder(httpService->asBinder());
97        }
98        data.writeCString(srcUrl);
99
100        if (headers == NULL) {
101            data.writeInt32(0);
102        } else {
103            // serialize the headers
104            data.writeInt64(headers->size());
105            for (size_t i = 0; i < headers->size(); ++i) {
106                data.writeString8(headers->keyAt(i));
107                data.writeString8(headers->valueAt(i));
108            }
109        }
110
111        remote()->transact(SET_DATA_SOURCE_URL, data, &reply);
112        return reply.readInt32();
113    }
114
115    status_t setDataSource(int fd, int64_t offset, int64_t length)
116    {
117        Parcel data, reply;
118        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
119        data.writeFileDescriptor(fd);
120        data.writeInt64(offset);
121        data.writeInt64(length);
122        remote()->transact(SET_DATA_SOURCE_FD, data, &reply);
123        return reply.readInt32();
124    }
125
126    sp<IMemory> getFrameAtTime(int64_t timeUs, int option)
127    {
128        ALOGV("getTimeAtTime: time(%lld us) and option(%d)", timeUs, option);
129        Parcel data, reply;
130        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
131        data.writeInt64(timeUs);
132        data.writeInt32(option);
133#ifndef DISABLE_GROUP_SCHEDULE_HACK
134        sendSchedPolicy(data);
135#endif
136        remote()->transact(GET_FRAME_AT_TIME, data, &reply);
137        status_t ret = reply.readInt32();
138        if (ret != NO_ERROR) {
139            return NULL;
140        }
141        return interface_cast<IMemory>(reply.readStrongBinder());
142    }
143
144    sp<IMemory> extractAlbumArt()
145    {
146        Parcel data, reply;
147        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
148#ifndef DISABLE_GROUP_SCHEDULE_HACK
149        sendSchedPolicy(data);
150#endif
151        remote()->transact(EXTRACT_ALBUM_ART, data, &reply);
152        status_t ret = reply.readInt32();
153        if (ret != NO_ERROR) {
154            return NULL;
155        }
156        return interface_cast<IMemory>(reply.readStrongBinder());
157    }
158
159    const char* extractMetadata(int keyCode)
160    {
161        Parcel data, reply;
162        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
163#ifndef DISABLE_GROUP_SCHEDULE_HACK
164        sendSchedPolicy(data);
165#endif
166        data.writeInt32(keyCode);
167        remote()->transact(EXTRACT_METADATA, data, &reply);
168        status_t ret = reply.readInt32();
169        if (ret != NO_ERROR) {
170            return NULL;
171        }
172        const char* str = reply.readCString();
173        if (str != NULL) {
174            String8 value(str);
175            if (mMetadata.indexOfKey(keyCode) < 0) {
176                mMetadata.add(keyCode, value);
177            } else {
178                mMetadata.replaceValueFor(keyCode, value);
179            }
180            return mMetadata.valueFor(keyCode).string();
181        } else {
182            return NULL;
183        }
184    }
185
186private:
187    KeyedVector<int, String8> mMetadata;
188};
189
190IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.media.IMediaMetadataRetriever");
191
192// ----------------------------------------------------------------------
193
194status_t BnMediaMetadataRetriever::onTransact(
195    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
196{
197    switch (code) {
198        case DISCONNECT: {
199            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
200            disconnect();
201            return NO_ERROR;
202        } break;
203        case SET_DATA_SOURCE_URL: {
204            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
205
206            sp<IMediaHTTPService> httpService;
207            if (data.readInt32()) {
208                httpService =
209                    interface_cast<IMediaHTTPService>(data.readStrongBinder());
210            }
211
212            const char* srcUrl = data.readCString();
213
214            KeyedVector<String8, String8> headers;
215            size_t numHeaders = (size_t) data.readInt64();
216            for (size_t i = 0; i < numHeaders; ++i) {
217                String8 key = data.readString8();
218                String8 value = data.readString8();
219                headers.add(key, value);
220            }
221
222            reply->writeInt32(
223                    setDataSource(
224                        httpService, srcUrl, numHeaders > 0 ? &headers : NULL));
225
226            return NO_ERROR;
227        } break;
228        case SET_DATA_SOURCE_FD: {
229            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
230            int fd = dup(data.readFileDescriptor());
231            int64_t offset = data.readInt64();
232            int64_t length = data.readInt64();
233            reply->writeInt32(setDataSource(fd, offset, length));
234            return NO_ERROR;
235        } break;
236        case GET_FRAME_AT_TIME: {
237            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
238            int64_t timeUs = data.readInt64();
239            int option = data.readInt32();
240            ALOGV("getTimeAtTime: time(%lld us) and option(%d)", timeUs, option);
241#ifndef DISABLE_GROUP_SCHEDULE_HACK
242            setSchedPolicy(data);
243#endif
244            sp<IMemory> bitmap = getFrameAtTime(timeUs, option);
245            if (bitmap != 0) {  // Don't send NULL across the binder interface
246                reply->writeInt32(NO_ERROR);
247                reply->writeStrongBinder(bitmap->asBinder());
248            } else {
249                reply->writeInt32(UNKNOWN_ERROR);
250            }
251#ifndef DISABLE_GROUP_SCHEDULE_HACK
252            restoreSchedPolicy();
253#endif
254            return NO_ERROR;
255        } break;
256        case EXTRACT_ALBUM_ART: {
257            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
258#ifndef DISABLE_GROUP_SCHEDULE_HACK
259            setSchedPolicy(data);
260#endif
261            sp<IMemory> albumArt = extractAlbumArt();
262            if (albumArt != 0) {  // Don't send NULL across the binder interface
263                reply->writeInt32(NO_ERROR);
264                reply->writeStrongBinder(albumArt->asBinder());
265            } else {
266                reply->writeInt32(UNKNOWN_ERROR);
267            }
268#ifndef DISABLE_GROUP_SCHEDULE_HACK
269            restoreSchedPolicy();
270#endif
271            return NO_ERROR;
272        } break;
273        case EXTRACT_METADATA: {
274            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
275#ifndef DISABLE_GROUP_SCHEDULE_HACK
276            setSchedPolicy(data);
277#endif
278            int keyCode = data.readInt32();
279            const char* value = extractMetadata(keyCode);
280            if (value != NULL) {  // Don't send NULL across the binder interface
281                reply->writeInt32(NO_ERROR);
282                reply->writeCString(value);
283            } else {
284                reply->writeInt32(UNKNOWN_ERROR);
285            }
286#ifndef DISABLE_GROUP_SCHEDULE_HACK
287            restoreSchedPolicy();
288#endif
289            return NO_ERROR;
290        } break;
291        default:
292            return BBinder::onTransact(code, data, reply, flags);
293    }
294}
295
296// ----------------------------------------------------------------------------
297
298}; // namespace android
299