media: add SyncSettings to MediaSync and MediaPlayer

Bug: 18249558
Change-Id: I76d7e20d0e0c2d1d24bf60ade6386986573476f0
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 85c6c67..f446d6c 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -50,6 +50,7 @@
 import android.media.SubtitleController.Anchor;
 import android.media.SubtitleData;
 import android.media.SubtitleTrack.RenderingWidget;
+import android.media.SyncSettings;
 
 import com.android.internal.app.IAppOpsService;
 
@@ -1447,6 +1448,28 @@
     public native PlaybackSettings getPlaybackSettings();
 
     /**
+     * Sets A/V sync mode.
+     *
+     * @param settings the A/V sync settings to apply
+     *
+     * @throws IllegalStateException if the internal player engine has not been
+     * initialized.
+     * @throws IllegalArgumentException if settings are not supported.
+     */
+    public native void setSyncSettings(@NonNull SyncSettings settings);
+
+    /**
+     * Gets the A/V sync mode.
+     *
+     * @return the A/V sync settings
+     *
+     * @throws IllegalStateException if the internal player engine has not been
+     * initialized.
+     */
+    @NonNull
+    public native SyncSettings getSyncSettings();
+
+    /**
      * Seeks to specified time position.
      *
      * @param msec the offset in milliseconds from the start to seek to
diff --git a/media/java/android/media/MediaSync.java b/media/java/android/media/MediaSync.java
index cc894cb..bfcefb4 100644
--- a/media/java/android/media/MediaSync.java
+++ b/media/java/android/media/MediaSync.java
@@ -441,6 +441,44 @@
 
     private native final void native_setPlaybackRate(float rate);
 
+    /**
+     * Sets A/V sync mode.
+     *
+     * @param settings the A/V sync settings to apply
+     *
+     * @throws IllegalStateException if the internal player engine has not been
+     * initialized.
+     * @throws IllegalArgumentException if settings are not supported.
+     */
+    public native void setSyncSettings(@NonNull SyncSettings settings);
+
+    /**
+     * Gets the A/V sync mode.
+     *
+     * @return the A/V sync settings
+     *
+     * @throws IllegalStateException if the internal player engine has not been
+     * initialized.
+     */
+    @NonNull
+    public native SyncSettings getSyncSettings();
+
+    /**
+     * Flushes all buffers from the sync object.
+     * <p>
+     * No callbacks are received for the flushed buffers.
+     *
+     * @throws IllegalStateException if the internal player engine has not been
+     * initialized.
+     */
+    public void flush() {
+        synchronized(mAudioLock) {
+            mAudioBuffers.clear();
+            mCallbackHandler.removeCallbacksAndMessages(null);
+        }
+        // TODO implement this for surface buffers.
+    }
+
    /**
     * Get current playback position.
     * <p>
diff --git a/media/java/android/media/SyncSettings.java b/media/java/android/media/SyncSettings.java
new file mode 100644
index 0000000..9740147
--- /dev/null
+++ b/media/java/android/media/SyncSettings.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2015 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.
+ */
+
+package android.media;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import android.annotation.IntDef;
+
+/**
+ * Structure for common A/V sync settings.
+ *
+ * Used by {@link MediaSync} {link MediaSync#getSyncSettings()} and
+ * {link MediaSync#setSyncSettings(SyncSettings)}
+ * to control A/V sync behavior.
+ * <p> <strong>audio adjust mode:</strong>
+ * select handling of audio track when changing playback speed due to sync.
+ * <ul>
+ * <li> {@link SyncSettings#AUDIO_ADJUST_MODE_DEFAULT}:
+ *   System will determine best handling. </li>
+ * <li> {@link SyncSettings#AUDIO_ADJUST_MODE_STRETCH}:
+ *   Change the speed of audio playback without altering its pitch.</li>
+ * <li> {@link SyncSettings#AUDIO_ADJUST_MODE_RESAMPLE}:
+ *   Change the speed of audio playback by resampling the audio.</li>
+ * </ul>
+ * <p> <strong>sync source:</strong> select
+ * clock source for sync.
+ * <ul>
+ * <li> {@link SyncSettings#SYNC_SOURCE_DEFAULT}:
+ *   System will determine best selection.</li>
+ * <li> {@link SyncSettings#SYNC_SOURCE_SYSTEM_CLOCK}:
+ *   Use system clock for sync source.</li>
+ * <li> {@link SyncSettings#SYNC_SOURCE_AUDIO}:
+ *   Use audio track for sync source.</li>
+ * <li> {@link SyncSettings#SYNC_SOURCE_VSYNC}:
+ *   Syncronize media to vsync.</li>
+ * </ul>
+ * <p> <strong>tolerance:</strong> specifies the amount of allowed playback rate
+ * change to keep media in sync with the sync source. The handling of this depends
+ * on the sync source.
+ * <p> <strong>frameRate:</strong> initial hint for video frame rate. Used when
+ * sync source is vsync.
+ */
+public final class SyncSettings {
+    /** @hide */
+    @IntDef(
+        value = {
+                SYNC_SOURCE_DEFAULT,
+                SYNC_SOURCE_SYSTEM_CLOCK,
+                SYNC_SOURCE_AUDIO,
+                SYNC_SOURCE_VSYNC,
+        }
+    )
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SyncSource {}
+
+    /**
+     * Use the default sync source (default). If media has video, the sync renders to a
+     * surface that directly renders to a display, and tolerance is non zero (e.g. not
+     * less than 0.001) vsync source is used for clock source.  Otherwise, if media has
+     * audio, audio track is used. Finally, if media has no audio, system clock is used.
+     */
+    public static final int SYNC_SOURCE_DEFAULT = 0;
+
+    /**
+     * Use system monotonic clock for sync source.
+     *
+     * @see System#nanoTime
+     */
+    public static final int SYNC_SOURCE_SYSTEM_CLOCK = 1;
+
+    /**
+     * Use audio track for sync source. This requires audio data and an audio track.
+     *
+     * @see AudioTrack#getTimeStamp
+     */
+    public static final int SYNC_SOURCE_AUDIO = 2;
+
+    /**
+     * Use vsync as the sync source. This requires video data and an output surface that
+     * directly renders to the display, e.g. {@link android.view.SurfaceView}
+     * <p>
+     * This mode allows smoother playback experience by adjusting the playback speed
+     * to match the vsync rate, e.g. playing 30fps content on a 59.94Hz display.
+     * When using this mode, the tolerance should be set to greater than 0 (e.g. at least
+     * 1/1000), so that the playback speed can actually be adjusted.
+     * <p>
+     * This mode can also be used to play 25fps content on a 60Hz display using
+     * a 2:3 pulldown (basically playing the content at 24fps), which results on
+     * better playback experience on most devices. In this case the tolerance should be
+     * at least (1/24).
+     *
+     * @see android.view.Choreographer.FrameCallback#doFrame
+     * @see android.view.Display#getAppVsyncOffsetNanos
+     */
+    public static final int SYNC_SOURCE_VSYNC = 3;
+
+    /** @hide */
+    @IntDef(
+        value = {
+                AUDIO_ADJUST_MODE_DEFAULT,
+                AUDIO_ADJUST_MODE_STRETCH,
+                AUDIO_ADJUST_MODE_RESAMPLE,
+        }
+    )
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AudioAdjustMode {}
+
+    /**
+     * System will determine best handling of audio for playback rate
+     * adjustments.
+     * <p>
+     * Used by default. This will make audio play faster or slower as required
+     * by the sync source without changing its pitch; however, system may fall
+     * back to some other method (e.g. change the pitch, or mute the audio) if
+     * time stretching is no longer supported for the playback rate.
+     */
+    public static final int AUDIO_ADJUST_MODE_DEFAULT = 0;
+
+    /**
+     * Time stretch audio when playback rate must be adjusted.
+     * <p>
+     * This will make audio play faster or slower as required by the sync source
+     * without changing its pitch, as long as it is supported for the playback
+     * rate.
+     *
+     * @see MediaSync#PLAYBACK_RATE_AUDIO_MODE_STRETCH
+     * @see MediaPlayer#PLAYBACK_RATE_AUDIO_MODE_STRETCH
+     */
+    public static final int AUDIO_ADJUST_MODE_STRETCH = 1;
+
+    /**
+     * Resample audio when playback rate must be adjusted.
+     * <p>
+     * This will make audio play faster or slower as required by the sync source
+     * by changing its pitch (making it lower to play slower, and higher to play
+     * faster.)
+     *
+     * @see MediaSync#PLAYBACK_RATE_AUDIO_MODE_RESAMPLE
+     * @see MediaPlayer#PLAYBACK_RATE_AUDIO_MODE_RESAMPLE
+     */
+    public static final int AUDIO_ADJUST_MODE_RESAMPLE = 2;
+
+    // flags to indicate which settings are actually set
+    private static final int SET_SYNC_SOURCE         = 1 << 0;
+    private static final int SET_AUDIO_ADJUST_MODE   = 1 << 1;
+    private static final int SET_TOLERANCE           = 1 << 2;
+    private static final int SET_FRAME_RATE          = 1 << 3;
+    private int mSet = 0;
+
+    // settings
+    private int mAudioAdjustMode = AUDIO_ADJUST_MODE_STRETCH;
+    private int mSyncSource = SYNC_SOURCE_DEFAULT;
+    private float mTolerance = 0.f;
+    private float mFrameRate = 0.f;
+
+    /**
+     * Allows defaults to be returned for properties not set.
+     * Otherwise a {@link java.lang.IllegalArgumentException} exception
+     * is raised when getting those properties
+     * which have defaults but have never been set.
+     * @return this <code>SyncSettings</code> instance.
+     */
+    public SyncSettings allowDefaults() {
+        mSet |= SET_SYNC_SOURCE | SET_AUDIO_ADJUST_MODE | SET_TOLERANCE;
+        return this;
+    }
+
+    /**
+     * Sets the audio adjust mode.
+     * @param audioAdjustMode
+     * @return this <code>SyncSettings</code> instance.
+     */
+    public SyncSettings setAudioAdjustMode(@AudioAdjustMode int audioAdjustMode) {
+        mAudioAdjustMode = audioAdjustMode;
+        mSet |= SET_AUDIO_ADJUST_MODE;
+        return this;
+    }
+
+    /**
+     * Retrieves the audio adjust mode.
+     * @return audio adjust mode
+     * @throws IllegalStateException if the audio adjust mode is not set.
+     */
+    public @AudioAdjustMode int getAudioAdjustMode() {
+        if ((mSet & SET_AUDIO_ADJUST_MODE) == 0) {
+            throw new IllegalStateException("audio adjust mode not set");
+        }
+        return mAudioAdjustMode;
+    }
+
+    /**
+     * Sets the sync source.
+     * @param syncSource
+     * @return this <code>SyncSettings</code> instance.
+     */
+    public SyncSettings setSyncSource(@SyncSource int syncSource) {
+        mSyncSource = syncSource;
+        mSet |= SET_SYNC_SOURCE;
+        return this;
+    }
+
+    /**
+     * Retrieves the sync source.
+     * @return sync source
+     * @throws IllegalStateException if the sync source is not set.
+     */
+    public @SyncSource int getSyncSource() {
+        if ((mSet & SET_SYNC_SOURCE) == 0) {
+            throw new IllegalStateException("sync source not set");
+        }
+        return mSyncSource;
+    }
+
+    /**
+     * Sets the tolerance. The default tolerance is 0.
+     * @param tolerance A non-negative number representing
+     *     the maximum deviation of the playback rate from the playback rate
+     *     set. ({@code abs(actual_rate - set_rate) / set_rate})
+     * @return this <code>SyncSettings</code> instance.
+     */
+    public SyncSettings setTolerance(float tolerance) {
+        mTolerance = tolerance;
+        mSet |= SET_TOLERANCE;
+        return this;
+    }
+
+    /**
+     * Retrieves the tolerance factor.
+     * @return tolerance factor. A non-negative number representing
+     *     the maximum deviation of the playback rate from the playback rate
+     *     set. ({@code abs(actual_rate - set_rate) / set_rate})
+     * @throws IllegalStateException if tolerance is not set.
+     */
+    public float getTolerance() {
+        if ((mSet & SET_TOLERANCE) == 0) {
+            throw new IllegalStateException("tolerance not set");
+        }
+        return mTolerance;
+    }
+
+    /**
+     * Sets the video frame rate hint to be used. By default the frame rate is unspecified.
+     * @param frameRate A non-negative number used as an initial hint on
+     *     the video frame rate to be used when using vsync as the sync source.
+     * @return this <code>SyncSettings</code> instance.
+     */
+    public SyncSettings setFrameRate(float frameRate) {
+        mFrameRate = frameRate;
+        mSet |= SET_FRAME_RATE;
+        return this;
+    }
+
+    /**
+     * Retrieves the video frame rate hint.
+     * @return frame rate factor. A non-negative number representing
+     *     the maximum deviation of the playback rate from the playback rate
+     *     set. ({@code abs(actual_rate - set_rate) / set_rate})
+     * @throws IllegalStateException if frame rate is not set.
+     */
+    public float getFrameRate() {
+        if ((mSet & SET_FRAME_RATE) == 0) {
+            throw new IllegalStateException("frame rate not set");
+        }
+        return mFrameRate;
+    }
+
+}
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index c8464c7..dbb53b4 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -2,6 +2,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
+    android_media_AmrInputStream.cpp \
     android_media_ImageWriter.cpp \
     android_media_ImageReader.cpp \
     android_media_MediaCrypto.cpp \
