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