Home | History | Annotate | Download | only in libmedia
      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