@@ -14,12 +15,12 @@
     android_media_MediaMetadataRetriever.cpp \
     android_media_MediaMuxer.cpp \
     android_media_MediaPlayer.cpp \
+    android_media_MediaProfiles.cpp \
     android_media_MediaRecorder.cpp \
     android_media_MediaScanner.cpp \
     android_media_MediaSync.cpp \
     android_media_ResampleInputStream.cpp \
-    android_media_MediaProfiles.cpp \
-    android_media_AmrInputStream.cpp \
+    android_media_SyncSettings.cpp \
     android_media_Utils.cpp \
     android_mtp_MtpDatabase.cpp \
     android_mtp_MtpDevice.cpp \
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index b79a6bb..2c61779 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -39,6 +39,7 @@
 #include "utils/String8.h"
 #include "android_media_MediaDataSource.h"
 #include "android_media_PlaybackSettings.h"
+#include "android_media_SyncSettings.h"
 #include "android_media_Utils.h"
 
 #include "android_os_Parcel.h"
@@ -69,6 +70,7 @@
 static fields_t fields;
 
 static PlaybackSettings::fields_t gPlaybackSettingsFields;
+static SyncSettings::fields_t gSyncSettingsFields;
 
 static Mutex sLock;
 
@@ -476,6 +478,56 @@
 }
 
 static void
