IMediaMetadataRetriever.cpp revision ec4dde75955f87e5afea3f59ef991130257f3f3a
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 <SkBitmap.h>
22#include <media/IMediaMetadataRetriever.h>
23
24// The binder is supposed to propagate the scheduler group across
25// the binder interface so that remote calls are executed with
26// the same priority as local calls. This is currently not working
27// so this change puts in a temporary hack to fix the issue with
28// metadata retrieval which can be a huge CPU hit if done on a
29// foreground thread.
30#ifndef DISABLE_GROUP_SCHEDULE_HACK
31
32/* desktop Linux needs a little help with gettid() */
33#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
34#define __KERNEL__
35# include <linux/unistd.h>
36#ifdef _syscall0
37_syscall0(pid_t,gettid)
38#else
39pid_t gettid() { return syscall(__NR_gettid);}
40#endif
41#undef __KERNEL__
42#endif
43
44#define LOG_TAG "IMediaMetadataRetriever"
45#include <utils/Log.h>
46#include <cutils/sched_policy.h>
47
48namespace android {
49
50static void sendSchedPolicy(Parcel& data)
51{
52    SchedPolicy policy;
53    get_sched_policy(gettid(), &policy);
54    data.writeInt32(policy);
55}
56
57static void setSchedPolicy(const Parcel& data)
58{
59    SchedPolicy policy = (SchedPolicy) data.readInt32();
60    set_sched_policy(gettid(), policy);
61}
62static void restoreSchedPolicy()
63{
64    set_sched_policy(gettid(), SP_FOREGROUND);
65}
66}; // end namespace android
67#endif
68
69namespace android {
70
71enum {
72    DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
73    SET_DATA_SOURCE_URL,
74    SET_DATA_SOURCE_FD,
75    SET_MODE,
76    GET_MODE,
77    CAPTURE_FRAME,
78    EXTRACT_ALBUM_ART,
79    EXTRACT_METADATA,
80};
81
82class BpMediaMetadataRetriever: public BpInterface<IMediaMetadataRetriever>
83{
84public:
85    BpMediaMetadataRetriever(const sp<IBinder>& impl)
86        : BpInterface<IMediaMetadataRetriever>(impl)
87    {
88    }
89
90    // disconnect from media metadata retriever service
91    void disconnect()
92    {
93        Parcel data, reply;
94        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
95        remote()->transact(DISCONNECT, data, &reply);
96    }
97
98    status_t setDataSource(const char* srcUrl)
99    {
100        Parcel data, reply;
101        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
102        data.writeCString(srcUrl);
103        remote()->transact(SET_DATA_SOURCE_URL, data, &reply);
104        return reply.readInt32();
105    }
106
107    status_t setDataSource(int fd, int64_t offset, int64_t length)
108    {
109        Parcel data, reply;
110        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
111        data.writeFileDescriptor(fd);
112        data.writeInt64(offset);
113        data.writeInt64(length);
114        remote()->transact(SET_DATA_SOURCE_FD, data, &reply);
115        return reply.readInt32();
116    }
117
118    status_t setMode(int mode)
119    {
120        Parcel data, reply;
121        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
122        data.writeInt32(mode);
123        remote()->transact(SET_MODE, data, &reply);
124        return reply.readInt32();
125    }
126
127    status_t getMode(int* mode) const
128    {
129        Parcel data, reply;
130        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
131        remote()->transact(GET_MODE, data, &reply);
132        *mode = reply.readInt32();
133        return reply.readInt32();
134    }
135
136    sp<IMemory> captureFrame()
137    {
138        Parcel data, reply;
139        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
140#ifndef DISABLE_GROUP_SCHEDULE_HACK
141        sendSchedPolicy(data);
142#endif
143        remote()->transact(CAPTURE_FRAME, data, &reply);
144        status_t ret = reply.readInt32();
145        if (ret != NO_ERROR) {
146            return NULL;
147        }
148        return interface_cast<IMemory>(reply.readStrongBinder());
149    }
150
151    sp<IMemory> extractAlbumArt()
152    {
153        Parcel data, reply;
154        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
155#ifndef DISABLE_GROUP_SCHEDULE_HACK
156        sendSchedPolicy(data);
157#endif
158        remote()->transact(EXTRACT_ALBUM_ART, data, &reply);
159        status_t ret = reply.readInt32();
160        if (ret != NO_ERROR) {
161            return NULL;
162        }
163        return interface_cast<IMemory>(reply.readStrongBinder());
164    }
165
166    const char* extractMetadata(int keyCode)
167    {
168        Parcel data, reply;
169        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
170#ifndef DISABLE_GROUP_SCHEDULE_HACK
171        sendSchedPolicy(data);
172#endif
173        data.writeInt32(keyCode);
174        remote()->transact(EXTRACT_METADATA, data, &reply);
175        status_t ret = reply.readInt32();
176        if (ret != NO_ERROR) {
177            return NULL;
178        }
179        return reply.readCString();
180    }
181};
182
183IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.media.IMediaMetadataRetriever");
184
185// ----------------------------------------------------------------------
186
187status_t BnMediaMetadataRetriever::onTransact(
188    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
189{
190    switch (code) {
191        case DISCONNECT: {
192            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
193            disconnect();
194            return NO_ERROR;
195        } break;
196        case SET_DATA_SOURCE_URL: {
197            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
198            const char* srcUrl = data.readCString();
199            reply->writeInt32(setDataSource(srcUrl));
200            return NO_ERROR;
201        } break;
202        case SET_DATA_SOURCE_FD: {
203            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
204            int fd = dup(data.readFileDescriptor());
205            int64_t offset = data.readInt64();
206            int64_t length = data.readInt64();
207            reply->writeInt32(setDataSource(fd, offset, length));
208            return NO_ERROR;
209        } break;
210        case SET_MODE: {
211            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
212            int mode = data.readInt32();
213            reply->writeInt32(setMode(mode));
214            return NO_ERROR;
215        } break;
216        case GET_MODE: {
217            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
218            int mode;
219            status_t status = getMode(&mode);
220            reply->writeInt32(mode);
221            reply->writeInt32(status);
222            return NO_ERROR;
223        } break;
224        case CAPTURE_FRAME: {
225            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
226#ifndef DISABLE_GROUP_SCHEDULE_HACK
227            setSchedPolicy(data);
228#endif
229            sp<IMemory> bitmap = captureFrame();
230            if (bitmap != 0) {  // Don't send NULL across the binder interface
231                reply->writeInt32(NO_ERROR);
232                reply->writeStrongBinder(bitmap->asBinder());
233            } else {
234                reply->writeInt32(UNKNOWN_ERROR);
235            }
236#ifndef DISABLE_GROUP_SCHEDULE_HACK
237            restoreSchedPolicy();
238#endif
239            return NO_ERROR;
240        } break;
241        case EXTRACT_ALBUM_ART: {
242            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
243#ifndef DISABLE_GROUP_SCHEDULE_HACK
244            setSchedPolicy(data);
245#endif
246            sp<IMemory> albumArt = extractAlbumArt();
247            if (albumArt != 0) {  // Don't send NULL across the binder interface
248                reply->writeInt32(NO_ERROR);
249                reply->writeStrongBinder(albumArt->asBinder());
250            } else {
251                reply->writeInt32(UNKNOWN_ERROR);
252            }
253#ifndef DISABLE_GROUP_SCHEDULE_HACK
254            restoreSchedPolicy();
255#endif
256            return NO_ERROR;
257        } break;
258        case EXTRACT_METADATA: {
259            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
260#ifndef DISABLE_GROUP_SCHEDULE_HACK
261            setSchedPolicy(data);
262#endif
263            int keyCode = data.readInt32();
264            const char* value = extractMetadata(keyCode);
265            if (value != NULL) {  // Don't send NULL across the binder interface
266                reply->writeInt32(NO_ERROR);
267                reply->writeCString(value);
268            } else {
269                reply->writeInt32(UNKNOWN_ERROR);
270            }
271#ifndef DISABLE_GROUP_SCHEDULE_HACK
272            restoreSchedPolicy();
273#endif
274            return NO_ERROR;
275        } break;
276        default:
277            return BBinder::onTransact(code, data, reply, flags);
278    }
279}
280
281// ----------------------------------------------------------------------------
282
283}; // namespace android
284