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 <stdint.h>
     19 #include <sys/types.h>
     20 #include <binder/Parcel.h>
     21 #include <media/IMediaMetadataRetriever.h>
     22 #include <utils/String8.h>
     23 #include <utils/KeyedVector.h>
     24 
     25 // The binder is supposed to propagate the scheduler group across
     26 // the binder interface so that remote calls are executed with
     27 // the same priority as local calls. This is currently not working
     28 // so this change puts in a temporary hack to fix the issue with
     29 // metadata retrieval which can be a huge CPU hit if done on a
     30 // foreground thread.
     31 #ifndef DISABLE_GROUP_SCHEDULE_HACK
     32 
     33 #undef LOG_TAG
     34 #define LOG_TAG "IMediaMetadataRetriever"
     35 #include <utils/Log.h>
     36 #include <cutils/sched_policy.h>
     37 
     38 namespace android {
     39 
     40 static void sendSchedPolicy(Parcel& data)
     41 {
     42     SchedPolicy policy;
     43     get_sched_policy(gettid(), &policy);
     44     data.writeInt32(policy);
     45 }
     46 
     47 static void setSchedPolicy(const Parcel& data)
     48 {
     49     SchedPolicy policy = (SchedPolicy) data.readInt32();
     50     set_sched_policy(gettid(), policy);
     51 }
     52 static void restoreSchedPolicy()
     53 {
     54     set_sched_policy(gettid(), SP_FOREGROUND);
     55 }
     56 }; // end namespace android
     57 #endif
     58 
     59 namespace android {
     60 
     61 enum {
     62     DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
     63     SET_DATA_SOURCE_URL,
     64     SET_DATA_SOURCE_FD,
     65     GET_FRAME_AT_TIME,
     66     EXTRACT_ALBUM_ART,
     67     EXTRACT_METADATA,
     68 };
     69 
     70 class BpMediaMetadataRetriever: public BpInterface<IMediaMetadataRetriever>
     71 {
     72 public:
     73     BpMediaMetadataRetriever(const sp<IBinder>& impl)
     74         : BpInterface<IMediaMetadataRetriever>(impl)
     75     {
     76     }
     77 
     78     // disconnect from media metadata retriever service
     79     void disconnect()
     80     {
     81         Parcel data, reply;
     82         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
     83         remote()->transact(DISCONNECT, data, &reply);
     84     }
     85 
     86     status_t setDataSource(
     87             const char *srcUrl, const KeyedVector<String8, String8> *headers)
     88     {
     89         Parcel data, reply;
     90         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
     91         data.writeCString(srcUrl);
     92 
     93         if (headers == NULL) {
     94             data.writeInt32(0);
     95         } else {
     96             // serialize the headers
     97             data.writeInt32(headers->size());
     98             for (size_t i = 0; i < headers->size(); ++i) {
     99                 data.writeString8(headers->keyAt(i));
    100                 data.writeString8(headers->valueAt(i));
    101             }
    102         }
    103 
    104         remote()->transact(SET_DATA_SOURCE_URL, data, &reply);
    105         return reply.readInt32();
    106     }
    107 
    108     status_t setDataSource(int fd, int64_t offset, int64_t length)
    109     {
    110         Parcel data, reply;
    111         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
    112         data.writeFileDescriptor(fd);
    113         data.writeInt64(offset);
    114         data.writeInt64(length);
    115         remote()->transact(SET_DATA_SOURCE_FD, data, &reply);
    116         return reply.readInt32();
    117     }
    118 
    119     sp<IMemory> getFrameAtTime(int64_t timeUs, int option)
    120     {
    121         ALOGV("getTimeAtTime: time(%lld us) and option(%d)", timeUs, option);
    122         Parcel data, reply;
    123         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
    124         data.writeInt64(timeUs);
    125         data.writeInt32(option);
    126 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    127         sendSchedPolicy(data);
    128 #endif
    129         remote()->transact(GET_FRAME_AT_TIME, data, &reply);
    130         status_t ret = reply.readInt32();
    131         if (ret != NO_ERROR) {
    132             return NULL;
    133         }
    134         return interface_cast<IMemory>(reply.readStrongBinder());
    135     }
    136 
    137     sp<IMemory> extractAlbumArt()
    138     {
    139         Parcel data, reply;
    140         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
    141 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    142         sendSchedPolicy(data);
    143 #endif
    144         remote()->transact(EXTRACT_ALBUM_ART, data, &reply);
    145         status_t ret = reply.readInt32();
    146         if (ret != NO_ERROR) {
    147             return NULL;
    148         }
    149         return interface_cast<IMemory>(reply.readStrongBinder());
    150     }
    151 
    152     const char* extractMetadata(int keyCode)
    153     {
    154         Parcel data, reply;
    155         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
    156 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    157         sendSchedPolicy(data);
    158 #endif
    159         data.writeInt32(keyCode);
    160         remote()->transact(EXTRACT_METADATA, data, &reply);
    161         status_t ret = reply.readInt32();
    162         if (ret != NO_ERROR) {
    163             return NULL;
    164         }
    165         const char* str = reply.readCString();
    166         if (str != NULL) {
    167             String8 value(str);
    168             if (mMetadata.indexOfKey(keyCode) < 0) {
    169                 mMetadata.add(keyCode, value);
    170             } else {
    171                 mMetadata.replaceValueFor(keyCode, value);
    172             }
    173             return mMetadata.valueFor(keyCode).string();
    174         } else {
    175             return NULL;
    176         }
    177     }
    178 
    179 private:
    180     KeyedVector<int, String8> mMetadata;
    181 };
    182 
    183 IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.media.IMediaMetadataRetriever");
    184 
    185 // ----------------------------------------------------------------------
    186 
    187 status_t BnMediaMetadataRetriever::onTransact(
    188     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
    189 {
    190     switch (code) {
    191         case DISCONNECT: {
    192             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
    193             disconnect();
    194             return NO_ERROR;
    195         } break;
    196         case SET_DATA_SOURCE_URL: {
    197             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
    198             const char* srcUrl = data.readCString();
    199 
    200             KeyedVector<String8, String8> headers;
    201             int32_t numHeaders = data.readInt32();
    202             for (int i = 0; i < numHeaders; ++i) {
    203                 String8 key = data.readString8();
    204                 String8 value = data.readString8();
    205                 headers.add(key, value);
    206             }
    207 
    208             reply->writeInt32(
    209                     setDataSource(srcUrl, numHeaders > 0 ? &headers : NULL));
    210 
    211             return NO_ERROR;
    212         } break;
    213         case SET_DATA_SOURCE_FD: {
    214             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
    215             int fd = dup(data.readFileDescriptor());
    216             int64_t offset = data.readInt64();
    217             int64_t length = data.readInt64();
    218             reply->writeInt32(setDataSource(fd, offset, length));
    219             return NO_ERROR;
    220         } break;
    221         case GET_FRAME_AT_TIME: {
    222             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
    223             int64_t timeUs = data.readInt64();
    224             int option = data.readInt32();
    225             ALOGV("getTimeAtTime: time(%lld us) and option(%d)", timeUs, option);
    226 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    227             setSchedPolicy(data);
    228 #endif
    229             sp<IMemory> bitmap = getFrameAtTime(timeUs, option);
    230             if (bitmap != 0) {  // Don't send NULL across the binder interface
    231                 reply->writeInt32(NO_ERROR);
    232                 reply->writeStrongBinder(bitmap->asBinder());
    233             } else {
    234                 reply->writeInt32(UNKNOWN_ERROR);
    235             }
    236 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    237             restoreSchedPolicy();
    238 #endif
    239             return NO_ERROR;
    240         } break;
    241         case EXTRACT_ALBUM_ART: {
    242             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
    243 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    244             setSchedPolicy(data);
    245 #endif
    246             sp<IMemory> albumArt = extractAlbumArt();
    247             if (albumArt != 0) {  // Don't send NULL across the binder interface
    248                 reply->writeInt32(NO_ERROR);
    249                 reply->writeStrongBinder(albumArt->asBinder());
    250             } else {
    251                 reply->writeInt32(UNKNOWN_ERROR);
    252             }
    253 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    254             restoreSchedPolicy();
    255 #endif
    256             return NO_ERROR;
    257         } break;
    258         case EXTRACT_METADATA: {
    259             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
    260 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    261             setSchedPolicy(data);
    262 #endif
    263             int keyCode = data.readInt32();
    264             const char* value = extractMetadata(keyCode);
    265             if (value != NULL) {  // Don't send NULL across the binder interface
    266                 reply->writeInt32(NO_ERROR);
    267                 reply->writeCString(value);
    268             } else {
    269                 reply->writeInt32(UNKNOWN_ERROR);
    270             }
    271 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    272             restoreSchedPolicy();
    273 #endif
    274             return NO_ERROR;
    275         } break;
    276         default:
    277             return BBinder::onTransact(code, data, reply, flags);
    278     }
    279 }
    280 
    281 // ----------------------------------------------------------------------------
    282 
    283 }; // namespace android
    284