+android_media_MediaPlayer_setSyncSettings(JNIEnv *env, jobject thiz, jobject settings)
+{
+    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+    if (mp == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+
+    SyncSettings scs;
+    scs.fillFromJobject(env, gSyncSettingsFields, settings);
+    ALOGV("setSyncSettings: %d:%d %d:%d %d:%f %d:%f",
+            scs.syncSourceSet, scs.syncSource,
+            scs.audioAdjustModeSet, scs.audioAdjustMode,
+            scs.toleranceSet, scs.tolerance,
+            scs.frameRateSet, scs.frameRate);
+
+    // TODO: pass sync settings to mediaplayer when it supports it
+    // process_media_player_call(env, thiz, mp->setSyncSettings(scs), NULL, NULL);
+}
+
+static jobject
+android_media_MediaPlayer_getSyncSettings(JNIEnv *env, jobject thiz)
+{
+    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+    if (mp == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return NULL;
+    }
+
+    SyncSettings scs;
+    scs.syncSource = 0; // SYNC_SOURCE_DEFAULT
+    scs.audioAdjustMode = 0; // AUDIO_ADJUST_MODE_DEFAULT
+    scs.tolerance = 0.f;
+    scs.frameRate = 0.f;
+
+    // TODO: get this from mediaplayer when it supports it
+    // process_media_player_call(
+    //        env, thiz, mp->getSyncSettings(&scs), NULL, NULL);
+    ALOGV("getSyncSettings: %d %d %f %f",
+            scs.syncSource, scs.audioAdjustMode, scs.tolerance, scs.frameRate);
+
+    scs.syncSourceSet = true;
+    scs.audioAdjustModeSet = true;
+    scs.toleranceSet = true;
+    scs.frameRateSet = false;
+
+    return scs.asJobject(env, gSyncSettingsFields);
+}
+
+static void
 android_media_MediaPlayer_seekTo(JNIEnv *env, jobject thiz, jint msec)
 {
     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
@@ -760,6 +812,7 @@
     env->DeleteLocalRef(clazz);
 
     gPlaybackSettingsFields.init(env);
+    gSyncSettingsFields.init(env);
 }
 
 static void
@@ -950,6 +1003,8 @@
     {"getVideoHeight",      "()I",                              (void *)android_media_MediaPlayer_getVideoHeight},
     {"setPlaybackSettings", "(Landroid/media/PlaybackSettings;)V", (void *)android_media_MediaPlayer_setPlaybackSettings},
     {"getPlaybackSettings", "()Landroid/media/PlaybackSettings;", (void *)android_media_MediaPlayer_getPlaybackSettings},
+    {"setSyncSettings",     "(Landroid/media/SyncSettings;)V",  (void *)android_media_MediaPlayer_setSyncSettings},
+    {"getSyncSettings",     "()Landroid/media/SyncSettings;",   (void *)android_media_MediaPlayer_getSyncSettings},
     {"seekTo",              "(I)V",                             (void *)android_media_MediaPlayer_seekTo},
     {"_pause",              "()V",                              (void *)android_media_MediaPlayer_pause},
     {"isPlaying",           "()Z",                              (void *)android_media_MediaPlayer_isPlaying},
diff --git a/media/jni/android_media_MediaSync.cpp b/media/jni/android_media_MediaSync.cpp
index e167f83..72dacdf 100644
--- a/media/jni/android_media_MediaSync.cpp
+++ b/media/jni/android_media_MediaSync.cpp
@@ -21,6 +21,7 @@
 #include "android_media_MediaSync.h"
 
 #include "android_media_AudioTrack.h"
+#include "android_media_SyncSettings.h"
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/android_view_Surface.h"
 #include "jni.h"
@@ -46,6 +47,7 @@
 };
 
 static fields_t gFields;
