Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c072caf1 authored by Guillaume Chelfi's avatar Guillaume Chelfi
Browse files

Add tunnel mode video peek related APIs

Bug: 157501309
Test: atest android.media.cts.DecoderTest#testTunneledVideoPlayback
      atest android.media.cts.DecoderTest#testTunneledVideoFlush
CTS-Coverage-Bug: 157501309

Change-Id: I5107106bb64d96ac6cc7d8704955b2f4477b94a8
parent b4c0d646
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -21325,6 +21325,7 @@ package android.media {
    method public void setCallback(@Nullable android.media.MediaCodec.Callback, @Nullable android.os.Handler);
    method public void setCallback(@Nullable android.media.MediaCodec.Callback);
    method public void setInputSurface(@NonNull android.view.Surface);
    method public void setOnFirstTunnelFrameReadyListener(@Nullable android.os.Handler, @Nullable android.media.MediaCodec.OnFirstTunnelFrameReadyListener);
    method public void setOnFrameRenderedListener(@Nullable android.media.MediaCodec.OnFrameRenderedListener, @Nullable android.os.Handler);
    method public void setOutputSurface(@NonNull android.view.Surface);
    method public void setParameters(@Nullable android.os.Bundle);
@@ -21352,6 +21353,7 @@ package android.media {
    field public static final String PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
    field public static final String PARAMETER_KEY_SUSPEND = "drop-input-frames";
    field public static final String PARAMETER_KEY_SUSPEND_TIME = "drop-start-time-us";
    field public static final String PARAMETER_KEY_TUNNEL_PEEK = "tunnel-peek";
    field public static final String PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate";
    field public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1; // 0x1
    field public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2; // 0x2
@@ -21442,6 +21444,10 @@ package android.media {
    field public static final String WIDTH = "android.media.mediacodec.width";
  }
  public static interface MediaCodec.OnFirstTunnelFrameReadyListener {
    method public void onFirstTunnelFrameReady(@NonNull android.media.MediaCodec);
  }
  public static interface MediaCodec.OnFrameRenderedListener {
    method public void onFrameRendered(@NonNull android.media.MediaCodec, long, long);
  }
+103 −0
Original line number Diff line number Diff line
@@ -1674,9 +1674,11 @@ final public class MediaCodec implements PlaybackComponent {
    public @interface BufferFlag {}

    private EventHandler mEventHandler;
    private EventHandler mOnFirstTunnelFrameReadyHandler;
    private EventHandler mOnFrameRenderedHandler;
    private EventHandler mCallbackHandler;
    private Callback mCallback;
    private OnFirstTunnelFrameReadyListener mOnFirstTunnelFrameReadyListener;
    private OnFrameRenderedListener mOnFrameRenderedListener;
    private final Object mListenerLock = new Object();
    private MediaCodecInfo mCodecInfo;
@@ -1687,6 +1689,7 @@ final public class MediaCodec implements PlaybackComponent {
    private static final int EVENT_CALLBACK = 1;
    private static final int EVENT_SET_CALLBACK = 2;
    private static final int EVENT_FRAME_RENDERED = 3;
    private static final int EVENT_FIRST_TUNNEL_FRAME_READY = 4;

    private static final int CB_INPUT_AVAILABLE = 1;
    private static final int CB_OUTPUT_AVAILABLE = 2;
@@ -1748,6 +1751,16 @@ final public class MediaCodec implements PlaybackComponent {
                                mCodec, (long)mediaTimeUs, (long)systemNano);
                    }
                    break;
                case EVENT_FIRST_TUNNEL_FRAME_READY:
                    OnFirstTunnelFrameReadyListener onFirstTunnelFrameReadyListener;
                    synchronized (mListenerLock) {
                        onFirstTunnelFrameReadyListener = mOnFirstTunnelFrameReadyListener;
                    }
                    if (onFirstTunnelFrameReadyListener == null) {
                        break;
                    }
                    onFirstTunnelFrameReadyListener.onFirstTunnelFrameReady(mCodec);
                    break;
                default:
                {
                    break;
@@ -1923,6 +1936,7 @@ final public class MediaCodec implements PlaybackComponent {
            mEventHandler = null;
        }
        mCallbackHandler = mEventHandler;
        mOnFirstTunnelFrameReadyHandler = mEventHandler;
        mOnFrameRenderedHandler = mEventHandler;

        mBufferLock = new Object();
@@ -2277,6 +2291,9 @@ final public class MediaCodec implements PlaybackComponent {
                mCallbackHandler.removeMessages(EVENT_SET_CALLBACK);
                mCallbackHandler.removeMessages(EVENT_CALLBACK);
            }
            if (mOnFirstTunnelFrameReadyHandler != null) {
                mOnFirstTunnelFrameReadyHandler.removeMessages(EVENT_FIRST_TUNNEL_FRAME_READY);
            }
            if (mOnFrameRenderedHandler != null) {
                mOnFrameRenderedHandler.removeMessages(EVENT_FRAME_RENDERED);
            }
@@ -4446,6 +4463,41 @@ final public class MediaCodec implements PlaybackComponent {
    public static final String PARAMETER_KEY_LOW_LATENCY =
            MediaFormat.KEY_LOW_LATENCY;

    /**
     * Control video peek of the first frame when a codec is configured for tunnel mode with
     * {@link MediaFormat#KEY_AUDIO_SESSION_ID} while the {@link AudioTrack} is paused.
     *<p>
     * When disabled (1) after a {@link #flush} or {@link #start}, (2) while the corresponding
     * {@link AudioTrack} is paused and (3) before any buffers are queued, the first frame is not to
     * be rendered until either this parameter is enabled or the corresponding {@link AudioTrack}
     * has begun playback. Once the frame is decoded and ready to be rendered,
     * {@link OnFirstTunnelFrameReadyListener#onFirstTunnelFrameReady} is called but the frame is
     * not rendered. The surface continues to show the previously-rendered content, or black if the
     * surface is new. A subsequent call to {@link AudioTrack#play} renders this frame and triggers
     * a callback to {@link OnFrameRenderedListener#onFrameRendered}, and video playback begins.
     *<p>
     * <b>Note</b>: To clear any previously rendered content and show black, configure the
     * MediaCodec with {@code KEY_PUSH_BLANK_BUFFERS_ON_STOP(1)}, and call {@link #stop} before
     * pushing new video frames to the codec.
     *<p>
     * When enabled (1) after a {@link #flush} or {@link #start} and (2) while the corresponding
     * {@link AudioTrack} is paused, the first frame is rendered as soon as it is decoded, or
     * immediately, if it has already been decoded. If not already decoded, when the frame is
     * decoded and ready to be rendered,
     * {@link OnFirstTunnelFrameReadyListener#onFirstTunnelFrameReady} is called. The frame is then
     * immediately rendered and {@link OnFrameRenderedListener#onFrameRendered} is subsequently
     * called.
     *<p>
     * The value is an Integer object containing the value 1 to enable or the value 0 to disable.
     *<p>
     * The default for this parameter is <b>enabled</b>. Once a frame has been rendered, changing
     * this parameter has no effect until a subsequent {@link #flush} or
     * {@link #stop}/{@link #start}.
     *
     * @see #setParameters(Bundle)
     */
    public static final String PARAMETER_KEY_TUNNEL_PEEK = "tunnel-peek";

    /**
     * Communicate additional parameter changes to the component instance.
     * <b>Note:</b> Some of these parameter changes may silently fail to apply.
@@ -4544,6 +4596,55 @@ final public class MediaCodec implements PlaybackComponent {
        setCallback(cb, null /* handler */);
    }

    /**
     * Listener to be called when the first output frame has been decoded
     * and is ready to be rendered for a codec configured for tunnel mode with
     * {@code KEY_AUDIO_SESSION_ID}.
     *
     * @see MediaCodec#setOnFirstTunnelFrameReadyListener
     */
    public interface OnFirstTunnelFrameReadyListener {

        /**
         * Called when the first output frame has been decoded and is ready to be
         * rendered.
         */
        void onFirstTunnelFrameReady(@NonNull MediaCodec codec);
    }

    /**
     * Registers a callback to be invoked when the first output frame has been decoded
     * and is ready to be rendered on a codec configured for tunnel mode with {@code
     * KEY_AUDIO_SESSION_ID}.
     *
     * @param handler the callback will be run on the handler's thread. If {@code
     * null}, the callback will be run on the default thread, which is the looper from
     * which the codec was created, or a new thread if there was none.
     *
     * @param listener the callback that will be run. If {@code null}, clears any registered
     * listener.
     */
    public void setOnFirstTunnelFrameReadyListener(
            @Nullable Handler handler, @Nullable OnFirstTunnelFrameReadyListener listener) {
        synchronized (mListenerLock) {
            mOnFirstTunnelFrameReadyListener = listener;
            if (listener != null) {
                EventHandler newHandler = getEventHandlerOn(
                        handler,
                        mOnFirstTunnelFrameReadyHandler);
                if (newHandler != mOnFirstTunnelFrameReadyHandler) {
                    mOnFirstTunnelFrameReadyHandler.removeMessages(EVENT_FIRST_TUNNEL_FRAME_READY);
                }
                mOnFirstTunnelFrameReadyHandler = newHandler;
            } else if (mOnFirstTunnelFrameReadyHandler != null) {
                mOnFirstTunnelFrameReadyHandler.removeMessages(EVENT_FIRST_TUNNEL_FRAME_READY);
            }
            native_enableOnFirstTunnelFrameReadyListener(listener != null);
        }
    }

    private native void native_enableOnFirstTunnelFrameReadyListener(boolean enable);

    /**
     * Listener to be called when an output frame has rendered on the output surface
     *
@@ -4667,6 +4768,8 @@ final public class MediaCodec implements PlaybackComponent {
            EventHandler handler = mEventHandler;
            if (what == EVENT_CALLBACK) {
                handler = mCallbackHandler;
            } else if (what == EVENT_FIRST_TUNNEL_FRAME_READY) {
                handler = mOnFirstTunnelFrameReadyHandler;
            } else if (what == EVENT_FRAME_RENDERED) {
                handler = mOnFrameRenderedHandler;
            }
+58 −0
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ enum {
    EVENT_CALLBACK = 1,
    EVENT_SET_CALLBACK = 2,
    EVENT_FRAME_RENDERED = 3,
    EVENT_FIRST_TUNNEL_FRAME_READY = 4,
};

static struct CryptoErrorCodes {
@@ -269,6 +270,18 @@ JMediaCodec::~JMediaCodec() {
    mClass = NULL;
}

status_t JMediaCodec::enableOnFirstTunnelFrameReadyListener(jboolean enable) {
    if (enable) {
        if (mOnFirstTunnelFrameReadyNotification == NULL) {
            mOnFirstTunnelFrameReadyNotification = new AMessage(kWhatFirstTunnelFrameReady, this);
        }
    } else {
        mOnFirstTunnelFrameReadyNotification.clear();
    }

    return mCodec->setOnFirstTunnelFrameReadyNotification(mOnFirstTunnelFrameReadyNotification);
}

status_t JMediaCodec::enableOnFrameRenderedListener(jboolean enable) {
    if (enable) {
        if (mOnFrameRenderedNotification == NULL) {
@@ -1058,6 +1071,27 @@ void JMediaCodec::handleCallback(const sp<AMessage> &msg) {
    env->DeleteLocalRef(obj);
}

void JMediaCodec::handleFirstTunnelFrameReadyNotification(const sp<AMessage> &msg) {
    int32_t arg1 = 0, arg2 = 0;
    jobject obj = NULL;
    JNIEnv *env = AndroidRuntime::getJNIEnv();

    sp<AMessage> data;
    CHECK(msg->findMessage("data", &data));

    status_t err = ConvertMessageToMap(env, data, &obj);
    if (err != OK) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return;
    }

    env->CallVoidMethod(
            mObject, gFields.postEventFromNativeID,
            EVENT_FIRST_TUNNEL_FRAME_READY, arg1, arg2, obj);

    env->DeleteLocalRef(obj);
}

void JMediaCodec::handleFrameRenderedNotification(const sp<AMessage> &msg) {
    int32_t arg1 = 0, arg2 = 0;
    jobject obj = NULL;
@@ -1100,6 +1134,11 @@ void JMediaCodec::onMessageReceived(const sp<AMessage> &msg) {
            }
            break;
        }
        case kWhatFirstTunnelFrameReady:
        {
            handleFirstTunnelFrameReadyNotification(msg);
            break;
        }
        default:
            TRESPASS();
    }
@@ -1256,6 +1295,22 @@ static jint throwExceptionAsNecessary(
    }
}

static void android_media_MediaCodec_native_enableOnFirstTunnelFrameReadyListener(
        JNIEnv *env,
        jobject thiz,
        jboolean enabled) {
    sp<JMediaCodec> codec = getMediaCodec(env, thiz);

    if (codec == NULL || codec->initCheck() != OK) {
        throwExceptionAsNecessary(env, INVALID_OPERATION);
        return;
    }

    status_t err = codec->enableOnFirstTunnelFrameReadyListener(enabled);

    throwExceptionAsNecessary(env, err);
}

static void android_media_MediaCodec_native_enableOnFrameRenderedListener(
        JNIEnv *env,
        jobject thiz,
@@ -3138,6 +3193,9 @@ static const JNINativeMethod gMethods[] = {
    { "native_setInputSurface", "(Landroid/view/Surface;)V",
      (void *)android_media_MediaCodec_setInputSurface },

    { "native_enableOnFirstTunnelFrameReadyListener", "(Z)V",
      (void *)android_media_MediaCodec_native_enableOnFirstTunnelFrameReadyListener },

    { "native_enableOnFrameRenderedListener", "(Z)V",
      (void *)android_media_MediaCodec_native_enableOnFrameRenderedListener },

+5 −0
Original line number Diff line number Diff line
@@ -63,6 +63,8 @@ struct JMediaCodec : public AHandler {
    void release();
    void releaseAsync();

    status_t enableOnFirstTunnelFrameReadyListener(jboolean enable);

    status_t enableOnFrameRenderedListener(jboolean enable);

    status_t setCallback(jobject cb);
@@ -176,6 +178,7 @@ private:
        kWhatCallbackNotify,
        kWhatFrameRendered,
        kWhatAsyncReleaseComplete,
        kWhatFirstTunnelFrameReady,
    };

    jclass mClass;
@@ -191,6 +194,7 @@ private:
    std::once_flag mAsyncReleaseFlag;

    sp<AMessage> mCallbackNotification;
    sp<AMessage> mOnFirstTunnelFrameReadyNotification;
    sp<AMessage> mOnFrameRenderedNotification;

    status_t mInitStatus;
@@ -203,6 +207,7 @@ private:
            jobject *buf) const;

    void handleCallback(const sp<AMessage> &msg);
    void handleFirstTunnelFrameReadyNotification(const sp<AMessage> &msg);
    void handleFrameRenderedNotification(const sp<AMessage> &msg);

    DISALLOW_EVIL_CONSTRUCTORS(JMediaCodec);