diff options
-rw-r--r-- | include/media/IMediaPlayer.h | 7 | ||||
-rw-r--r-- | include/media/mediaplayer.h | 3 | ||||
-rw-r--r-- | media/java/android/media/MediaPlayer.java | 51 | ||||
-rw-r--r-- | media/java/android/media/Metadata.java | 1 | ||||
-rw-r--r-- | media/jni/android_media_MediaPlayer.cpp | 21 | ||||
-rw-r--r-- | media/libmedia/IMediaPlayer.cpp | 15 | ||||
-rw-r--r-- | media/libmedia/mediaplayer.cpp | 11 | ||||
-rw-r--r-- | media/libmediaplayerservice/MediaPlayerService.cpp | 185 | ||||
-rw-r--r-- | media/libmediaplayerservice/MediaPlayerService.h | 15 |
9 files changed, 295 insertions, 14 deletions
diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h index 85aeb30..074125f 100644 --- a/include/media/IMediaPlayer.h +++ b/include/media/IMediaPlayer.h @@ -52,8 +52,13 @@ public: // @param request Parcel that must start with the media player // interface token. // @param[out] reply Parcel to hold the reply data. Cannot be null. - // @return OK if the invocation was made. PERMISSION_DENIED otherwise. + // @return OK if the invocation was made successfully. virtual status_t invoke(const Parcel& request, Parcel *reply) = 0; + + // Set a new metadata filter. + // @param filter A set of allow and drop rules serialized in a Parcel. + // @return OK if the invocation was made successfully. + virtual status_t setMetadataFilter(const Parcel& filter) = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h index dd8ea19..8326a21 100644 --- a/include/media/mediaplayer.h +++ b/include/media/mediaplayer.h @@ -97,6 +97,8 @@ enum media_info_type { MEDIA_INFO_BAD_INTERLEAVING = 800, // The media is not seekable (e.g live stream). MEDIA_INFO_NOT_SEEKABLE = 801, + // New media metadata is available. + MEDIA_INFO_METADATA_UPDATE = 802, }; @@ -152,6 +154,7 @@ public: static sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); static sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); status_t invoke(const Parcel& request, Parcel *reply); + status_t setMetadataFilter(const Parcel& filter); private: void clear_l(); status_t seekTo_l(int msec); diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 139fb41..306ea81 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -937,25 +937,49 @@ public class MediaPlayer /** * Set a filter for the metadata update notification and update * retrieval. The caller provides 2 set of metadata keys, allowed - * and disallowed. The disallow set always takes the precedence - * over the allowed one. + * and blocked. The blocked set always takes precedence over the + * allowed one. * Metadata.MATCH_ALL and Metadata.MATCH_NONE are 2 sets available as - * shorthands to allow/disallow all or no metadata. + * shorthands to allow/block all or no metadata. * * By default, there is no filter set. * * @param allow Is the set of metadata the client is interested - * receiving new notifications for. - * @param disallow Is the set of metadata the client is not interested - * receiving new notifications for. + * in receiving new notifications for. + * @param block Is the set of metadata the client is not interested + * in receiving new notifications for. * @return The call status code. * // FIXME: unhide. * {@hide} */ - public int setMetadataFilter(Set<Integer> allow, Set<Integer> disallow) { - // FIXME: Implement. - return 0; + public int setMetadataFilter(Set<Integer> allow, Set<Integer> block) { + // Do our serialization manually instead of calling + // Parcel.writeArray since the sets are made of the same type + // we avoid paying the price of calling writeValue (used by + // writeArray) which burns an extra int per element to encode + // the type. + Parcel request = newRequest(); + + // The parcel starts already with an interface token. There + // are 2 filters. Each one starts with a 4bytes number to + // store the len followed by a number of int (4 bytes as well) + // representing the metadata type. + int capacity = request.dataSize() + 4 * (1 + allow.size() + 1 + block.size()); + + if (request.dataCapacity() < capacity) { + request.setDataCapacity(capacity); + } + + request.writeInt(allow.size()); + for(Integer t: allow) { + request.writeInt(t); + } + request.writeInt(block.size()); + for(Integer t: block) { + request.writeInt(t); + } + return native_setMetadataFilter(request); } /** @@ -1045,6 +1069,15 @@ public class MediaPlayer */ private native final int native_invoke(Parcel request, Parcel reply); + /** + * @param request Parcel with the 2 serialized lists of allowed + * metadata types followed by the one to be + * dropped. Each list starts with an integer + * indicating the number of metadata type elements. + * @return The status code. + */ + private native final int native_setMetadataFilter(Parcel request); + private native final void native_setup(Object mediaplayer_this); private native final void native_finalize(); diff --git a/media/java/android/media/Metadata.java b/media/java/android/media/Metadata.java index 80748a9..7bc4e9c 100644 --- a/media/java/android/media/Metadata.java +++ b/media/java/android/media/Metadata.java @@ -56,6 +56,7 @@ public class Metadata // public static final int ANY = 0; // Never used for metadata returned, only for filtering. + // Keep in sync with kAny in MediaPlayerService.cpp // TODO: Should we use numbers compatible with the metadata retriever? public static final int TITLE = 1; // String diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 2c08c16..19a2a41 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -455,18 +455,34 @@ android_media_MediaPlayer_invoke(JNIEnv *env, jobject thiz, sp<MediaPlayer> media_player = getMediaPlayer(env, thiz); if (media_player == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", NULL); + return UNKNOWN_ERROR; } Parcel *request = parcelForJavaObject(env, java_request); Parcel *reply = parcelForJavaObject(env, java_reply); - const status_t status = media_player->invoke(*request, reply); // Don't use process_media_player_call which use the async loop to // report errors, instead returns the status. - return status; + return media_player->invoke(*request, reply); } +// Sends the new filter to the client. +static jint +android_media_MediaPlayer_setMetadataFilter(JNIEnv *env, jobject thiz, jobject request) +{ + sp<MediaPlayer> media_player = getMediaPlayer(env, thiz); + if (media_player == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return UNKNOWN_ERROR; + } + + Parcel *filter = parcelForJavaObject(env, request); + + return media_player->setMetadataFilter(*filter); +} + + static void android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) { @@ -529,6 +545,7 @@ static JNINativeMethod gMethods[] = { {"setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume}, {"getFrameAt", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaPlayer_getFrameAt}, {"native_invoke", "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke}, + {"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_setMetadataFilter}, {"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup}, {"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize}, }; diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp index 3f278f4..131e510 100644 --- a/media/libmedia/IMediaPlayer.cpp +++ b/media/libmedia/IMediaPlayer.cpp @@ -41,6 +41,7 @@ enum { SET_LOOPING, SET_VOLUME, INVOKE, + SET_METADATA_FILTER, }; class BpMediaPlayer: public BpInterface<IMediaPlayer> @@ -178,6 +179,15 @@ public: status_t retcode = remote()->transact(INVOKE, request, reply); return retcode; } + + status_t setMetadataFilter(const Parcel& request) + { + Parcel reply; + // Avoid doing any extra copy of the request. The interface + // descriptor should have been set by MediaPlayer.java. + remote()->transact(SET_METADATA_FILTER, request, &reply); + return reply.readInt32(); + } }; IMPLEMENT_META_INTERFACE(MediaPlayer, "android.media.IMediaPlayer"); @@ -273,6 +283,11 @@ status_t BnMediaPlayer::onTransact( invoke(data, reply); return NO_ERROR; } break; + case SET_METADATA_FILTER: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(setMetadataFilter(data)); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 4683166..d8c622f 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -208,7 +208,16 @@ status_t MediaPlayer::invoke(const Parcel& request, Parcel *reply) return INVALID_OPERATION; } - +status_t MediaPlayer::setMetadataFilter(const Parcel& filter) +{ + LOGD("setMetadataFilter"); + Mutex::Autolock _l(mLock); + if (mPlayer == NULL) + { + return NO_INIT; + } + return mPlayer->setMetadataFilter(filter); +} status_t MediaPlayer::setVideoSurface(const sp<Surface>& surface) { diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index c4dccdf..3adbfac 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -69,6 +69,150 @@ pid_t gettid() { return syscall(__NR_gettid);} #undef __KERNEL__ #endif +namespace { +using android::status_t; +using android::OK; +using android::BAD_VALUE; +using android::NOT_ENOUGH_DATA; +using android::Parcel; +using android::Vector; + +// Max number of entries in the filter. +const int kMaxFilterSize = 64; // I pulled that out of thin air. + +// Keep in sync with ANY in Metadata.java +const int32_t kAny = 0; + +// To order the metadata types in the vector-filter. +int lessThan(const int32_t *lhs, const int32_t *rhs) +{ + return *lhs < *rhs ? 0 : 1; +} + +// Unmarshall a filter from a Parcel. +// Filter format in a parcel: +// +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | number of entries (n) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | metadata type 1 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | metadata type 2 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// .... +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | metadata type n | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// @param p Parcel that should start with a filter. +// @param[out] filter On exit contains the list of metadata type to be +// filtered. +// @param[out] status On exit contains the status code to be returned. +// @return true if the parcel starts with a valid filter. +bool unmarshallFilter(const Parcel& p, + Vector<int32_t> *filter, + status_t *status) +{ + int32_t s; + if (p.readInt32(&s) != OK) + { + LOGE("Failed to read filter's length"); + *status = NOT_ENOUGH_DATA; + return false; + } + + if( s > kMaxFilterSize || s < 0) + { + LOGE("Invalid filter len %d", s); + *status = BAD_VALUE; + return false; + } + + size_t size = s; + + filter->clear(); + filter->setCapacity(size); + + s *= sizeof(int32_t); + + if (p.dataAvail() < static_cast<size_t>(s)) + { + LOGE("Filter too short expected %d but got %d", s, p.dataAvail()); + *status = NOT_ENOUGH_DATA; + return false; + } + + const int32_t *data = static_cast<const int32_t*>(p.readInplace(s)); + + if (NULL == data ) + { + LOGE("Filter had no data"); + *status = BAD_VALUE; + return false; + } + + // TODO: The stl impl of vector would be more efficient here + // because it degenerates into a memcpy on pod types. Try to + // replace later or use stl::set. + for (size_t i = 0; i < size; ++i) + { + filter->push(*data); + ++data; + } + *status = OK; + return true; +} + +bool unmarshallBothFilters(const Parcel& p, + Vector<int32_t> *allow, + Vector<int32_t> *block, + status_t *status) +{ + if (!(unmarshallFilter(p, allow, status) && unmarshallFilter(p, block, status))) + { + return false; + } + allow->sort(lessThan); + block->sort(lessThan); + return true; +} + +// @param filter Should be sorted in ascending order. +// @param val To be searched. +// @return true if a match was found. +bool findMetadata(const Vector<int32_t> filter, const int32_t val) +{ + // Deal with empty and ANY right away + if (filter.isEmpty()) return false; + if (filter[0] == kAny) return true; + + ssize_t min = 0; + ssize_t max = filter.size() - 1; + ssize_t mid; + do + { + mid = (min + max) / 2; + if (val > filter[mid]) + { + min = mid + 1; + } + else + { + max = mid - 1; + } + if (filter[mid] == val) + { + return true; + } + } + while(min <= max); + return false; +} + +} // anonymous namespace + + namespace android { // TODO: Temp hack until we can register players @@ -686,6 +830,22 @@ status_t MediaPlayerService::Client::invoke(const Parcel& request, return p->invoke(request, reply); } +// This call doesn't need to access the native player. +status_t MediaPlayerService::Client::setMetadataFilter(const Parcel& filter) +{ + status_t status; + Vector<int32_t> allow, drop; + + if (unmarshallBothFilters(filter, &allow, &drop, &status)) + { + Mutex::Autolock l(mLock); + + mMetadataAllow = allow; + mMetadataDrop = drop; + } + return status; +} + status_t MediaPlayerService::Client::prepareAsync() { LOGV("[%d] prepareAsync", mConnId); @@ -808,10 +968,35 @@ status_t MediaPlayerService::Client::setVolume(float leftVolume, float rightVolu void MediaPlayerService::Client::notify(void* cookie, int msg, int ext1, int ext2) { Client* client = static_cast<Client*>(cookie); + + if (MEDIA_INFO == msg && + MEDIA_INFO_METADATA_UPDATE == ext1 && + client->shouldDropMetadata(ext2 /* metadata type */)) { + return; + } LOGV("[%d] notify (%p, %d, %d, %d)", client->mConnId, cookie, msg, ext1, ext2); client->mClient->notify(msg, ext1, ext2); } +bool MediaPlayerService::Client::shouldDropMetadata(int code) const +{ + Mutex::Autolock l(mLock); + + if (findMetadata(mMetadataDrop, code)) + { + return true; + } + + if (mMetadataAllow.isEmpty() || findMetadata(mMetadataAllow, code)) + { + return false; + } + else + { + return true; + } +} + #if CALLBACK_ANTAGONIZER const int Antagonizer::interval = 10000; // 10 msecs diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 12f2231..b915e86 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -23,6 +23,7 @@ #include <utils/List.h> #include <utils/Errors.h> #include <utils/KeyedVector.h> +#include <utils/Vector.h> #include <ui/SurfaceComposerClient.h> #include <media/IMediaPlayerService.h> @@ -143,7 +144,7 @@ class MediaPlayerService : public BnMediaPlayerService sp<MemoryHeapBase> mHeap; float mMsecsPerFrame; uint16_t mChannelCount; - uint16_t mFormat; + uint16_t mFormat; ssize_t mFrameCount; uint32_t mSampleRate; uint32_t mSize; @@ -168,6 +169,7 @@ public: void removeClient(wp<Client> client); + private: class Client : public BnMediaPlayer { @@ -188,6 +190,7 @@ private: virtual status_t setLooping(int loop); virtual status_t setVolume(float leftVolume, float rightVolume); virtual status_t invoke(const Parcel& request, Parcel *reply); + virtual status_t setMetadataFilter(const Parcel& filter); sp<MediaPlayerBase> createPlayer(player_type playerType); status_t setDataSource(const char *url); @@ -210,6 +213,12 @@ private: sp<MediaPlayerBase> getPlayer() const { Mutex::Autolock lock(mLock); return mPlayer; } + + /** + * @return true if the metadata should be dropped. + */ + bool shouldDropMetadata(int code) const; + mutable Mutex mLock; sp<MediaPlayerBase> mPlayer; sp<MediaPlayerService> mService; @@ -219,6 +228,10 @@ private: status_t mStatus; bool mLoop; int32_t mConnId; + // FIXME: Replace Vector<> with std::set<> when available. std::set support find. + // Metadata filters. + Vector<int32_t> mMetadataAllow; // protected by mLock + Vector<int32_t> mMetadataDrop; // protected by mLock #if CALLBACK_ANTAGONIZER Antagonizer* mAntagonizer; #endif |