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