• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/IMediaMetadataRetriever.h>
22 #include <utils/String8.h>
23 #include <utils/KeyedVector.h>
24 
25 // The binder is supposed to propagate the scheduler group across
26 // the binder interface so that remote calls are executed with
27 // the same priority as local calls. This is currently not working
28 // so this change puts in a temporary hack to fix the issue with
29 // metadata retrieval which can be a huge CPU hit if done on a
30 // foreground thread.
31 #ifndef DISABLE_GROUP_SCHEDULE_HACK
32 
33 #undef LOG_TAG
34 #define LOG_TAG "IMediaMetadataRetriever"
35 #include <utils/Log.h>
36 #include <cutils/sched_policy.h>
37 
38 namespace android {
39 
sendSchedPolicy(Parcel & data)40 static void sendSchedPolicy(Parcel& data)
41 {
42     SchedPolicy policy;
43     get_sched_policy(gettid(), &policy);
44     data.writeInt32(policy);
45 }
46 
setSchedPolicy(const Parcel & data)47 static void setSchedPolicy(const Parcel& data)
48 {
49     SchedPolicy policy = (SchedPolicy) data.readInt32();
50     set_sched_policy(gettid(), policy);
51 }
restoreSchedPolicy()52 static void restoreSchedPolicy()
53 {
54     set_sched_policy(gettid(), SP_FOREGROUND);
55 }
56 }; // end namespace android
57 #endif
58 
59 namespace android {
60 
61 enum {
62     DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
63     SET_DATA_SOURCE_URL,
64     SET_DATA_SOURCE_FD,
65     GET_FRAME_AT_TIME,
66     EXTRACT_ALBUM_ART,
67     EXTRACT_METADATA,
68 };
69 
70 class BpMediaMetadataRetriever: public BpInterface<IMediaMetadataRetriever>
71 {
72 public:
BpMediaMetadataRetriever(const sp<IBinder> & impl)73     BpMediaMetadataRetriever(const sp<IBinder>& impl)
74         : BpInterface<IMediaMetadataRetriever>(impl)
75     {
76     }
77 
78     // disconnect from media metadata retriever service
disconnect()79     void disconnect()
80     {
81         Parcel data, reply;
82         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
83         remote()->transact(DISCONNECT, data, &reply);
84     }
85 
setDataSource(const char * srcUrl,const KeyedVector<String8,String8> * headers)86     status_t setDataSource(
87             const char *srcUrl, const KeyedVector<String8, String8> *headers)
88     {
89         Parcel data, reply;
90         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
91         data.writeCString(srcUrl);
92 
93         if (headers == NULL) {
94             data.writeInt32(0);
95         } else {
96             // serialize the headers
97             data.writeInt32(headers->size());
98             for (size_t i = 0; i < headers->size(); ++i) {
99                 data.writeString8(headers->keyAt(i));
100                 data.writeString8(headers->valueAt(i));
101             }
102         }
103 
104         remote()->transact(SET_DATA_SOURCE_URL, data, &reply);
105         return reply.readInt32();
106     }
107 
setDataSource(int fd,int64_t offset,int64_t length)108     status_t setDataSource(int fd, int64_t offset, int64_t length)
109     {
110         Parcel data, reply;
111         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
112         data.writeFileDescriptor(fd);
113         data.writeInt64(offset);
114         data.writeInt64(length);
115         remote()->transact(SET_DATA_SOURCE_FD, data, &reply);
116         return reply.readInt32();
117     }
118 
getFrameAtTime(int64_t timeUs,int option)119     sp<IMemory> getFrameAtTime(int64_t timeUs, int option)
120     {
121         ALOGV("getTimeAtTime: time(%lld us) and option(%d)", timeUs, option);
122         Parcel data, reply;
123         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
124         data.writeInt64(timeUs);
125         data.writeInt32(option);
126 #ifndef DISABLE_GROUP_SCHEDULE_HACK
127         sendSchedPolicy(data);
128 #endif
129         remote()->transact(GET_FRAME_AT_TIME, data, &reply);
130         status_t ret = reply.readInt32();
131         if (ret != NO_ERROR) {
132             return NULL;
133         }
134         return interface_cast<IMemory>(reply.readStrongBinder());
135     }
136 
extractAlbumArt()137     sp<IMemory> extractAlbumArt()
138     {
139         Parcel data, reply;
140         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
141 #ifndef DISABLE_GROUP_SCHEDULE_HACK
142         sendSchedPolicy(data);
143 #endif
144         remote()->transact(EXTRACT_ALBUM_ART, data, &reply);
145         status_t ret = reply.readInt32();
146         if (ret != NO_ERROR) {
147             return NULL;
148         }
149         return interface_cast<IMemory>(reply.readStrongBinder());
150     }
151 
extractMetadata(int keyCode)152     const char* extractMetadata(int keyCode)
153     {
154         Parcel data, reply;
155         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
156 #ifndef DISABLE_GROUP_SCHEDULE_HACK
157         sendSchedPolicy(data);
158 #endif
159         data.writeInt32(keyCode);
160         remote()->transact(EXTRACT_METADATA, data, &reply);
161         status_t ret = reply.readInt32();
162         if (ret != NO_ERROR) {
163             return NULL;
164         }
165         const char* str = reply.readCString();
166         if (str != NULL) {
167             String8 value(str);
168             if (mMetadata.indexOfKey(keyCode) < 0) {
169                 mMetadata.add(keyCode, value);
170             } else {
171                 mMetadata.replaceValueFor(keyCode, value);
172             }
173             return mMetadata.valueFor(keyCode).string();
174         } else {
175             return NULL;
176         }
177     }
178 
179 private:
180     KeyedVector<int, String8> mMetadata;
181 };
182 
183 IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.media.IMediaMetadataRetriever");
184 
185 // ----------------------------------------------------------------------
186 
onTransact(uint32_t code,const Parcel & data,Parcel * reply,uint32_t flags)187 status_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 
200             KeyedVector<String8, String8> headers;
201             int32_t numHeaders = data.readInt32();
202             for (int i = 0; i < numHeaders; ++i) {
203                 String8 key = data.readString8();
204                 String8 value = data.readString8();
205                 headers.add(key, value);
206             }
207 
208             reply->writeInt32(
209                     setDataSource(srcUrl, numHeaders > 0 ? &headers : NULL));
210 
211             return NO_ERROR;
212         } break;
213         case SET_DATA_SOURCE_FD: {
214             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
215             int fd = dup(data.readFileDescriptor());
216             int64_t offset = data.readInt64();
217             int64_t length = data.readInt64();
218             reply->writeInt32(setDataSource(fd, offset, length));
219             return NO_ERROR;
220         } break;
221         case GET_FRAME_AT_TIME: {
222             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
223             int64_t timeUs = data.readInt64();
224             int option = data.readInt32();
225             ALOGV("getTimeAtTime: time(%lld us) and option(%d)", timeUs, option);
226 #ifndef DISABLE_GROUP_SCHEDULE_HACK
227             setSchedPolicy(data);
228 #endif
229             sp<IMemory> bitmap = getFrameAtTime(timeUs, option);
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