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 38namespace android { 39 40static void sendSchedPolicy(Parcel& data) 41{ 42 SchedPolicy policy; 43 get_sched_policy(gettid(), &policy); 44 data.writeInt32(policy); 45} 46 47static void setSchedPolicy(const Parcel& data) 48{ 49 SchedPolicy policy = (SchedPolicy) data.readInt32(); 50 set_sched_policy(gettid(), policy); 51} 52static void restoreSchedPolicy() 53{ 54 set_sched_policy(gettid(), SP_FOREGROUND); 55} 56}; // end namespace android 57#endif 58 59namespace android { 60 61enum { 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 70class BpMediaMetadataRetriever: public BpInterface<IMediaMetadataRetriever> 71{ 72public: 73 BpMediaMetadataRetriever(const sp<IBinder>& impl) 74 : BpInterface<IMediaMetadataRetriever>(impl) 75 { 76 } 77 78 // disconnect from media metadata retriever service 79 void disconnect() 80 { 81 Parcel data, reply; 82 data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); 83 remote()->transact(DISCONNECT, data, &reply); 84 } 85 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 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 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 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 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 179private: 180 KeyedVector<int, String8> mMetadata; 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 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