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