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