diff options
| author | 2020-01-14 17:29:03 -0800 | |
|---|---|---|
| committer | 2020-01-16 12:30:26 -0800 | |
| commit | 59dffec3f16bdfad8c882fbabfa907eb1b2f875c (patch) | |
| tree | 008e3b3cdc9c1fd80fdc3a124e59829c78b96f56 | |
| parent | 72973ef6e87cc960e0568833147f954bd0f33871 (diff) | |
AudioTrack: Add Tuner Configuration methods
Test: atest AudioTrackTest#testTunerConfiguration
Bug: 133526565
Change-Id: I0cc8e9e8b780e1a87c38656c8dfc67dac03969d8
| -rw-r--r-- | api/current.txt | 17 | ||||
| -rw-r--r-- | core/jni/android_media_AudioTrack.cpp | 221 | ||||
| -rw-r--r-- | media/java/android/media/AudioTrack.java | 193 |
3 files changed, 340 insertions, 91 deletions
diff --git a/api/current.txt b/api/current.txt index 16a9e49da032..77de35f66fc4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -24248,6 +24248,9 @@ package android.media { method public int write(@NonNull float[], int, int, int); method public int write(@NonNull java.nio.ByteBuffer, int, int); method public int write(@NonNull java.nio.ByteBuffer, int, int, long); + field public static final int ENCAPSULATION_MODE_ELEMENTARY_STREAM = 1; // 0x1 + field public static final int ENCAPSULATION_MODE_HANDLE = 2; // 0x2 + field public static final int ENCAPSULATION_MODE_NONE = 0; // 0x0 field public static final int ERROR = -1; // 0xffffffff field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa @@ -24274,10 +24277,12 @@ package android.media { method @NonNull public android.media.AudioTrack.Builder setAudioAttributes(@NonNull android.media.AudioAttributes) throws java.lang.IllegalArgumentException; method @NonNull public android.media.AudioTrack.Builder setAudioFormat(@NonNull android.media.AudioFormat) throws java.lang.IllegalArgumentException; method @NonNull public android.media.AudioTrack.Builder setBufferSizeInBytes(@IntRange(from=0) int) throws java.lang.IllegalArgumentException; + method @NonNull public android.media.AudioTrack.Builder setEncapsulationMode(int); method @NonNull public android.media.AudioTrack.Builder setOffloadedPlayback(boolean); method @NonNull public android.media.AudioTrack.Builder setPerformanceMode(int); method @NonNull public android.media.AudioTrack.Builder setSessionId(@IntRange(from=1) int) throws java.lang.IllegalArgumentException; method @NonNull public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException; + method @NonNull public android.media.AudioTrack.Builder setTunerConfiguration(@NonNull android.media.AudioTrack.TunerConfiguration); } public static final class AudioTrack.MetricsConstants { @@ -24305,6 +24310,18 @@ package android.media { method public void onTearDown(@NonNull android.media.AudioTrack); } + public static class AudioTrack.TunerConfiguration { + method public int getContentId(); + method public int getSyncId(); + } + + public static class AudioTrack.TunerConfiguration.Builder { + ctor public AudioTrack.TunerConfiguration.Builder(); + method @NonNull public android.media.AudioTrack.TunerConfiguration build(); + method @NonNull public android.media.AudioTrack.TunerConfiguration.Builder setContentId(@IntRange(from=1) int); + method @NonNull public android.media.AudioTrack.TunerConfiguration.Builder setSyncId(@IntRange(from=1) int); + } + public class CamcorderProfile { method public static android.media.CamcorderProfile get(int); method public static android.media.CamcorderProfile get(int, int); diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index c979133d2493..041019ec4841 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -102,6 +102,47 @@ class AudioTrackJniStorage { } }; +class TunerConfigurationHelper { + JNIEnv *const mEnv; + jobject const mTunerConfiguration; + + struct Ids { + Ids(JNIEnv *env) + : mClass(FindClassOrDie(env, "android/media/AudioTrack$TunerConfiguration")), + mContentId(GetFieldIDOrDie(env, mClass, "mContentId", "I")), + mSyncId(GetFieldIDOrDie(env, mClass, "mSyncId", "I")) {} + const jclass mClass; + const jfieldID mContentId; + const jfieldID mSyncId; + }; + + static const Ids &getIds(JNIEnv *env) { + // Meyer's singleton, initializes first time control passes through + // declaration in a block and is thread-safe per ISO/IEC 14882:2011 6.7.4. + static Ids ids(env); + return ids; + } + +public: + TunerConfigurationHelper(JNIEnv *env, jobject tunerConfiguration) + : mEnv(env), mTunerConfiguration(tunerConfiguration) {} + + int32_t getContentId() const { + if (mEnv == nullptr || mTunerConfiguration == nullptr) return 0; + const Ids &ids = getIds(mEnv); + return (int32_t)mEnv->GetIntField(mTunerConfiguration, ids.mContentId); + } + + int32_t getSyncId() const { + if (mEnv == nullptr || mTunerConfiguration == nullptr) return 0; + const Ids &ids = getIds(mEnv); + return (int32_t)mEnv->GetIntField(mTunerConfiguration, ids.mSyncId); + } + + // optional check to confirm class and field ids can be found. + static void initCheckOrDie(JNIEnv *env) { (void)getIds(env); } +}; + static Mutex sLock; static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies; @@ -213,24 +254,36 @@ sp<AudioTrack> android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audio } // ---------------------------------------------------------------------------- -static jint -android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa, - jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask, - jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession, - jlong nativeAudioTrack, jboolean offload) { - +static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, + jobject jaa, jintArray jSampleRate, + jint channelPositionMask, jint channelIndexMask, + jint audioFormat, jint buffSizeInBytes, jint memoryMode, + jintArray jSession, jlong nativeAudioTrack, + jboolean offload, jint encapsulationMode, + jobject tunerConfiguration) { ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d," - " nativeAudioTrack=0x%" PRIX64 ", offload=%d", - jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes, - nativeAudioTrack, offload); - - sp<AudioTrack> lpTrack = 0; + " nativeAudioTrack=0x%" PRIX64 ", offload=%d encapsulationMode=%d tuner=%p", + jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes, + nativeAudioTrack, offload, encapsulationMode, tunerConfiguration); if (jSession == NULL) { ALOGE("Error creating AudioTrack: invalid session ID pointer"); return (jint) AUDIO_JAVA_ERROR; } + // TODO: replace when we land matching AudioTrack::set() in frameworks/av in r or r-tv-dev. + if (tunerConfiguration != nullptr) { + const TunerConfigurationHelper tunerHelper(env, tunerConfiguration); + ALOGE("Error creating AudioTrack: unsupported tuner contentId:%d syncId:%d", + tunerHelper.getContentId(), tunerHelper.getSyncId()); + return (jint)AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; + } + // TODO: replace when we land matching AudioTrack::set() in frameworks/av in r or r-tv-dev. + if (encapsulationMode != 0 /* ENCAPSULATION_MODE_NONE */) { + ALOGE("Error creating AudioTrack: unsupported encapsulationMode %d", encapsulationMode); + return (jint)AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; + } + jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); if (nSession == NULL) { ALOGE("Error creating AudioTrack: Error retrieving session id pointer"); @@ -249,6 +302,7 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job } // if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one. + sp<AudioTrack> lpTrack; if (nativeAudioTrack == 0) { if (jaa == 0) { ALOGE("Error creating AudioTrack: invalid audio attributes"); @@ -1304,82 +1358,75 @@ static void android_media_AudioTrack_set_delay_padding(JNIEnv *env, jobject thi // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- static const JNINativeMethod gMethods[] = { - // name, signature, funcPtr - {"native_is_direct_output_supported", - "(IIIIIII)Z", - (void *)android_media_AudioTrack_is_direct_output_supported}, - {"native_start", "()V", (void *)android_media_AudioTrack_start}, - {"native_stop", "()V", (void *)android_media_AudioTrack_stop}, - {"native_pause", "()V", (void *)android_media_AudioTrack_pause}, - {"native_flush", "()V", (void *)android_media_AudioTrack_flush}, - {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZ)I", - (void *)android_media_AudioTrack_setup}, - {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize}, - {"native_release", "()V", (void *)android_media_AudioTrack_release}, - {"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_writeArray<jbyteArray>}, - {"native_write_native_bytes", - "(Ljava/nio/ByteBuffer;IIIZ)I", - (void *)android_media_AudioTrack_write_native_bytes}, - {"native_write_short", "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>}, - {"native_write_float", "([FIIIZ)I",(void *)android_media_AudioTrack_writeArray<jfloatArray>}, - {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume}, - {"native_get_buffer_size_frames", - "()I", (void *)android_media_AudioTrack_get_buffer_size_frames}, - {"native_set_buffer_size_frames", - "(I)I", (void *)android_media_AudioTrack_set_buffer_size_frames}, - {"native_get_buffer_capacity_frames", - "()I", (void *)android_media_AudioTrack_get_buffer_capacity_frames}, - {"native_set_playback_rate", - "(I)I", (void *)android_media_AudioTrack_set_playback_rate}, - {"native_get_playback_rate", - "()I", (void *)android_media_AudioTrack_get_playback_rate}, - {"native_set_playback_params", - "(Landroid/media/PlaybackParams;)V", - (void *)android_media_AudioTrack_set_playback_params}, - {"native_get_playback_params", - "()Landroid/media/PlaybackParams;", - (void *)android_media_AudioTrack_get_playback_params}, - {"native_set_marker_pos","(I)I", (void *)android_media_AudioTrack_set_marker_pos}, - {"native_get_marker_pos","()I", (void *)android_media_AudioTrack_get_marker_pos}, - {"native_set_pos_update_period", - "(I)I", (void *)android_media_AudioTrack_set_pos_update_period}, - {"native_get_pos_update_period", - "()I", (void *)android_media_AudioTrack_get_pos_update_period}, - {"native_set_position", "(I)I", (void *)android_media_AudioTrack_set_position}, - {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position}, - {"native_get_latency", "()I", (void *)android_media_AudioTrack_get_latency}, - {"native_get_underrun_count", "()I", (void *)android_media_AudioTrack_get_underrun_count}, - {"native_get_flags", "()I", (void *)android_media_AudioTrack_get_flags}, - {"native_get_timestamp", "([J)I", (void *)android_media_AudioTrack_get_timestamp}, - {"native_getMetrics", "()Landroid/os/PersistableBundle;", - (void *)android_media_AudioTrack_native_getMetrics}, - {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop}, - {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload}, - {"native_get_output_sample_rate", - "(I)I", (void *)android_media_AudioTrack_get_output_sample_rate}, - {"native_get_min_buff_size", - "(III)I", (void *)android_media_AudioTrack_get_min_buff_size}, - {"native_setAuxEffectSendLevel", - "(F)I", (void *)android_media_AudioTrack_setAuxEffectSendLevel}, - {"native_attachAuxEffect", - "(I)I", (void *)android_media_AudioTrack_attachAuxEffect}, - {"native_setOutputDevice", "(I)Z", - (void *)android_media_AudioTrack_setOutputDevice}, - {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId}, - {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback}, - {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback}, - {"native_applyVolumeShaper", - "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I", - (void *)android_media_AudioTrack_apply_volume_shaper}, - {"native_getVolumeShaperState", - "(I)Landroid/media/VolumeShaper$State;", - (void *)android_media_AudioTrack_get_volume_shaper_state}, - {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation}, - {"native_getPortId", "()I", (void *)android_media_AudioTrack_get_port_id}, - {"native_set_delay_padding", "(II)V", (void *)android_media_AudioTrack_set_delay_padding}, + // name, signature, funcPtr + {"native_is_direct_output_supported", "(IIIIIII)Z", + (void *)android_media_AudioTrack_is_direct_output_supported}, + {"native_start", "()V", (void *)android_media_AudioTrack_start}, + {"native_stop", "()V", (void *)android_media_AudioTrack_stop}, + {"native_pause", "()V", (void *)android_media_AudioTrack_pause}, + {"native_flush", "()V", (void *)android_media_AudioTrack_flush}, + {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZILjava/lang/Object;)I", + (void *)android_media_AudioTrack_setup}, + {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize}, + {"native_release", "()V", (void *)android_media_AudioTrack_release}, + {"native_write_byte", "([BIIIZ)I", (void *)android_media_AudioTrack_writeArray<jbyteArray>}, + {"native_write_native_bytes", "(Ljava/nio/ByteBuffer;IIIZ)I", + (void *)android_media_AudioTrack_write_native_bytes}, + {"native_write_short", "([SIIIZ)I", + (void *)android_media_AudioTrack_writeArray<jshortArray>}, + {"native_write_float", "([FIIIZ)I", + (void *)android_media_AudioTrack_writeArray<jfloatArray>}, + {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume}, + {"native_get_buffer_size_frames", "()I", + (void *)android_media_AudioTrack_get_buffer_size_frames}, + {"native_set_buffer_size_frames", "(I)I", + (void *)android_media_AudioTrack_set_buffer_size_frames}, + {"native_get_buffer_capacity_frames", "()I", + (void *)android_media_AudioTrack_get_buffer_capacity_frames}, + {"native_set_playback_rate", "(I)I", (void *)android_media_AudioTrack_set_playback_rate}, + {"native_get_playback_rate", "()I", (void *)android_media_AudioTrack_get_playback_rate}, + {"native_set_playback_params", "(Landroid/media/PlaybackParams;)V", + (void *)android_media_AudioTrack_set_playback_params}, + {"native_get_playback_params", "()Landroid/media/PlaybackParams;", + (void *)android_media_AudioTrack_get_playback_params}, + {"native_set_marker_pos", "(I)I", (void *)android_media_AudioTrack_set_marker_pos}, + {"native_get_marker_pos", "()I", (void *)android_media_AudioTrack_get_marker_pos}, + {"native_set_pos_update_period", "(I)I", + (void *)android_media_AudioTrack_set_pos_update_period}, + {"native_get_pos_update_period", "()I", + (void *)android_media_AudioTrack_get_pos_update_period}, + {"native_set_position", "(I)I", (void *)android_media_AudioTrack_set_position}, + {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position}, + {"native_get_latency", "()I", (void *)android_media_AudioTrack_get_latency}, + {"native_get_underrun_count", "()I", (void *)android_media_AudioTrack_get_underrun_count}, + {"native_get_flags", "()I", (void *)android_media_AudioTrack_get_flags}, + {"native_get_timestamp", "([J)I", (void *)android_media_AudioTrack_get_timestamp}, + {"native_getMetrics", "()Landroid/os/PersistableBundle;", + (void *)android_media_AudioTrack_native_getMetrics}, + {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop}, + {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload}, + {"native_get_output_sample_rate", "(I)I", + (void *)android_media_AudioTrack_get_output_sample_rate}, + {"native_get_min_buff_size", "(III)I", (void *)android_media_AudioTrack_get_min_buff_size}, + {"native_setAuxEffectSendLevel", "(F)I", + (void *)android_media_AudioTrack_setAuxEffectSendLevel}, + {"native_attachAuxEffect", "(I)I", (void *)android_media_AudioTrack_attachAuxEffect}, + {"native_setOutputDevice", "(I)Z", (void *)android_media_AudioTrack_setOutputDevice}, + {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId}, + {"native_enableDeviceCallback", "()V", + (void *)android_media_AudioTrack_enableDeviceCallback}, + {"native_disableDeviceCallback", "()V", + (void *)android_media_AudioTrack_disableDeviceCallback}, + {"native_applyVolumeShaper", + "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I", + (void *)android_media_AudioTrack_apply_volume_shaper}, + {"native_getVolumeShaperState", "(I)Landroid/media/VolumeShaper$State;", + (void *)android_media_AudioTrack_get_volume_shaper_state}, + {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation}, + {"native_getPortId", "()I", (void *)android_media_AudioTrack_get_port_id}, + {"native_set_delay_padding", "(II)V", (void *)android_media_AudioTrack_set_delay_padding}, }; - // field names found in android/media/AudioTrack.java #define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" #define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj" @@ -1436,6 +1483,10 @@ int register_android_media_AudioTrack(JNIEnv *env) gPlaybackParamsFields.init(env); gVolumeShaperFields.init(env); + + // optional check that the TunerConfiguration class and fields exist. + TunerConfigurationHelper::initCheckOrDie(env); + return res; } diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 0ced68ef8695..4dbc79b54199 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -213,6 +213,36 @@ public class AudioTrack extends PlayerBase private final static String TAG = "android.media.AudioTrack"; + /** @hide */ + @IntDef({ + ENCAPSULATION_MODE_NONE, + ENCAPSULATION_MODE_ELEMENTARY_STREAM, + ENCAPSULATION_MODE_HANDLE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EncapsulationMode {} + + // Important: The ENCAPSULATION_MODE values must be kept in sync with native header files. + /** + * This mode indicates no metadata encapsulation, + * which is the default mode for sending audio data + * through {@code AudioTrack}. + */ + public static final int ENCAPSULATION_MODE_NONE = 0; + /** + * This mode indicates metadata encapsulation with an elementary stream payload. + * Both compressed and PCM format is allowed. + * + * TODO(b/147778408) Link: See the Android developers guide for more information. + */ + public static final int ENCAPSULATION_MODE_ELEMENTARY_STREAM = 1; + /** + * This mode indicates metadata encapsulation with a handle payload. + * The handle is a 64 bit long, provided by the Tuner API. + * + * TODO(b/147778408) Link: Fill in Tuner API to obtain the handle. + */ + public static final int ENCAPSULATION_MODE_HANDLE = 2; /** @hide */ @IntDef({ @@ -592,11 +622,13 @@ public class AudioTrack extends PlayerBase public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes, int mode, int sessionId) throws IllegalArgumentException { - this(attributes, format, bufferSizeInBytes, mode, sessionId, false /*offload*/); + this(attributes, format, bufferSizeInBytes, mode, sessionId, false /*offload*/, + ENCAPSULATION_MODE_NONE, null /* tunerConfiguration */); } private AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes, - int mode, int sessionId, boolean offload) + int mode, int sessionId, boolean offload, int encapsulationMode, + @Nullable TunerConfiguration tunerConfiguration) throws IllegalArgumentException { super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_JAM_AUDIOTRACK); // mState already == STATE_UNINITIALIZED @@ -663,7 +695,7 @@ public class AudioTrack extends PlayerBase int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes, sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat, mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/, - offload); + offload, encapsulationMode, tunerConfiguration); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing AudioTrack."); return; // with mState == STATE_UNINITIALIZED @@ -672,6 +704,8 @@ public class AudioTrack extends PlayerBase mSampleRate = sampleRate[0]; mSessionId = session[0]; + // TODO: consider caching encapsulationMode and tunerConfiguration in the Java object. + if ((mAttributes.getFlags() & AudioAttributes.FLAG_HW_AV_SYNC) != 0) { int frameSizeInBytes; if (AudioFormat.isEncodingLinearFrames(mAudioFormat)) { @@ -745,7 +779,9 @@ public class AudioTrack extends PlayerBase 0 /*mDataLoadMode - NA*/, session, nativeTrackInJavaObj, - false /*offload*/); + false /*offload*/, + ENCAPSULATION_MODE_NONE, + null /* tunerConfiguration */); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing AudioTrack."); return; // with mState == STATE_UNINITIALIZED @@ -758,6 +794,99 @@ public class AudioTrack extends PlayerBase } /** + * TunerConfiguration is used to convey tuner information + * from the android.media.tv.Tuner API to AudioTrack construction. + * + * Use the Builder to construct the TunerConfiguration object, + * which is then used by the {@link AudioTrack.Builder} to create an AudioTrack. + */ + public static class TunerConfiguration { + private final int mContentId; + private final int mSyncId; + + private TunerConfiguration(int contentId, int syncId) { + mContentId = contentId; + mSyncId = syncId; + } + + /** + * Returns the contentId. + */ + public int getContentId() { + return mContentId; + } + + /** + * Returns the syncId. + */ + public int getSyncId() { + return mSyncId; + } + + /** + * Builder class for {@link AudioTrack.TunerConfiguration} objects. + */ + public static class Builder { + private int mContentId; + private int mSyncId; + + /** + * Sets the contentId from the Tuner filter. + * + * @param contentId selects the audio stream to use. + * See android.media.tv.tuner.filter.Filter#getId(). + * This is always a positive number. + * TODO(b/147778408) Link to tuner filter doc when unhidden. + * @return the same Builder instance. + */ + public @NonNull Builder setContentId(@IntRange(from = 1) int contentId) { + if (contentId < 1) { + throw new IllegalArgumentException( + "contentId " + contentId + " must be positive"); + } + mContentId = contentId; + return this; + } + + /** + * Sets the syncId from the Tuner filter. + * + * @param syncId selects the clock to use for synchronization + * of audio with other streams such as video. + * See android.media.tv.tuner.Tuner#getAvSyncHwId(). + * This is always a positive number. + * TODO(b/147778408) Link to tuner filter doc when unhidden. + * @return the same Builder instance. + */ + public @NonNull Builder setSyncId(@IntRange(from = 1) int syncId) { + if (syncId < 1) { + throw new IllegalArgumentException("syncId " + syncId + " must be positive"); + } + mSyncId = syncId; + return this; + } + + /** + * Builds a {@link AudioTrack.TunerConfiguration} instance initialized with + * the parameters set on this {@code Builder}. + * + * @return a new successfully initialized {@link AudioTrack.TunerConfiguration}. + * @throws UnsupportedOperationException if the parameters set on the + * {@code Builder} are incompatible. + */ + public @NonNull TunerConfiguration build() { + if (mContentId < 1 || mSyncId < 1) { + throw new UnsupportedOperationException( + "contentId " + mContentId + + " syncId " + mSyncId + + " must be set"); + } + return new TunerConfiguration(mContentId, mSyncId); + } + } + } + + /** * Builder class for {@link AudioTrack} objects. * Use this class to configure and create an <code>AudioTrack</code> instance. By setting audio * attributes and audio format parameters, you indicate which of those vary from the default @@ -799,10 +928,12 @@ public class AudioTrack extends PlayerBase private AudioAttributes mAttributes; private AudioFormat mFormat; private int mBufferSizeInBytes; + private int mEncapsulationMode = ENCAPSULATION_MODE_NONE; private int mSessionId = AudioManager.AUDIO_SESSION_ID_GENERATE; private int mMode = MODE_STREAM; private int mPerformanceMode = PERFORMANCE_MODE_NONE; private boolean mOffload = false; + private TunerConfiguration mTunerConfiguration; /** * Constructs a new Builder with the default values as described above. @@ -869,6 +1000,34 @@ public class AudioTrack extends PlayerBase } /** + * Sets the encapsulation mode. + * + * Encapsulation mode allows metadata to be sent together with + * the audio data payload in a {@code ByteBuffer}. + * The data format is specified in the Android developers site. + * + * TODO(b/147778408) Link to doc page. + * + * @param encapsulationMode one of {@link AudioTrack#ENCAPSULATION_MODE_NONE}, + * {@link AudioTrack#ENCAPSULATION_MODE_ELEMENTARY_STREAM}, + * {@link AudioTrack#ENCAPSULATION_MODE_HANDLE}. + * @return the same Builder instance. + */ + public @NonNull Builder setEncapsulationMode(@EncapsulationMode int encapsulationMode) { + switch (encapsulationMode) { + case ENCAPSULATION_MODE_NONE: + case ENCAPSULATION_MODE_ELEMENTARY_STREAM: + case ENCAPSULATION_MODE_HANDLE: + mEncapsulationMode = encapsulationMode; + break; + default: + throw new IllegalArgumentException( + "Invalid encapsulation mode " + encapsulationMode); + } + return this; + } + + /** * Sets the mode under which buffers of audio data are transferred from the * {@link AudioTrack} to the framework. * @param mode one of {@link AudioTrack#MODE_STREAM}, {@link AudioTrack#MODE_STATIC}. @@ -949,6 +1108,25 @@ public class AudioTrack extends PlayerBase } /** + * Sets the tuner configuration for the {@code AudioTrack}. + * + * The {@link AudioTrack.TunerConfiguration} consists of parameters obtained from + * the Android TV tuner API which indicate the audio content stream id and the + * synchronization id for the {@code AudioTrack}. + * + * @param tunerConfiguration obtained by {@link AudioTrack.TunerConfiguration.Builder}. + * @return the same Builder instance. + */ + public @NonNull Builder setTunerConfiguration( + @NonNull TunerConfiguration tunerConfiguration) { + if (tunerConfiguration == null) { + throw new IllegalArgumentException("tunerConfiguration is null"); + } + mTunerConfiguration = tunerConfiguration; + return this; + } + + /** * Builds an {@link AudioTrack} instance initialized with all the parameters set * on this <code>Builder</code>. * @return a new successfully initialized {@link AudioTrack} instance. @@ -1003,6 +1181,8 @@ public class AudioTrack extends PlayerBase } } + // TODO: Check mEncapsulationMode compatibility with MODE_STATIC, etc? + try { // If the buffer size is not specified in streaming mode, // use a single frame for the buffer size and let the @@ -1012,7 +1192,8 @@ public class AudioTrack extends PlayerBase * mFormat.getBytesPerSample(mFormat.getEncoding()); } final AudioTrack track = new AudioTrack( - mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId, mOffload); + mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId, mOffload, + mEncapsulationMode, mTunerConfiguration); if (track.getState() == STATE_UNINITIALIZED) { // release is not necessary throw new UnsupportedOperationException("Cannot create AudioTrack"); @@ -3595,7 +3776,7 @@ public class AudioTrack extends PlayerBase Object /*AudioAttributes*/ attributes, int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat, int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack, - boolean offload); + boolean offload, int encapsulationMode, Object tunerConfiguration); private native final void native_finalize(); |