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