summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/media/IMediaPlayer.h7
-rw-r--r--include/media/mediaplayer.h3
-rw-r--r--media/java/android/media/MediaPlayer.java51
-rw-r--r--media/java/android/media/Metadata.java1
-rw-r--r--media/jni/android_media_MediaPlayer.cpp21
-rw-r--r--media/libmedia/IMediaPlayer.cpp15
-rw-r--r--media/libmedia/mediaplayer.cpp11
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.cpp185
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.h15
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