+static SyncSettings::fields_t gSyncSettingsFields;
 
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -266,6 +268,55 @@
     return JNI_TRUE;
 }
 
+static void
+android_media_MediaSync_setSyncSettings(JNIEnv *env, jobject thiz, jobject settings)
+{
+    sp<JMediaSync> sync = getMediaSync(env, thiz);
+    if (sync == NULL) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return;
+    }
+
+    SyncSettings scs;
+    scs.fillFromJobject(env, gSyncSettingsFields, settings);
+    ALOGV("setSyncSettings: %d:%d %d:%d %d:%f %d:%f",
+            scs.syncSourceSet, scs.syncSource,
+            scs.audioAdjustModeSet, scs.audioAdjustMode,
+            scs.toleranceSet, scs.tolerance,
+            scs.frameRateSet, scs.frameRate);
+
+    // TODO: pass sync settings to mediasync when it supports it
+}
+
+static jobject
+android_media_MediaSync_getSyncSettings(JNIEnv *env, jobject thiz)
+{
+    sp<JMediaSync> sync = getMediaSync(env, thiz);
+    if (sync == NULL) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return NULL;
+    }
+
+    SyncSettings scs;
+    scs.syncSource = 0; // SYNC_SOURCE_DEFAULT
+    scs.audioAdjustMode = 0; // AUDIO_ADJUST_MODE_DEFAULT
+    scs.tolerance = 0.f;
+    scs.frameRate = 0.f;
+
+    // TODO: get this from mediaplayer when it supports it
+    // process_media_player_call(
+    //        env, thiz, mp->getSyncSettings(&scs), NULL, NULL);
+    ALOGV("getSyncSettings: %d %d %f %f",
+            scs.syncSource, scs.audioAdjustMode, scs.tolerance, scs.frameRate);
+
+    scs.syncSourceSet = true;
+    scs.audioAdjustModeSet = true;
+    scs.toleranceSet = true;
+    scs.frameRateSet = false;
+
+    return scs.asJobject(env, gSyncSettingsFields);
+}
+
 static void android_media_MediaSync_native_init(JNIEnv *env) {
     ScopedLocalRef<jclass> clazz(env, env->FindClass("android/media/MediaSync"));
     CHECK(clazz.get() != NULL);
@@ -287,6 +338,8 @@
     gFields.mediaTimestampClockRateID =
         env->GetFieldID(clazz.get(), "clockRate", "F");
     CHECK(gFields.mediaTimestampClockRateID != NULL);
+
+    gSyncSettingsFields.init(env);
 }
 
 static void android_media_MediaSync_native_setup(JNIEnv *env, jobject thiz) {
@@ -342,6 +395,10 @@
 
     { "native_setPlaybackRate", "(F)V", (void *)android_media_MediaSync_native_setPlaybackRate },
 
+    { "setSyncSettings", "(Landroid/media/SyncSettings;)V", (void *)android_media_MediaSync_setSyncSettings},
+
+    { "getSyncSettings", "()Landroid/media/SyncSettings;", (void *)android_media_MediaSync_getSyncSettings},
+
     { "native_finalize", "()V", (void *)android_media_MediaSync_native_finalize },
 };
 
