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 <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 
     42 namespace android {
     43 
     44 static void sendSchedPolicy(Parcel& data)
     45 {
     46     SchedPolicy policy;
     47     get_sched_policy(gettid(), &policy);
     48     data.writeInt32(policy);
     49 }
     50 
     51 static void setSchedPolicy(const Parcel& data)
     52 {
     53     SchedPolicy policy = (SchedPolicy) data.readInt32();
     54     set_sched_policy(gettid(), policy);
     55 }
     56 static void restoreSchedPolicy()
     57 {
     58     set_sched_policy(gettid(), SP_FOREGROUND);
     59 }
     60 }; // end namespace android
     61 #endif
     62 
     63 namespace android {
     64 
     65 enum {
     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     EXTRACT_ALBUM_ART,
     72     EXTRACT_METADATA,
     73 };
     74 
     75 class BpMediaMetadataRetriever: public BpInterface<IMediaMetadataRetriever>
     76 {
     77 public:
     78     explicit BpMediaMetadataRetriever(const sp<IBinder>& impl)
     79         : BpInterface<IMediaMetadataRetriever>(impl)
     80     {
     81     }
     82 
     83     // disconnect from media metadata retriever service
     84     void disconnect()
     85     {
     86         Parcel data, reply;
     87         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
     88         remote()->transact(DISCONNECT, data, &reply);
     89     }
     90 
     91     status_t setDataSource(
     92             const sp<IMediaHTTPService> &httpService,
     93             const char *srcUrl,
     94             const KeyedVector<String8, String8> *headers)
     95     {
     96         Parcel data, reply;
     97         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
     98         data.writeInt32(httpService != NULL);
     99         if (httpService != NULL) {
    100             data.writeStrongBinder(IInterface::asBinder(httpService));
    101         }
    102         data.writeCString(srcUrl);
    103 
    104         if (headers == NULL) {
    105             data.writeInt32(0);
    106         } else {
    107             // serialize the headers
    108             data.writeInt64(headers->size());
    109             for (size_t i = 0; i < headers->size(); ++i) {
    110                 data.writeString8(headers->keyAt(i));
    111                 data.writeString8(headers->valueAt(i));
    112             }
    113         }
    114 
    115         remote()->transact(SET_DATA_SOURCE_URL, data, &reply);
    116         return reply.readInt32();
    117     }
    118 
    119     status_t setDataSource(int fd, int64_t offset, int64_t length)
    120     {
    121         Parcel data, reply;
    122         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
    123         data.writeFileDescriptor(fd);
    124         data.writeInt64(offset);
    125         data.writeInt64(length);
    126         remote()->transact(SET_DATA_SOURCE_FD, data, &reply);
    127         return reply.readInt32();
    128     }
    129 
    130     status_t setDataSource(const sp<IDataSource>& source)
    131     {
    132         Parcel data, reply;
    133         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
    134         data.writeStrongBinder(IInterface::asBinder(source));
    135         remote()->transact(SET_DATA_SOURCE_CALLBACK, data, &reply);
    136         return reply.readInt32();
    137     }
    138 
    139     sp<IMemory> getFrameAtTime(int64_t timeUs, int option)
    140     {
    141         ALOGV("getTimeAtTime: time(%" PRId64 " us) and option(%d)", timeUs, option);
    142         Parcel data, reply;
    143         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
    144         data.writeInt64(timeUs);
    145         data.writeInt32(option);
    146 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    147         sendSchedPolicy(data);
    148 #endif
    149         remote()->transact(GET_FRAME_AT_TIME, data, &reply);
    150         status_t ret = reply.readInt32();
    151         if (ret != NO_ERROR) {
    152             return NULL;
    153         }
    154         return interface_cast<IMemory>(reply.readStrongBinder());
    155     }
    156 
    157     sp<IMemory> extractAlbumArt()
    158     {
    159         Parcel data, reply;
    160         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
    161 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    162         sendSchedPolicy(data);
    163 #endif
    164         remote()->transact(EXTRACT_ALBUM_ART, data, &reply);
    165         status_t ret = reply.readInt32();
    166         if (ret != NO_ERROR) {
    167             return NULL;
    168         }
    169         return interface_cast<IMemory>(reply.readStrongBinder());
    170     }
    171 
    172     const char* extractMetadata(int keyCode)
    173     {
    174         Parcel data, reply;
    175         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
    176 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    177         sendSchedPolicy(data);
    178 #endif
    179         data.writeInt32(keyCode);
    180         remote()->transact(EXTRACT_METADATA, data, &reply);
    181         status_t ret = reply.readInt32();
    182         if (ret != NO_ERROR) {
    183             return NULL;
    184         }
    185         const char* str = reply.readCString();
    186         if (str != NULL) {
    187             String8 value(str);
    188             if (mMetadata.indexOfKey(keyCode) < 0) {
    189                 mMetadata.add(keyCode, value);
    190             } else {
    191                 mMetadata.replaceValueFor(keyCode, value);
    192             }
    193             return mMetadata.valueFor(keyCode).string();
    194         } else {
    195             return NULL;
    196         }
    197     }
    198 
    199 private:
    200     KeyedVector<int, String8> mMetadata;
    201 };
    202 
    203 IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.media.IMediaMetadataRetriever");
    204 
    205 // ----------------------------------------------------------------------
    206 
    207 status_t BnMediaMetadataRetriever::onTransact(
    208     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
    209 {
    210     switch (code) {
    211         case DISCONNECT: {
    212             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
    213             disconnect();
    214             return NO_ERROR;
    215         } break;
    216         case SET_DATA_SOURCE_URL: {
    217             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
    218 
    219             sp<IMediaHTTPService> httpService;
    220             if (data.readInt32()) {
    221                 httpService =
    222                     interface_cast<IMediaHTTPService>(data.readStrongBinder());
    223             }
    224 
    225             const char* srcUrl = data.readCString();
    226 
    227             if (httpService == NULL || srcUrl == NULL) {
    228                 reply->writeInt32(BAD_VALUE);
    229                 return NO_ERROR;
    230             }
    231 
    232             KeyedVector<String8, String8> headers;
    233             size_t numHeaders = (size_t) data.readInt64();
    234             for (size_t i = 0; i < numHeaders; ++i) {
    235                 String8 key = data.readString8();
    236                 String8 value = data.readString8();
    237                 headers.add(key, value);
    238             }
    239 
    240             reply->writeInt32(
    241                     setDataSource(
    242                         httpService, srcUrl, numHeaders > 0 ? &headers : NULL));
    243 
    244             return NO_ERROR;
    245         } break;
    246         case SET_DATA_SOURCE_FD: {
    247             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
    248             int fd = data.readFileDescriptor();
    249             int64_t offset = data.readInt64();
    250             int64_t length = data.readInt64();
    251             reply->writeInt32(setDataSource(fd, offset, length));
    252             return NO_ERROR;
    253         } break;
    254         case SET_DATA_SOURCE_CALLBACK: {
    255             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
    256             sp<IDataSource> source =
    257                 interface_cast<IDataSource>(data.readStrongBinder());
    258             if (source == NULL) {
    259                 reply->writeInt32(BAD_VALUE);
    260             } else {
    261                 reply->writeInt32(setDataSource(source));
    262             }
    263             return NO_ERROR;
    264         } break;
    265         case GET_FRAME_AT_TIME: {
    266             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
    267             int64_t timeUs = data.readInt64();
    268             int option = data.readInt32();
    269             ALOGV("getTimeAtTime: time(%" PRId64 " us) and option(%d)", timeUs, option);
    270 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    271             setSchedPolicy(data);
    272 #endif
    273             sp<IMemory> bitmap = getFrameAtTime(timeUs, option);
    274             if (bitmap != 0) {  // Don't send NULL across the binder interface
    275                 reply->writeInt32(NO_ERROR);
    276                 reply->writeStrongBinder(IInterface::asBinder(bitmap));
    277             } else {
    278                 reply->writeInt32(UNKNOWN_ERROR);
    279             }
    280 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    281             restoreSchedPolicy();
    282 #endif
    283             return NO_ERROR;
    284         } break;
    285         case EXTRACT_ALBUM_ART: {
    286             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
    287 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    288             setSchedPolicy(data);
    289 #endif
    290             sp<IMemory> albumArt = extractAlbumArt();
    291             if (albumArt != 0) {  // Don't send NULL across the binder interface
    292                 reply->writeInt32(NO_ERROR);
    293                 reply->writeStrongBinder(IInterface::asBinder(albumArt));
    294             } else {
    295                 reply->writeInt32(UNKNOWN_ERROR);
    296             }
    297 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    298             restoreSchedPolicy();
    299 #endif
    300             return NO_ERROR;
    301         } break;
    302         case EXTRACT_METADATA: {
    303             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
    304 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    305             setSchedPolicy(data);
    306 #endif
    307             int keyCode = data.readInt32();
    308             const char* value = extractMetadata(keyCode);
    309             if (value != NULL) {  // Don't send NULL across the binder interface
    310                 reply->writeInt32(NO_ERROR);
    311                 reply->writeCString(value);
    312             } else {
    313                 reply->writeInt32(UNKNOWN_ERROR);
    314             }
    315 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    316             restoreSchedPolicy();
    317 #endif
    318             return NO_ERROR;
    319         } break;
    320         default:
    321             return BBinder::onTransact(code, data, reply, flags);
    322     }
    323 }
    324 
    325 // ----------------------------------------------------------------------------
    326 
    327 } // namespace android
    328