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