diff --git a/media/jni/android_media_SyncSettings.cpp b/media/jni/android_media_SyncSettings.cpp
new file mode 100644
index 0000000..2f0605e
--- /dev/null
+++ b/media/jni/android_media_SyncSettings.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2015, 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 "android_media_SyncSettings.h"
+
+#include "JNIHelp.h"
+
+namespace android {
+
+void SyncSettings::fields_t::init(JNIEnv *env) {
+    jclass lclazz = env->FindClass("android/media/SyncSettings");
+    if (lclazz == NULL) {
+        return;
+    }
+
+    clazz = (jclass)env->NewGlobalRef(lclazz);
+    if (clazz == NULL) {
+        return;
+    }
+
+    constructID = env->GetMethodID(clazz, "<init>", "()V");
+
+    sync_source = env->GetFieldID(clazz, "mSyncSource", "I");
+    audio_adjust_mode = env->GetFieldID(clazz, "mAudioAdjustMode", "I");
+    tolerance = env->GetFieldID(clazz, "mTolerance", "F");
+    frame_rate = env->GetFieldID(clazz, "mFrameRate", "F");
+    set = env->GetFieldID(clazz, "mSet", "I");
+
+    set_sync_source =
+        env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "SET_SYNC_SOURCE", "I"));
+    set_audio_adjust_mode = env->GetStaticIntField(
+            clazz, env->GetStaticFieldID(clazz, "SET_AUDIO_ADJUST_MODE", "I"));
+    set_tolerance =
+        env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "SET_TOLERANCE", "I"));
+    set_frame_rate =
+        env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "SET_FRAME_RATE", "I"));
+
+    env->DeleteLocalRef(lclazz);
+}
+
+void SyncSettings::fields_t::exit(JNIEnv *env) {
+    env->DeleteGlobalRef(clazz);
+    clazz = NULL;
+}
+
+void SyncSettings::fillFromJobject(JNIEnv *env, const fields_t& fields, jobject settings) {
+    syncSource = env->GetIntField(settings, fields.sync_source);
+    audioAdjustMode = env->GetIntField(settings, fields.audio_adjust_mode);
+    tolerance = env->GetFloatField(settings, fields.tolerance);
+    frameRate = env->GetFloatField(settings, fields.frame_rate);
+    int set = env->GetIntField(settings, fields.set);
+
+    syncSourceSet = set & fields.set_sync_source;
+    audioAdjustModeSet = set & fields.set_audio_adjust_mode;
+    toleranceSet = set & fields.set_tolerance;
+    frameRateSet = set & fields.set_frame_rate;
+}
+
+jobject SyncSettings::asJobject(JNIEnv *env, const fields_t& fields) {
+    jobject settings = env->NewObject(fields.clazz, fields.constructID);
+    if (settings == NULL) {
+        return NULL;
+    }
+    env->SetIntField(settings, fields.sync_source, (jint)syncSource);
+    env->SetIntField(settings, fields.audio_adjust_mode, (jint)audioAdjustMode);
+    env->SetFloatField(settings, fields.tolerance, (jfloat)tolerance);
+    env->SetFloatField(settings, fields.frame_rate, (jfloat)frameRate);
+    env->SetIntField(
+            settings, fields.set,
+            (syncSourceSet ? fields.set_sync_source : 0)
+                    | (audioAdjustModeSet ? fields.set_audio_adjust_mode : 0)
+                    | (toleranceSet ? fields.set_tolerance : 0)
+                    | (frameRateSet ? fields.set_frame_rate : 0));
+
+    return settings;
+}
+
+}  // namespace android
diff --git a/media/jni/android_media_SyncSettings.h b/media/jni/android_media_SyncSettings.h
new file mode 100644
index 0000000..586533f
--- /dev/null
+++ b/media/jni/android_media_SyncSettings.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2015, 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_SYNC_SETTINGS_H_
+#define _ANDROID_MEDIA_SYNC_SETTINGS_H_
+
+#include "jni.h"
+
+namespace android {
+
+struct SyncSettings {
+    // keep this here until it is implemented
+    int syncSource;
+    int audioAdjustMode;
+    float tolerance;
+    float frameRate;
+
+    bool syncSourceSet;
+    bool audioAdjustModeSet;
+    bool toleranceSet;
+    bool frameRateSet;
+
+    struct fields_t {
+        jclass      clazz;
+        jmethodID   constructID;
+
+        jfieldID    sync_source;
+        jfieldID    audio_adjust_mode;
+        jfieldID    tolerance;
+        jfieldID    frame_rate;
+        jfieldID    set;
+        jint        set_sync_source;
+        jint        set_audio_adjust_mode;
+        jint        set_tolerance;
+        jint        set_frame_rate;
+
+        // initializes fields
+        void init(JNIEnv *env);
+
+        // releases global references held
+        void exit(JNIEnv *env);
+    };
+
+    // fills this from an android.media.SyncSettings object
+    void fillFromJobject(JNIEnv *env, const fields_t& fields, jobject settings);
+
+    // returns this as a android.media.SyncSettings object
+    jobject asJobject(JNIEnv *env, const fields_t& fields);
+};
+
+}  // namespace android
+
+#endif  // _ANDROID_MEDIA_SYNC_SETTINGS_H_