diff options
| -rw-r--r-- | Android.bp | 1 | ||||
| -rw-r--r-- | media/java/android/media/MediaPlayer2.java | 16 | ||||
| -rw-r--r-- | media/java/android/media/MediaPlayer2Impl.java | 191 | ||||
| -rw-r--r-- | media/jni/Android.bp | 2 | ||||
| -rw-r--r-- | media/jni/android_media_MediaPlayer2.cpp | 51 | ||||
| -rw-r--r-- | media/proto/Android.bp | 20 | ||||
| -rw-r--r-- | media/proto/jarjar-rules.txt | 2 | ||||
| -rw-r--r-- | media/proto/mediaplayer2.proto | 53 |
8 files changed, 199 insertions, 137 deletions
diff --git a/Android.bp b/Android.bp index 07b301867129..eeede0f946b4 100644 --- a/Android.bp +++ b/Android.bp @@ -679,6 +679,7 @@ java_library { static_libs: [ "framework-protos", + "mediaplayer2-protos", "android.hidl.base-V1.0-java", "android.hardware.cas-V1.0-java", "android.hardware.contexthub-V1.0-java", diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java index aa78b0d0721d..89827bc98a7e 100644 --- a/media/java/android/media/MediaPlayer2.java +++ b/media/java/android/media/MediaPlayer2.java @@ -773,22 +773,6 @@ public abstract class MediaPlayer2 implements SubtitleController.Listener } /** - * Invoke a generic method on the native player using opaque - * parcels for the request and reply. Both payloads' format is a - * convention between the java caller and the native player. - * Must be called after setDataSource to make sure a native player - * exists. On failure, a RuntimeException is thrown. - * - * @param request Parcel with the data for the extension. The - * caller must use {@link #newRequest()} to get one. - * - * @param reply Output parcel with the data returned by the - * native player. - * {@hide} - */ - public void invoke(Parcel request, Parcel reply) { } - - /** * Insert a task in the command queue to help the client to identify whether a batch * of commands has been finished. When this command is processed, a notification * {@code EventCallback.onCommandLabelReached} will be fired with the diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java index c06b97b65735..42d3d2212b07 100644 --- a/media/java/android/media/MediaPlayer2Impl.java +++ b/media/java/android/media/MediaPlayer2Impl.java @@ -25,6 +25,9 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.graphics.SurfaceTexture; +import android.media.MediaPlayer2Proto; +import android.media.MediaPlayer2Proto.PlayerMessage; +import android.media.MediaPlayer2Proto.Value; import android.media.SubtitleController.Anchor; import android.media.SubtitleTrack.RenderingWidget; import android.net.Uri; @@ -49,6 +52,7 @@ import android.view.Surface; import android.view.SurfaceHolder; import android.widget.VideoView; +import com.android.framework.protobuf.InvalidProtocolBufferException; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; @@ -72,6 +76,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.HashMap; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -555,28 +560,30 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { } /** - * Invoke a generic method on the native player using opaque - * parcels for the request and reply. Both payloads' format is a + * Invoke a generic method on the native player using opaque protocol + * buffer message for the request and reply. Both payloads' format is a * convention between the java caller and the native player. - * Must be called after setDataSource or setPlaylist to make sure a native player - * exists. On failure, a RuntimeException is thrown. * - * @param request Parcel with the data for the extension. The + * @param request PlayerMessage for the extension. The * caller must use {@link #newRequest()} to get one. * - * @param reply Output parcel with the data returned by the + * @return PlayerMessage with the data returned by the * native player. - * {@hide} */ - @Override - public void invoke(Parcel request, Parcel reply) { - int retcode = native_invoke(request, reply); - reply.setDataPosition(0); - if (retcode != 0) { - throw new RuntimeException("failure code: " + retcode); + private PlayerMessage invoke(PlayerMessage msg) { + byte[] ret = _invoke(msg.toByteArray()); + if (ret == null) { + return null; + } + try { + return PlayerMessage.parseFrom(ret); + } catch (InvalidProtocolBufferException e) { + return null; } } + private native byte[] _invoke(byte[] request); + @Override public void notifyWhenCommandLabelReached(Object label) { addTask(new Task(CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED, false) { @@ -683,16 +690,12 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { final String msg = "Scaling mode " + mode + " is not supported"; throw new IllegalArgumentException(msg); } - Parcel request = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - try { - request.writeInt(INVOKE_ID_SET_VIDEO_SCALE_MODE); - request.writeInt(mode); - invoke(request, reply); - } finally { - request.recycle(); - reply.recycle(); - } + PlayerMessage request = PlayerMessage.newBuilder() + .addValues(Value.newBuilder() + .setInt32Value(INVOKE_ID_SET_VIDEO_SCALE_MODE)) + .addValues(Value.newBuilder().setInt32Value(mode)) + .build(); + invoke(request); } }); } @@ -1799,14 +1802,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { private native void _setAuxEffectSendLevel(float level); /* - * @param request Parcel destinated to the media player. - * @param reply[out] Parcel that will contain the reply. - * @return The status code. - */ - private native final int native_invoke(Parcel request, Parcel reply); - - - /* * @param update_only If true fetch only the set of metadata that have * changed since the last invocation of getMetadata. * The set is built using the unfiltered @@ -1886,18 +1881,18 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { final int mTrackType; final MediaFormat mFormat; - TrackInfoImpl(Parcel in) { - mTrackType = in.readInt(); - // TODO: parcel in the full MediaFormat; currently we are using createSubtitleFormat + TrackInfoImpl(Iterator<Value> in) { + mTrackType = in.next().getInt32Value(); + // TODO: build the full MediaFormat; currently we are using createSubtitleFormat // even for audio/video tracks, meaning we only set the mime and language. - String mime = in.readString(); - String language = in.readString(); + String mime = in.next().getStringValue(); + String language = in.next().getStringValue(); mFormat = MediaFormat.createSubtitleFormat(mime, language); if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) { - mFormat.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.readInt()); - mFormat.setInteger(MediaFormat.KEY_IS_DEFAULT, in.readInt()); - mFormat.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.readInt()); + mFormat.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.next().getInt32Value()); + mFormat.setInteger(MediaFormat.KEY_IS_DEFAULT, in.next().getInt32Value()); + mFormat.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.next().getInt32Value()); } } @@ -1952,23 +1947,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { out.append("}"); return out.toString(); } - - /** - * Used to read a TrackInfoImpl from a Parcel. - */ - /* package private */ static final Parcelable.Creator<TrackInfoImpl> CREATOR - = new Parcelable.Creator<TrackInfoImpl>() { - @Override - public TrackInfoImpl createFromParcel(Parcel in) { - return new TrackInfoImpl(in); - } - - @Override - public TrackInfoImpl[] newArray(int size) { - return new TrackInfoImpl[size]; - } - }; - }; // We would like domain specific classes with more informative names than the `first` and `second` @@ -2010,17 +1988,23 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { } private TrackInfoImpl[] getInbandTrackInfoImpl() throws IllegalStateException { - Parcel request = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - try { - request.writeInt(INVOKE_ID_GET_TRACK_INFO); - invoke(request, reply); - TrackInfoImpl trackInfo[] = reply.createTypedArray(TrackInfoImpl.CREATOR); - return trackInfo; - } finally { - request.recycle(); - reply.recycle(); + PlayerMessage request = PlayerMessage.newBuilder() + .addValues(Value.newBuilder().setInt32Value(INVOKE_ID_GET_TRACK_INFO)) + .build(); + PlayerMessage response = invoke(request); + if (response == null) { + return null; } + Iterator<Value> in = response.getValuesList().iterator(); + int size = in.next().getInt32Value(); + if (size == 0) { + return null; + } + TrackInfoImpl trackInfo[] = new TrackInfoImpl[size]; + for (int i = 0; i < size; ++i) { + trackInfo[i] = new TrackInfoImpl(in); + } + return trackInfo; } /* @@ -2481,26 +2465,24 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { } } - Parcel request = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - try { - request.writeInt(INVOKE_ID_GET_SELECTED_TRACK); - request.writeInt(trackType); - invoke(request, reply); - int inbandTrackIndex = reply.readInt(); - synchronized (mIndexTrackPairs) { - for (int i = 0; i < mIndexTrackPairs.size(); i++) { - Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i); - if (p.first != null && p.first == inbandTrackIndex) { - return i; - } + PlayerMessage request = PlayerMessage.newBuilder() + .addValues(Value.newBuilder().setInt32Value(INVOKE_ID_GET_SELECTED_TRACK)) + .addValues(Value.newBuilder().setInt32Value(trackType)) + .build(); + PlayerMessage response = invoke(request); + if (response == null) { + return -1; + } + int inbandTrackIndex = response.getValues(0).getInt32Value(); + synchronized (mIndexTrackPairs) { + for (int i = 0; i < mIndexTrackPairs.size(); i++) { + Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i); + if (p.first != null && p.first == inbandTrackIndex) { + return i; } } - return -1; - } finally { - request.recycle(); - reply.recycle(); } + return -1; } /** @@ -2617,16 +2599,12 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { private void selectOrDeselectInbandTrack(int index, boolean select) throws IllegalStateException { - Parcel request = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - try { - request.writeInt(select? INVOKE_ID_SELECT_TRACK: INVOKE_ID_DESELECT_TRACK); - request.writeInt(index); - invoke(request, reply); - } finally { - request.recycle(); - reply.recycle(); - } + PlayerMessage request = PlayerMessage.newBuilder() + .addValues(Value.newBuilder().setInt32Value( + select? INVOKE_ID_SELECT_TRACK: INVOKE_ID_DESELECT_TRACK)) + .addValues(Value.newBuilder().setInt32Value(index)) + .build(); + invoke(request); } // Have to declare protected for finalize() since it is protected @@ -2935,20 +2913,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { case MEDIA_INFO: { - synchronized (mEventCbLock) { - for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) { - cb.first.execute(() -> cb.second.onInfo( - mMediaPlayer, dsd, what, extra)); - } - } - switch (msg.arg1) { - case MEDIA_INFO_DATA_SOURCE_START: - if (isCurrentSrcId) { - prepareNextDataSource(); - } - break; - case MEDIA_INFO_VIDEO_TRACK_LAGGING: Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")"); break; @@ -2980,6 +2945,20 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { } break; } + + synchronized (mEventCbLock) { + for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) { + cb.first.execute(() -> cb.second.onInfo( + mMediaPlayer, dsd, what, extra)); + } + } + + if (msg.arg1 == MEDIA_INFO_DATA_SOURCE_START) { + if (isCurrentSrcId) { + prepareNextDataSource(); + } + } + // No real default action so far. return; } diff --git a/media/jni/Android.bp b/media/jni/Android.bp index de9a24b078cb..b46816424995 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -133,8 +133,10 @@ cc_library_shared { "libmediaextractor", "libmediametrics", "libmediaplayer2", + "libmediaplayer2-protos", "libmediautils", "libnetd_client", + "libprotobuf-cpp-lite", "libstagefright_esds", "libstagefright_foundation", "libstagefright_httplive", diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp index 426598727c89..d924b38de981 100644 --- a/media/jni/android_media_MediaPlayer2.cpp +++ b/media/jni/android_media_MediaPlayer2.cpp @@ -56,6 +56,10 @@ #include "android_util_Binder.h" #include <binder/Parcel.h> +#include "mediaplayer2.pb.h" + +using android::media::MediaPlayer2Proto::PlayerMessage; + // Modular DRM begin #define FIND_CLASS(var, className) \ var = env->FindClass(className); \ @@ -196,6 +200,11 @@ void JNIMediaPlayer2Listener::notify(int64_t srcId, int msg, int ext1, int ext2, if (obj && obj->dataSize() > 0) { jobject jParcel = createJavaParcelObject(env); if (jParcel != NULL) { + // TODO: replace the following Parcel usages with proto. + // 1. MEDIA_DRM_INFO : DrmInfo + // 2. MEDIA_TIMED_TEXT : TimedText + // 2. MEDIA_SUBTITLE_DATA : SubtitleData + // 4. MEDIA_META_DATA : TimedMetaData Parcel* nativeParcel = parcelForJavaObject(env, jParcel); nativeParcel->setData(obj->data(), obj->dataSize()); env->CallStaticVoidMethod(mClass, fields.post_event, mObject, @@ -911,6 +920,9 @@ android_media_MediaPlayer2_setParameter(JNIEnv *env, jobject thiz, jint key, job return false; } + // TODO: parcelForJavaObject() shouldn't be used since it's dependent on + // framework's Parcel implementation. This setParameter() is used + // only with AudioAttribute. Can this be used as jobject with JAudioTrack? Parcel *request = parcelForJavaObject(env, java_request); status_t err = mp->setParameter(key, *request); if (err == OK) { @@ -978,26 +990,35 @@ android_media_MediaPlayer2_setVolume(JNIEnv *env, jobject thiz, jfloat leftVolum process_media_player_call( env, thiz, mp->setVolume((float) leftVolume, (float) rightVolume), NULL, NULL ); } -// Sends the request and reply parcels to the media player via the -// binder interface. -static jint -android_media_MediaPlayer2_invoke(JNIEnv *env, jobject thiz, - jobject java_request, jobject java_reply) -{ +static jbyteArray +android_media_MediaPlayer2_invoke(JNIEnv *env, jobject thiz, jbyteArray requestData) { sp<MediaPlayer2> media_player = getMediaPlayer(env, thiz); - if (media_player == NULL ) { + if (media_player == NULL) { jniThrowException(env, "java/lang/IllegalStateException", NULL); - return UNKNOWN_ERROR; + return NULL; } - Parcel *request = parcelForJavaObject(env, java_request); - Parcel *reply = parcelForJavaObject(env, java_reply); + // Get the byte[] pointer and data length. + jbyte* pData = env->GetByteArrayElements(requestData, NULL); + jsize pDataLen = env->GetArrayLength(requestData); + + // Deserialize from the byte stream. + PlayerMessage request; + PlayerMessage response; + request.ParseFromArray(pData, pDataLen); + + media_player->invoke(request, &response); + + int size = response.ByteSize(); + jbyte* temp = new jbyte[size]; + response.SerializeToArray(temp, size); - request->setDataPosition(0); + // return the response as a byte array. + jbyteArray out = env->NewByteArray(size); + env->SetByteArrayRegion(out, 0, size, temp); + delete[] temp; - // Don't use process_media_player_call which use the async loop to - // report errors, instead returns the status. - return (jint) media_player->invoke(*request, reply); + return out; } // Sends the new filter to the client. @@ -1512,7 +1533,7 @@ static const JNINativeMethod gMethods[] = { {"setLooping", "(Z)V", (void *)android_media_MediaPlayer2_setLooping}, {"isLooping", "()Z", (void *)android_media_MediaPlayer2_isLooping}, {"_setVolume", "(FF)V", (void *)android_media_MediaPlayer2_setVolume}, - {"native_invoke", "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer2_invoke}, + {"_invoke", "([B)[B", (void *)android_media_MediaPlayer2_invoke}, {"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer2_setMetadataFilter}, {"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer2_getMetadata}, {"native_init", "()V", (void *)android_media_MediaPlayer2_native_init}, diff --git a/media/proto/Android.bp b/media/proto/Android.bp new file mode 100644 index 000000000000..50d44c3e6145 --- /dev/null +++ b/media/proto/Android.bp @@ -0,0 +1,20 @@ +java_library_static { + name: "mediaplayer2-protos", + host_supported: true, + proto: { + type: "lite", + }, + srcs: ["mediaplayer2.proto"], + no_framework_libs: true, + jarjar_rules: "jarjar-rules.txt", +} + +cc_library_static { + name: "libmediaplayer2-protos", + host_supported: true, + proto: { + export_proto_headers: true, + type: "lite", + }, + srcs: ["mediaplayer2.proto"], +} diff --git a/media/proto/jarjar-rules.txt b/media/proto/jarjar-rules.txt new file mode 100644 index 000000000000..7be6e732d3fd --- /dev/null +++ b/media/proto/jarjar-rules.txt @@ -0,0 +1,2 @@ +rule com.google.protobuf.** com.android.framework.protobuf.@1 + diff --git a/media/proto/mediaplayer2.proto b/media/proto/mediaplayer2.proto new file mode 100644 index 000000000000..6287d6cd326d --- /dev/null +++ b/media/proto/mediaplayer2.proto @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 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. + */ + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; + +// C++ namespace: android::media:MediaPlayer2Proto: +package android.media.MediaPlayer2Proto; + +option java_package = "android.media"; +option java_outer_classname = "MediaPlayer2Proto"; + +message Value { + // The kind of value. + oneof kind { + // Represents a boolean value. + bool bool_value = 1; + // Represents an int32 value. + int32 int32_value = 2; + // Represents an uint32 value. + uint32 uint32_value = 3; + // Represents an int64 value. + int64 int64_value = 4; + // Represents an uint64 value. + uint64 uint64_value = 5; + // Represents a float value. + double float_value = 6; + // Represents a double value. + double double_value = 7; + // Represents a string value. + string string_value = 8; + // Represents a bytes value. + bytes bytes_value = 9; + } +} + +message PlayerMessage { + repeated Value values = 1; +} |