AndroidのMadiaPlayerはSurfaceをどうやってNativeに渡しているか?

本家フォーラムより。

Surface to Native - Android Developers | Google Groups

SurfaceオブジェクトをNativeで使いたいということのようだ。

Googleのエンジニアからのレスとして

Sorry, there is currently no API in the NDK for working with surfaces.

NDKには今そういうAPIは無いと。まぁそうだろう。

How is the existing applications passing surface to their native libs?
Example Media Player or recorder Java Application to Opencore?

ということで、既存のアプリ、MediaPlayerなどはどうやってJavaからSurfaceをNative(OpenCore)に渡しているのか?

これは、実際にソースを読むと分かるのだけど、frameworks/base/media/java/android/madia/MediaPlayer.java のsetDisplay()でMediaPlayer.mSurfaceにSurfaceオブジェクトのインスタンスが格納される。

そしてMediaPlayer.Create()から、

public native void prepare() throws IOException, IllegalStateException;

nativeのprepare()が呼ばれる。

これがJNI層のframeworks/base/media/jni/androidmediaMediaPlayer.cppのandroidmediaMediaPlayer_prepare()に関連づけられていて、SurfaceオブジェクトはNative層に降りてくる。

    jobject surface = env->GetObjectField(thiz, fields.surface);
    if (surface != NULL) {
        const sp<Surface>& native_surface = get_surface(env, surface);
        LOGV("prepare: surface=%p (id=%d)",
             native_surface.get(), native_surface->ID());
        mp->setVideoSurface(native_surface);
    }

JNIのGetObjectField()を使ってJavaのSurfaceオブジェクトを取得し、それからget_surface()を使ってNativeのSurfaceオブジェクトにしている。具体的には以下の行。

        const sp&lt;Surface&gt;&amp; native_surface = get_surface(env, surface);

さて、このget_surface()は、以下のような感じ。

static sp<Surface> get_surface(JNIEnv* env, jobject clazz)
{
    Surface* const p = (Surface*)env->GetIntField(clazz, fields.surface_native);
    return sp<Surface>(p);
}

単にキャストしてるだけに見える。その仕組みとは。

そもそも、JavaのSurfaceクラスと、NativeのSurfaceオブジェクトは一心同体で、JavaでSurfaceクラスのインスタンスを作ると、その時点で対応するNativeのSurfaceクラスのインスタンスも作られる仕組みになっている。

JavaのSurfaceのコンストラクタの中でJNIを通してNativeのSurfaceのインスタンスを作り、そのポインタ(アドレス)をSurface.javaのmSurfaceフィールドに保持して関連づけている。あとは、必要に応じてJavaとNativeでそれぞれのSurfaceインスタンスを取りだして操作しているのだ。

JavaのクラスとNativeのクラスとの関連づけの方法は応用が利きそう。Java側はNativeのことを考えずにインスタンスを使うことができ、そして必要な時にNativeで処理させるって具合で。

コメント

タイトルとURLをコピーしました