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 <SkBitmap.h>
     22 #include <media/IMediaMetadataRetriever.h>
     23 #include <utils/String8.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         LOGV("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         return reply.readCString();
    166     }
    167 };
    168 
    169 IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.media.IMediaMetadataRetriever");
    170 
    171 // ----------------------------------------------------------------------
    172 
    173 status_t BnMediaMetadataRetriever::onTransact(
    174     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
    175 {
    176     switch (code) {
    177         case DISCONNECT: {
    178             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
    179             disconnect();
    180             return NO_ERROR;
    181         } break;
    182         case SET_DATA_SOURCE_URL: {
    183             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
    184             const char* srcUrl = data.readCString();
    185 
    186             KeyedVector<String8, String8> headers;
    187             int32_t numHeaders = data.readInt32();
    188             for (int i = 0; i < numHeaders; ++i) {
    189                 String8 key = data.readString8();
    190                 String8 value = data.readString8();
    191                 headers.add(key, value);
    192             }
    193 
    194             reply->writeInt32(
    195                     setDataSource(srcUrl, numHeaders > 0 ? &headers : NULL));
    196 
    197             return NO_ERROR;
    198         } break;
    199         case SET_DATA_SOURCE_FD: {
    200             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
    201             int fd = dup(data.readFileDescriptor());
    202             int64_t offset = data.readInt64();
    203             int64_t length = data.readInt64();
    204             reply->writeInt32(setDataSource(fd, offset, length));
    205             return NO_ERROR;
    206         } break;
    207         case GET_FRAME_AT_TIME: {
    208             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
    209             int64_t timeUs = data.readInt64();
    210             int option = data.readInt32();
    211             LOGV("getTimeAtTime: time(%lld us) and option(%d)", timeUs, option);
    212 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    213             setSchedPolicy(data);
    214 #endif
    215             sp<IMemory> bitmap = getFrameAtTime(timeUs, option);
    216             if (bitmap != 0) {  // Don't send NULL across the binder interface
    217                 reply->writeInt32(NO_ERROR);
    218                 reply->writeStrongBinder(bitmap->asBinder());
    219             } else {
    220                 reply->writeInt32(UNKNOWN_ERROR);
    221             }
    222 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    223             restoreSchedPolicy();
    224 #endif
    225             return NO_ERROR;
    226         } break;
    227         case EXTRACT_ALBUM_ART: {
    228             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
    229 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    230             setSchedPolicy(data);
    231 #endif
    232             sp<IMemory> albumArt = extractAlbumArt();
    233             if (albumArt != 0) {  // Don't send NULL across the binder interface
    234                 reply->writeInt32(NO_ERROR);
    235                 reply->writeStrongBinder(albumArt->asBinder());
    236             } else {
    237                 reply->writeInt32(UNKNOWN_ERROR);
    238             }
    239 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    240             restoreSchedPolicy();
    241 #endif
    242             return NO_ERROR;
    243         } break;
    244         case EXTRACT_METADATA: {
    245             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
    246 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    247             setSchedPolicy(data);
    248 #endif
    249             int keyCode = data.readInt32();
    250             const char* value = extractMetadata(keyCode);
    251             if (value != NULL) {  // Don't send NULL across the binder interface
    252                 reply->writeInt32(NO_ERROR);
    253                 reply->writeCString(value);
    254             } else {
    255                 reply->writeInt32(UNKNOWN_ERROR);
    256             }
    257 #ifndef DISABLE_GROUP_SCHEDULE_HACK
    258             restoreSchedPolicy();
    259 #endif
    260             return NO_ERROR;
    261         } break;
    262         default:
    263             return BBinder::onTransact(code, data, reply, flags);
    264     }
    265 }
    266 
    267 // ----------------------------------------------------------------------------
    268 
    269 }; // namespace android
    270