From 0e0fee1e3a04023c9ec122d3bd7da54e9565c3e7 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Wed, 25 Jan 2017 18:01:56 -0800 Subject: [PATCH] API access to mediametrics provide SDK/API access to media metrics for user apps. This lets the apps metrics data for their instances of player and codecs. Others to come. Bug: 34715803 Test: booted, invoked new APIs to verify desired results Change-Id: Iaae6406aadef30adc893952961ac154a7c4c761d --- api/current.txt | 2 + api/system-current.txt | 2 + api/test-current.txt | 2 + media/java/android/media/MediaCodec.java | 9 ++ media/java/android/media/MediaPlayer.java | 9 ++ media/jni/Android.mk | 1 + media/jni/android_media_MediaCodec.cpp | 41 +++++++++ media/jni/android_media_MediaCodec.h | 2 + media/jni/android_media_MediaMetricsJNI.cpp | 93 +++++++++++++++++++++ media/jni/android_media_MediaMetricsJNI.h | 34 ++++++++ media/jni/android_media_MediaPlayer.cpp | 31 +++++++ 11 files changed, 226 insertions(+) create mode 100644 media/jni/android_media_MediaMetricsJNI.cpp create mode 100644 media/jni/android_media_MediaMetricsJNI.h diff --git a/api/current.txt b/api/current.txt index e2616a6f90362..28a2c7ab189b2 100644 --- a/api/current.txt +++ b/api/current.txt @@ -21320,6 +21320,7 @@ package android.media { method public deprecated java.nio.ByteBuffer[] getInputBuffers(); method public final android.media.MediaFormat getInputFormat(); method public android.media.Image getInputImage(int); + method public android.os.Bundle getMetrics(); method public final java.lang.String getName(); method public java.nio.ByteBuffer getOutputBuffer(int); method public deprecated java.nio.ByteBuffer[] getOutputBuffers(); @@ -22145,6 +22146,7 @@ package android.media { method public int getCurrentPosition(); method public android.media.BufferingParams getDefaultBufferingParams(); method public int getDuration(); + method public android.os.Bundle getMetrics(); method public android.media.PlaybackParams getPlaybackParams(); method public int getSelectedTrack(int) throws java.lang.IllegalStateException; method public android.media.SyncParams getSyncParams(); diff --git a/api/system-current.txt b/api/system-current.txt index 21f15f273b5bc..ce316d910fc2b 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -22924,6 +22924,7 @@ package android.media { method public deprecated java.nio.ByteBuffer[] getInputBuffers(); method public final android.media.MediaFormat getInputFormat(); method public android.media.Image getInputImage(int); + method public android.os.Bundle getMetrics(); method public final java.lang.String getName(); method public java.nio.ByteBuffer getOutputBuffer(int); method public deprecated java.nio.ByteBuffer[] getOutputBuffers(); @@ -23749,6 +23750,7 @@ package android.media { method public int getCurrentPosition(); method public android.media.BufferingParams getDefaultBufferingParams(); method public int getDuration(); + method public android.os.Bundle getMetrics(); method public android.media.PlaybackParams getPlaybackParams(); method public int getSelectedTrack(int) throws java.lang.IllegalStateException; method public android.media.SyncParams getSyncParams(); diff --git a/api/test-current.txt b/api/test-current.txt index 940f68bc76607..f4a52ea5bf5ef 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -21412,6 +21412,7 @@ package android.media { method public deprecated java.nio.ByteBuffer[] getInputBuffers(); method public final android.media.MediaFormat getInputFormat(); method public android.media.Image getInputImage(int); + method public android.os.Bundle getMetrics(); method public final java.lang.String getName(); method public java.nio.ByteBuffer getOutputBuffer(int); method public deprecated java.nio.ByteBuffer[] getOutputBuffers(); @@ -22237,6 +22238,7 @@ package android.media { method public int getCurrentPosition(); method public android.media.BufferingParams getDefaultBufferingParams(); method public int getDuration(); + method public android.os.Bundle getMetrics(); method public android.media.PlaybackParams getPlaybackParams(); method public int getSelectedTrack(int) throws java.lang.IllegalStateException; method public android.media.SyncParams getSyncParams(); diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 264944f43baea..75ccffee6f097 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -3133,6 +3133,15 @@ final public class MediaCodec { @NonNull public native final String getName(); + /** + * Returns Analytics/Metrics data about the current content being + * + * @return a Bundle containint the set of attributes and values available + * for the media being handled by this instance of MediaCodec + * + */ + public native Bundle getMetrics(); + /** * Change a video encoder's target bitrate on the fly. The value is an * Integer object containing the new bitrate in bps. diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 4023400b71c48..88dde53eff468 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -25,6 +25,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.net.Uri; +import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -1386,6 +1387,14 @@ public class MediaPlayer extends PlayerBase */ public native int getVideoHeight(); + /** + * Returns Analytics/Metrics data about the current video in this player. + * + * @return the a map of attributes and values available for this video + * player or null if no metrics are available. + */ + public native Bundle getMetrics(); + /** * Checks whether the MediaPlayer is playing. * diff --git a/media/jni/Android.mk b/media/jni/Android.mk index 8d4271f637a2c..f69313c07c56a 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -11,6 +11,7 @@ LOCAL_SRC_FILES:= \ android_media_MediaDrm.cpp \ android_media_MediaExtractor.cpp \ android_media_MediaHTTPConnection.cpp \ + android_media_MediaMetricsJNI.cpp \ android_media_MediaMetadataRetriever.cpp \ android_media_MediaMuxer.cpp \ android_media_MediaPlayer.cpp \ diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index c2c66fdb6ca96..6f9883c08050c 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -21,6 +21,7 @@ #include "android_media_MediaCodec.h" #include "android_media_MediaCrypto.h" +#include "android_media_MediaMetricsJNI.h" #include "android_media_Utils.h" #include "android_runtime/AndroidRuntime.h" #include "android_runtime/android_view_Surface.h" @@ -618,6 +619,12 @@ status_t JMediaCodec::getName(JNIEnv *env, jstring *nameStr) const { return OK; } +status_t JMediaCodec::getMetrics(JNIEnv *, Parcel *reply) const { + + status_t status = mCodec->getMetrics(reply); + return status; +} + status_t JMediaCodec::setParameters(const sp &msg) { return mCodec->setParameters(msg); } @@ -1646,6 +1653,37 @@ static jobject android_media_MediaCodec_getName( return NULL; } +static jobject +android_media_MediaCodec_getMetrics(JNIEnv *env, jobject thiz) +{ + ALOGV("android_media_MediaCodec_getMetrics"); + + sp codec = getMediaCodec(env, thiz); + if (codec == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return 0; + } + + // get what we have for the metrics from the codec + Parcel reply; + status_t err = codec->getMetrics(env, &reply); + if (err != OK) { + ALOGE("getMetrics failed"); + return (jobject) NULL; + } + + // build and return the Bundle + MediaAnalyticsItem *item = new MediaAnalyticsItem; + item->readFromParcel(reply); + jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL); + + // housekeeping + delete item; + item = NULL; + + return mybundle; +} + static void android_media_MediaCodec_setParameters( JNIEnv *env, jobject thiz, jobjectArray keys, jobjectArray vals) { ALOGV("android_media_MediaCodec_setParameters"); @@ -1954,6 +1992,9 @@ static const JNINativeMethod gMethods[] = { { "getName", "()Ljava/lang/String;", (void *)android_media_MediaCodec_getName }, + { "getMetrics", "()Landroid/os/Bundle;", + (void *)android_media_MediaCodec_getMetrics}, + { "setParameters", "([Ljava/lang/String;[Ljava/lang/Object;)V", (void *)android_media_MediaCodec_setParameters }, diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h index 5f0d3df173601..b3b1b3a87ba4e 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -111,6 +111,8 @@ struct JMediaCodec : public AHandler { status_t getName(JNIEnv *env, jstring *name) const; + status_t getMetrics(JNIEnv *env, Parcel *reply) const; + status_t setParameters(const sp ¶ms); void setVideoScalingMode(int mode); diff --git a/media/jni/android_media_MediaMetricsJNI.cpp b/media/jni/android_media_MediaMetricsJNI.cpp new file mode 100644 index 0000000000000..fb606bac78280 --- /dev/null +++ b/media/jni/android_media_MediaMetricsJNI.cpp @@ -0,0 +1,93 @@ +/* + * Copyright 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "android_media_MediaMetricsJNI.h" +#include + + +namespace android { + +// place the attributes into a java Bundle object +// decide whether this is appropriately scoped here. +// if we do it somewhere else, we have to figure a "give me all the attrs" +// access to the inside of MediaAnalyticsItem +jobject MediaMetricsJNI::writeMetricsToBundle(JNIEnv* env, MediaAnalyticsItem *item, jobject mybundle) { + + jclass clazzBundle = env->FindClass("android/os/Bundle"); + if (clazzBundle==NULL) { + ALOGD("can't find android/os/Bundle"); + return NULL; + } + // sometimes the caller provides one for us to fill + if (mybundle == NULL) { + // create the bundle + jmethodID constructID = env->GetMethodID(clazzBundle, "", "()V"); + mybundle = env->NewObject(clazzBundle, constructID); + if (mybundle == NULL) { + return NULL; + } + } + + // grab methods that we can invoke + jmethodID setIntID = env->GetMethodID(clazzBundle, "putInt", "(Ljava/lang/String;I)V"); + jmethodID setLongID = env->GetMethodID(clazzBundle, "putLong", "(Ljava/lang/String;J)V"); + jmethodID setDoubleID = env->GetMethodID(clazzBundle, "putDouble", "(Ljava/lang/String;D)V"); + jmethodID setStringID = env->GetMethodID(clazzBundle, "putString", "(Ljava/lang/String;Ljava/lang/String;)V"); + + // env, class, method, {parms} + //env->CallVoidMethod(env, mybundle, setIntID, jstr, jint); + + // iterate through my attributes + // -- get name, get type, get value + // -- insert appropriately into the bundle + for (size_t i = 0 ; i < item->mPropCount; i++ ) { + MediaAnalyticsItem::Prop *prop = &item->mProps[i]; + // build the key parameter from prop->mName + jstring keyName = env->NewStringUTF(prop->mName); + // invoke the appropriate method to insert + switch (prop->mType) { + case MediaAnalyticsItem::kTypeInt32: + env->CallVoidMethod(mybundle, setIntID, + keyName, (jint) prop->u.int32Value); + break; + case MediaAnalyticsItem::kTypeInt64: + env->CallVoidMethod(mybundle, setLongID, + keyName, (jlong) prop->u.int64Value); + break; + case MediaAnalyticsItem::kTypeDouble: + env->CallVoidMethod(mybundle, setDoubleID, + keyName, (jdouble) prop->u.doubleValue); + break; + case MediaAnalyticsItem::kTypeCString: + env->CallVoidMethod(mybundle, setStringID, keyName, + env->NewStringUTF(prop->u.CStringValue)); + break; + default: + ALOGE("to_String bad item type: %d for %s", + prop->mType, prop->mName); + break; + } + } + + return mybundle; +} + +}; // namespace android + diff --git a/media/jni/android_media_MediaMetricsJNI.h b/media/jni/android_media_MediaMetricsJNI.h new file mode 100644 index 0000000000000..d1742121277b6 --- /dev/null +++ b/media/jni/android_media_MediaMetricsJNI.h @@ -0,0 +1,34 @@ +/* + * Copyright 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROID_MEDIA_MEDIAMETRICSJNI_H_ +#define _ANDROID_MEDIA_MEDIAMETRICSJNI_H_ + +#include +#include +#include +#include + +namespace android { + +class MediaMetricsJNI { +public: + static jobject writeMetricsToBundle(JNIEnv* env, MediaAnalyticsItem *item, jobject mybundle); +}; + +}; // namespace android + +#endif // _ANDROID_MEDIA_MEDIAMETRICSJNI_H_ diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 8225052424267..af59d818f4479 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -23,6 +23,8 @@ #include #include #include +#include +#include // for FOURCC definition #include #include #include @@ -39,6 +41,7 @@ #include "utils/String8.h" #include "android_media_BufferingParams.h" #include "android_media_MediaDataSource.h" +#include "android_media_MediaMetricsJNI.h" #include "android_media_PlaybackParams.h" #include "android_media_SyncParams.h" #include "android_media_Utils.h" @@ -684,6 +687,33 @@ android_media_MediaPlayer_getVideoHeight(JNIEnv *env, jobject thiz) return (jint) h; } +static jobject +android_media_MediaPlayer_getMetrics(JNIEnv *env, jobject thiz) +{ + sp mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return 0; + } + + Parcel p; + int key = FOURCC('m','t','r','X'); + status_t status = mp->getParameter(key, &p); + if (status != OK) { + ALOGD("getMetrics() failed: %d", status); + return (jobject) NULL; + } + + MediaAnalyticsItem *item = new MediaAnalyticsItem; + item->readFromParcel(p); + jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL); + + // housekeeping + delete item; + item = NULL; + + return mybundle; +} static jint android_media_MediaPlayer_getCurrentPosition(JNIEnv *env, jobject thiz) @@ -1118,6 +1148,7 @@ static const JNINativeMethod gMethods[] = { {"_stop", "()V", (void *)android_media_MediaPlayer_stop}, {"getVideoWidth", "()I", (void *)android_media_MediaPlayer_getVideoWidth}, {"getVideoHeight", "()I", (void *)android_media_MediaPlayer_getVideoHeight}, + {"getMetrics", "()Landroid/os/Bundle;", (void *)android_media_MediaPlayer_getMetrics}, {"setPlaybackParams", "(Landroid/media/PlaybackParams;)V", (void *)android_media_MediaPlayer_setPlaybackParams}, {"getPlaybackParams", "()Landroid/media/PlaybackParams;", (void *)android_media_MediaPlayer_getPlaybackParams}, {"setSyncParams", "(Landroid/media/SyncParams;)V", (void *)android_media_MediaPlayer_setSyncParams},