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