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/IDataSource.h> 24 #include <media/IMediaHTTPService.h> 25 #include <media/IMediaMetadataRetriever.h> 26 #include <processgroup/sched_policy.h> 27 #include <utils/String8.h> 28 #include <utils/KeyedVector.h> 29 30 // The binder is supposed to propagate the scheduler group across 31 // the binder interface so that remote calls are executed with 32 // the same priority as local calls. This is currently not working 33 // so this change puts in a temporary hack to fix the issue with 34 // metadata retrieval which can be a huge CPU hit if done on a 35 // foreground thread. 36 #ifndef DISABLE_GROUP_SCHEDULE_HACK 37 38 #undef LOG_TAG 39 #define LOG_TAG "IMediaMetadataRetriever" 40 #include <utils/Log.h> 41 #include <cutils/sched_policy.h> 42 43 namespace android { 44 45 static void sendSchedPolicy(Parcel& data) 46 { 47 SchedPolicy policy; 48 get_sched_policy(gettid(), &policy); 49 data.writeInt32(policy); 50 } 51 52 static void setSchedPolicy(const Parcel& data) 53 { 54 SchedPolicy policy = (SchedPolicy) data.readInt32(); 55 set_sched_policy(gettid(), policy); 56 } 57 static void restoreSchedPolicy() 58 { 59 set_sched_policy(gettid(), SP_FOREGROUND); 60 } 61 }; // end namespace android 62 #endif 63 64 namespace android { 65 66 enum { 67 DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, 68 SET_DATA_SOURCE_URL, 69 SET_DATA_SOURCE_FD, 70 SET_DATA_SOURCE_CALLBACK, 71 GET_FRAME_AT_TIME, 72 GET_IMAGE_AT_INDEX, 73 GET_IMAGE_RECT_AT_INDEX, 74 GET_FRAME_AT_INDEX, 75 EXTRACT_ALBUM_ART, 76 EXTRACT_METADATA, 77 }; 78 79 class BpMediaMetadataRetriever: public BpInterface<IMediaMetadataRetriever> 80 { 81 public: 82 explicit BpMediaMetadataRetriever(const sp<IBinder>& impl) 83 : BpInterface<IMediaMetadataRetriever>(impl) 84 { 85 } 86 87 // disconnect from media metadata retriever service 88 void disconnect() 89 { 90 Parcel data, reply; 91 data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); 92 remote()->transact(DISCONNECT, data, &reply); 93 } 94 95 status_t setDataSource( 96 const sp<IMediaHTTPService> &httpService, 97 const char *srcUrl, 98 const KeyedVector<String8, String8> *headers) 99 { 100 Parcel data, reply; 101 data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); 102 data.writeInt32(httpService != NULL); 103 if (httpService != NULL) { 104 data.writeStrongBinder(IInterface::asBinder(httpService)); 105 } 106 data.writeCString(srcUrl); 107 108 if (headers == NULL) { 109 data.writeInt32(0); 110 } else { 111 // serialize the headers 112 data.writeInt64(headers->size()); 113 for (size_t i = 0; i < headers->size(); ++i) { 114 data.writeString8(headers->keyAt(i)); 115 data.writeString8(headers->valueAt(i)); 116 } 117 } 118 119 remote()->transact(SET_DATA_SOURCE_URL, data, &reply); 120 return reply.readInt32(); 121 } 122 123 status_t setDataSource(int fd, int64_t offset, int64_t length) 124 { 125 Parcel data, reply; 126 data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); 127 data.writeFileDescriptor(fd); 128 data.writeInt64(offset); 129 data.writeInt64(length); 130 remote()->transact(SET_DATA_SOURCE_FD, data, &reply); 131 return reply.readInt32(); 132 } 133 134 status_t setDataSource(const sp<IDataSource>& source, const char *mime) 135 { 136 Parcel data, reply; 137 data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); 138 data.writeStrongBinder(IInterface::asBinder(source)); 139 140 if (mime != NULL) { 141 data.writeInt32(1); 142 data.writeCString(mime); 143 } else { 144 data.writeInt32(0); 145 } 146 remote()->transact(SET_DATA_SOURCE_CALLBACK, data, &reply); 147 return reply.readInt32(); 148 } 149 150 sp<IMemory> getFrameAtTime(int64_t timeUs, int option, int colorFormat, bool metaOnly) 151 { 152 ALOGV("getTimeAtTime: time(%" PRId64 " us), option(%d), colorFormat(%d) metaOnly(%d)", 153 timeUs, option, colorFormat, metaOnly); 154 Parcel data, reply; 155 data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); 156 data.writeInt64(timeUs); 157 data.writeInt32(option); 158 data.writeInt32(colorFormat); 159 data.writeInt32(metaOnly); 160 #ifndef DISABLE_GROUP_SCHEDULE_HACK 161 sendSchedPolicy(data); 162 #endif 163 remote()->transact(GET_FRAME_AT_TIME, data, &reply); 164 status_t ret = reply.readInt32(); 165 if (ret != NO_ERROR) { 166 return NULL; 167 } 168 return interface_cast<IMemory>(reply.readStrongBinder()); 169 } 170 171 sp<IMemory> getImageAtIndex(int index, int colorFormat, bool metaOnly, bool thumbnail) 172 { 173 ALOGV("getImageAtIndex: index %d, colorFormat(%d) metaOnly(%d) thumbnail(%d)", 174 index, colorFormat, metaOnly, thumbnail); 175 Parcel data, reply; 176 data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); 177 data.writeInt32(index); 178 data.writeInt32(colorFormat); 179 data.writeInt32(metaOnly); 180 data.writeInt32(thumbnail); 181 #ifndef DISABLE_GROUP_SCHEDULE_HACK 182 sendSchedPolicy(data); 183 #endif 184 remote()->transact(GET_IMAGE_AT_INDEX, data, &reply); 185 status_t ret = reply.readInt32(); 186 if (ret != NO_ERROR) { 187 return NULL; 188 } 189 return interface_cast<IMemory>(reply.readStrongBinder()); 190 } 191 192 sp<IMemory> getImageRectAtIndex( 193 int index, int colorFormat, int left, int top, int right, int bottom) 194 { 195 ALOGV("getImageRectAtIndex: index %d, colorFormat(%d) rect {%d, %d, %d, %d}", 196 index, colorFormat, left, top, right, bottom); 197 Parcel data, reply; 198 data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); 199 data.writeInt32(index); 200 data.writeInt32(colorFormat); 201 data.writeInt32(left); 202 data.writeInt32(top); 203 data.writeInt32(right); 204 data.writeInt32(bottom); 205 #ifndef DISABLE_GROUP_SCHEDULE_HACK 206 sendSchedPolicy(data); 207 #endif 208 remote()->transact(GET_IMAGE_RECT_AT_INDEX, data, &reply); 209 status_t ret = reply.readInt32(); 210 if (ret != NO_ERROR) { 211 return NULL; 212 } 213 return interface_cast<IMemory>(reply.readStrongBinder()); 214 } 215 216 status_t getFrameAtIndex(std::vector<sp<IMemory> > *frames, 217 int frameIndex, int numFrames, int colorFormat, bool metaOnly) 218 { 219 ALOGV("getFrameAtIndex: frameIndex(%d), numFrames(%d), colorFormat(%d) metaOnly(%d)", 220 frameIndex, numFrames, colorFormat, metaOnly); 221 Parcel data, reply; 222 data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); 223 data.writeInt32(frameIndex); 224 data.writeInt32(numFrames); 225 data.writeInt32(colorFormat); 226 data.writeInt32(metaOnly); 227 #ifndef DISABLE_GROUP_SCHEDULE_HACK 228 sendSchedPolicy(data); 229 #endif 230 remote()->transact(GET_FRAME_AT_INDEX, data, &reply); 231 status_t ret = reply.readInt32(); 232 if (ret != NO_ERROR) { 233 return ret; 234 } 235 int retNumFrames = reply.readInt32(); 236 if (retNumFrames < numFrames) { 237 numFrames = retNumFrames; 238 } 239 for (int i = 0; i < numFrames; i++) { 240 frames->push_back(interface_cast<IMemory>(reply.readStrongBinder())); 241 } 242 return OK; 243 } 244 245 sp<IMemory> extractAlbumArt() 246 { 247 Parcel data, reply; 248 data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); 249 #ifndef DISABLE_GROUP_SCHEDULE_HACK 250 sendSchedPolicy(data); 251 #endif 252 remote()->transact(EXTRACT_ALBUM_ART, data, &reply); 253 status_t ret = reply.readInt32(); 254 if (ret != NO_ERROR) { 255 return NULL; 256 } 257 return interface_cast<IMemory>(reply.readStrongBinder()); 258 } 259 260 const char* extractMetadata(int keyCode) 261 { 262 Parcel data, reply; 263 data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); 264 #ifndef DISABLE_GROUP_SCHEDULE_HACK 265 sendSchedPolicy(data); 266 #endif 267 data.writeInt32(keyCode); 268 remote()->transact(EXTRACT_METADATA, data, &reply); 269 status_t ret = reply.readInt32(); 270 if (ret != NO_ERROR) { 271 return NULL; 272 } 273 const char* str = reply.readCString(); 274 if (str != NULL) { 275 String8 value(str); 276 if (mMetadata.indexOfKey(keyCode) < 0) { 277 mMetadata.add(keyCode, value); 278 } else { 279 mMetadata.replaceValueFor(keyCode, value); 280 } 281 return mMetadata.valueFor(keyCode).string(); 282 } else { 283 return NULL; 284 } 285 } 286 287 private: 288 KeyedVector<int, String8> mMetadata; 289 }; 290 291 IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.media.IMediaMetadataRetriever"); 292 293 // ---------------------------------------------------------------------- 294 295 status_t BnMediaMetadataRetriever::onTransact( 296 uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) 297 { 298 switch (code) { 299 case DISCONNECT: { 300 CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); 301 disconnect(); 302 return NO_ERROR; 303 } break; 304 case SET_DATA_SOURCE_URL: { 305 CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); 306 307 sp<IMediaHTTPService> httpService; 308 if (data.readInt32()) { 309 httpService = 310 interface_cast<IMediaHTTPService>(data.readStrongBinder()); 311 } 312 313 const char* srcUrl = data.readCString(); 314 315 if (httpService == NULL || srcUrl == NULL) { 316 reply->writeInt32(BAD_VALUE); 317 return NO_ERROR; 318 } 319 320 KeyedVector<String8, String8> headers; 321 size_t numHeaders = (size_t) data.readInt64(); 322 for (size_t i = 0; i < numHeaders; ++i) { 323 String8 key = data.readString8(); 324 String8 value = data.readString8(); 325 headers.add(key, value); 326 } 327 328 reply->writeInt32( 329 setDataSource( 330 httpService, srcUrl, numHeaders > 0 ? &headers : NULL)); 331 332 return NO_ERROR; 333 } break; 334 case SET_DATA_SOURCE_FD: { 335 CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); 336 int fd = data.readFileDescriptor(); 337 int64_t offset = data.readInt64(); 338 int64_t length = data.readInt64(); 339 reply->writeInt32(setDataSource(fd, offset, length)); 340 return NO_ERROR; 341 } break; 342 case SET_DATA_SOURCE_CALLBACK: { 343 CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); 344 sp<IDataSource> source = 345 interface_cast<IDataSource>(data.readStrongBinder()); 346 if (source == NULL) { 347 reply->writeInt32(BAD_VALUE); 348 } else { 349 int32_t hasMime = data.readInt32(); 350 const char *mime = NULL; 351 if (hasMime) { 352 mime = data.readCString(); 353 } 354 reply->writeInt32(setDataSource(source, mime)); 355 } 356 return NO_ERROR; 357 } break; 358 case GET_FRAME_AT_TIME: { 359 CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); 360 int64_t timeUs = data.readInt64(); 361 int option = data.readInt32(); 362 int colorFormat = data.readInt32(); 363 bool metaOnly = (data.readInt32() != 0); 364 ALOGV("getTimeAtTime: time(%" PRId64 " us), option(%d), colorFormat(%d), metaOnly(%d)", 365 timeUs, option, colorFormat, metaOnly); 366 #ifndef DISABLE_GROUP_SCHEDULE_HACK 367 setSchedPolicy(data); 368 #endif 369 sp<IMemory> bitmap = getFrameAtTime(timeUs, option, colorFormat, metaOnly); 370 if (bitmap != 0) { // Don't send NULL across the binder interface 371 reply->writeInt32(NO_ERROR); 372 reply->writeStrongBinder(IInterface::asBinder(bitmap)); 373 } else { 374 reply->writeInt32(UNKNOWN_ERROR); 375 } 376 #ifndef DISABLE_GROUP_SCHEDULE_HACK 377 restoreSchedPolicy(); 378 #endif 379 return NO_ERROR; 380 } break; 381 case GET_IMAGE_AT_INDEX: { 382 CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); 383 int index = data.readInt32(); 384 int colorFormat = data.readInt32(); 385 bool metaOnly = (data.readInt32() != 0); 386 bool thumbnail = (data.readInt32() != 0); 387 ALOGV("getImageAtIndex: index(%d), colorFormat(%d), metaOnly(%d), thumbnail(%d)", 388 index, colorFormat, metaOnly, thumbnail); 389 #ifndef DISABLE_GROUP_SCHEDULE_HACK 390 setSchedPolicy(data); 391 #endif 392 sp<IMemory> bitmap = getImageAtIndex(index, colorFormat, metaOnly, thumbnail); 393 if (bitmap != 0) { // Don't send NULL across the binder interface 394 reply->writeInt32(NO_ERROR); 395 reply->writeStrongBinder(IInterface::asBinder(bitmap)); 396 } else { 397 reply->writeInt32(UNKNOWN_ERROR); 398 } 399 #ifndef DISABLE_GROUP_SCHEDULE_HACK 400 restoreSchedPolicy(); 401 #endif 402 return NO_ERROR; 403 } break; 404 405 case GET_IMAGE_RECT_AT_INDEX: { 406 CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); 407 int index = data.readInt32(); 408 int colorFormat = data.readInt32(); 409 int left = data.readInt32(); 410 int top = data.readInt32(); 411 int right = data.readInt32(); 412 int bottom = data.readInt32(); 413 ALOGV("getImageRectAtIndex: index(%d), colorFormat(%d), rect {%d, %d, %d, %d}", 414 index, colorFormat, left, top, right, bottom); 415 #ifndef DISABLE_GROUP_SCHEDULE_HACK 416 setSchedPolicy(data); 417 #endif 418 sp<IMemory> bitmap = getImageRectAtIndex( 419 index, colorFormat, left, top, right, bottom); 420 if (bitmap != 0) { // Don't send NULL across the binder interface 421 reply->writeInt32(NO_ERROR); 422 reply->writeStrongBinder(IInterface::asBinder(bitmap)); 423 } else { 424 reply->writeInt32(UNKNOWN_ERROR); 425 } 426 #ifndef DISABLE_GROUP_SCHEDULE_HACK 427 restoreSchedPolicy(); 428 #endif 429 return NO_ERROR; 430 } break; 431 432 case GET_FRAME_AT_INDEX: { 433 CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); 434 int frameIndex = data.readInt32(); 435 int numFrames = data.readInt32(); 436 int colorFormat = data.readInt32(); 437 bool metaOnly = (data.readInt32() != 0); 438 ALOGV("getFrameAtIndex: frameIndex(%d), numFrames(%d), colorFormat(%d), metaOnly(%d)", 439 frameIndex, numFrames, colorFormat, metaOnly); 440 #ifndef DISABLE_GROUP_SCHEDULE_HACK 441 setSchedPolicy(data); 442 #endif 443 std::vector<sp<IMemory> > frames; 444 status_t err = getFrameAtIndex( 445 &frames, frameIndex, numFrames, colorFormat, metaOnly); 446 reply->writeInt32(err); 447 if (OK == err) { 448 reply->writeInt32(frames.size()); 449 for (size_t i = 0; i < frames.size(); i++) { 450 reply->writeStrongBinder(IInterface::asBinder(frames[i])); 451 } 452 } 453 #ifndef DISABLE_GROUP_SCHEDULE_HACK 454 restoreSchedPolicy(); 455 #endif 456 return NO_ERROR; 457 } break; 458 case EXTRACT_ALBUM_ART: { 459 CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); 460 #ifndef DISABLE_GROUP_SCHEDULE_HACK 461 setSchedPolicy(data); 462 #endif 463 sp<IMemory> albumArt = extractAlbumArt(); 464 if (albumArt != 0) { // Don't send NULL across the binder interface 465 reply->writeInt32(NO_ERROR); 466 reply->writeStrongBinder(IInterface::asBinder(albumArt)); 467 } else { 468 reply->writeInt32(UNKNOWN_ERROR); 469 } 470 #ifndef DISABLE_GROUP_SCHEDULE_HACK 471 restoreSchedPolicy(); 472 #endif 473 return NO_ERROR; 474 } break; 475 case EXTRACT_METADATA: { 476 CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); 477 #ifndef DISABLE_GROUP_SCHEDULE_HACK 478 setSchedPolicy(data); 479 #endif 480 int keyCode = data.readInt32(); 481 const char* value = extractMetadata(keyCode); 482 if (value != NULL) { // Don't send NULL across the binder interface 483 reply->writeInt32(NO_ERROR); 484 reply->writeCString(value); 485 } else { 486 reply->writeInt32(UNKNOWN_ERROR); 487 } 488 #ifndef DISABLE_GROUP_SCHEDULE_HACK 489 restoreSchedPolicy(); 490 #endif 491 return NO_ERROR; 492 } break; 493 default: 494 return BBinder::onTransact(code, data, reply, flags); 495 } 496 } 497 498 // ---------------------------------------------------------------------------- 499 500 } // namespace android 501