Merge "Add AudioRecord timestamps"
This commit is contained in:
@@ -19874,6 +19874,7 @@ package android.media {
|
||||
method public android.media.AudioDeviceInfo getRoutedDevice();
|
||||
method public int getSampleRate();
|
||||
method public int getState();
|
||||
method public int getTimestamp(android.media.AudioTimestamp, int);
|
||||
method public int read(byte[], int, int);
|
||||
method public int read(byte[], int, int, int);
|
||||
method public int read(short[], int, int);
|
||||
@@ -19945,6 +19946,8 @@ package android.media {
|
||||
|
||||
public final class AudioTimestamp {
|
||||
ctor public AudioTimestamp();
|
||||
field public static final int TIMEBASE_BOOTTIME = 1; // 0x1
|
||||
field public static final int TIMEBASE_MONOTONIC = 0; // 0x0
|
||||
field public long framePosition;
|
||||
field public long nanoTime;
|
||||
}
|
||||
|
||||
@@ -21140,6 +21140,7 @@ package android.media {
|
||||
method public android.media.AudioDeviceInfo getRoutedDevice();
|
||||
method public int getSampleRate();
|
||||
method public int getState();
|
||||
method public int getTimestamp(android.media.AudioTimestamp, int);
|
||||
method public int read(byte[], int, int);
|
||||
method public int read(byte[], int, int, int);
|
||||
method public int read(short[], int, int);
|
||||
@@ -21213,6 +21214,8 @@ package android.media {
|
||||
|
||||
public final class AudioTimestamp {
|
||||
ctor public AudioTimestamp();
|
||||
field public static final int TIMEBASE_BOOTTIME = 1; // 0x1
|
||||
field public static final int TIMEBASE_MONOTONIC = 0; // 0x0
|
||||
field public long framePosition;
|
||||
field public long nanoTime;
|
||||
}
|
||||
|
||||
@@ -19883,6 +19883,7 @@ package android.media {
|
||||
method public android.media.AudioDeviceInfo getRoutedDevice();
|
||||
method public int getSampleRate();
|
||||
method public int getState();
|
||||
method public int getTimestamp(android.media.AudioTimestamp, int);
|
||||
method public int read(byte[], int, int);
|
||||
method public int read(byte[], int, int, int);
|
||||
method public int read(short[], int, int);
|
||||
@@ -19954,6 +19955,8 @@ package android.media {
|
||||
|
||||
public final class AudioTimestamp {
|
||||
ctor public AudioTimestamp();
|
||||
field public static final int TIMEBASE_BOOTTIME = 1; // 0x1
|
||||
field public static final int TIMEBASE_MONOTONIC = 0; // 0x0
|
||||
field public long framePosition;
|
||||
field public long nanoTime;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,10 @@ struct audio_attributes_fields_t {
|
||||
};
|
||||
static audio_attributes_fields_t javaAudioAttrFields;
|
||||
static audio_record_fields_t javaAudioRecordFields;
|
||||
static struct {
|
||||
jfieldID fieldFramePosition; // AudioTimestamp.framePosition
|
||||
jfieldID fieldNanoTime; // AudioTimestamp.nanoTime
|
||||
} javaAudioTimestampFields;
|
||||
|
||||
struct audiorecord_callback_cookie {
|
||||
jclass audioRecord_class;
|
||||
@@ -678,7 +682,40 @@ static void android_media_AudioRecord_disableDeviceCallback(
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
static jint android_media_AudioRecord_get_timestamp(JNIEnv *env, jobject thiz,
|
||||
jobject timestamp, jint timebase) {
|
||||
sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
|
||||
|
||||
if (lpRecorder == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException",
|
||||
"Unable to retrieve AudioRecord pointer for getTimestamp()");
|
||||
return (jint)AUDIO_JAVA_ERROR;
|
||||
}
|
||||
|
||||
// TODO Enable.
|
||||
#if 0
|
||||
// get the record timestamp
|
||||
ExtendedTimestamp ts;
|
||||
jint status = nativeToJavaStatus(lpRecorder->getExtendedTimestamp(&ts));
|
||||
|
||||
if (status == AUDIO_JAVA_SUCCESS) {
|
||||
// set the data
|
||||
int64_t position, time;
|
||||
|
||||
status = nativeToJavaStatus(ts.getBestTimestamp(&position, &time, timebase));
|
||||
if (status == AUDIO_JAVA_SUCCESS) {
|
||||
env->SetLongField(
|
||||
timestamp, javaAudioTimestampFields.fieldFramePosition, position);
|
||||
env->SetLongField(
|
||||
timestamp, javaAudioTimestampFields.fieldNanoTime, time);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
#else
|
||||
return (jint)AUDIO_JAVA_INVALID_OPERATION;
|
||||
#endif
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -716,6 +753,8 @@ static const JNINativeMethod gMethods[] = {
|
||||
{"native_enableDeviceCallback", "()V", (void *)android_media_AudioRecord_enableDeviceCallback},
|
||||
{"native_disableDeviceCallback", "()V",
|
||||
(void *)android_media_AudioRecord_disableDeviceCallback},
|
||||
{"native_get_timestamp", "(Landroid/media/AudioTimestamp;I)I",
|
||||
(void *)android_media_AudioRecord_get_timestamp},
|
||||
};
|
||||
|
||||
// field names found in android/media/AudioRecord.java
|
||||
@@ -758,6 +797,13 @@ int register_android_media_AudioRecord(JNIEnv *env)
|
||||
javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env,
|
||||
audioAttrClass, "mFormattedTags", "Ljava/lang/String;");
|
||||
|
||||
// Get the RecordTimestamp class and fields
|
||||
jclass audioTimestampClass = FindClassOrDie(env, "android/media/AudioTimestamp");
|
||||
javaAudioTimestampFields.fieldFramePosition =
|
||||
GetFieldIDOrDie(env, audioTimestampClass, "framePosition", "J");
|
||||
javaAudioTimestampFields.fieldNanoTime =
|
||||
GetFieldIDOrDie(env, audioTimestampClass, "nanoTime", "J");
|
||||
|
||||
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
|
||||
}
|
||||
|
||||
|
||||
@@ -825,6 +825,33 @@ public class AudioRecord implements AudioRouting
|
||||
return native_get_pos_update_period();
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll for an {@link AudioTimestamp} on demand.
|
||||
* <p>
|
||||
* The AudioTimestamp reflects the frame delivery information at
|
||||
* the earliest point available in the capture pipeline.
|
||||
* <p>
|
||||
* Calling {@link #startRecording()} following a {@link #stop()} will reset
|
||||
* the frame count to 0.
|
||||
*
|
||||
* @param timestamp a reference to a non-null AudioTimestamp instance.
|
||||
* @param timebase one of
|
||||
* {@link AudioTimestamp#TIMEBASE_BOOTTIME AudioTimestamp.TIMEBASE_BOOTTIME} or
|
||||
* {@link AudioTimestamp#TIMEBASE_MONOTONIC AudioTimestamp.TIMEBASE_MONOTONIC}.
|
||||
* @return {@link #SUCCESS} if a timestamp is available,
|
||||
* or {@link #ERROR_INVALID_OPERATION} if a timestamp not available.
|
||||
*/
|
||||
public int getTimestamp(@NonNull AudioTimestamp timestamp,
|
||||
@AudioTimestamp.Timebase int timebase)
|
||||
{
|
||||
if (timestamp == null ||
|
||||
(timebase != AudioTimestamp.TIMEBASE_BOOTTIME
|
||||
&& timebase != AudioTimestamp.TIMEBASE_MONOTONIC)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return native_get_timestamp(timestamp, timebase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum buffer size required for the successful creation of an AudioRecord
|
||||
* object, in byte units.
|
||||
@@ -1709,6 +1736,9 @@ public class AudioRecord implements AudioRouting
|
||||
private native final void native_enableDeviceCallback();
|
||||
private native final void native_disableDeviceCallback();
|
||||
|
||||
private native final int native_get_timestamp(@NonNull AudioTimestamp timestamp,
|
||||
@AudioTimestamp.Timebase int timebase);
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Utility methods
|
||||
//------------------
|
||||
|
||||
@@ -16,29 +16,74 @@
|
||||
|
||||
package android.media;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
|
||||
/**
|
||||
* Structure that groups a position in frame units relative to an assumed audio stream,
|
||||
* together with the estimated time when that frame was presented or is committed to be
|
||||
* presented.
|
||||
* In the case of audio output, "present" means that audio produced on device
|
||||
* is detectable by an external observer off device.
|
||||
* together with the estimated time when that frame enters or leaves the audio
|
||||
* processing pipeline on that device. This can be used to coordinate events
|
||||
* and interactions with the external environment.
|
||||
* <p>
|
||||
* The time is based on the implementation's best effort, using whatever knowledge
|
||||
* is available to the system, but cannot account for any delay unknown to the implementation.
|
||||
*
|
||||
* @see AudioTrack#getTimestamp
|
||||
* @see AudioTrack#getTimestamp AudioTrack.getTimestamp(AudioTimestamp)
|
||||
* @see AudioRecord#getTimestamp AudioRecord.getTimestamp(AudioTimestamp, int)
|
||||
*/
|
||||
public final class AudioTimestamp
|
||||
{
|
||||
/**
|
||||
* Clock monotonic or its equivalent on the system,
|
||||
* in the same units and timebase as {@link java.lang.System#nanoTime}.
|
||||
*/
|
||||
public static final int TIMEBASE_MONOTONIC = 0;
|
||||
|
||||
/**
|
||||
* Clock monotonic including suspend time or its equivalent on the system,
|
||||
* in the same units and timebase as {@link android.os.SystemClock#elapsedRealtimeNanos}.
|
||||
*/
|
||||
public static final int TIMEBASE_BOOTTIME = 1;
|
||||
|
||||
/** @hide */
|
||||
@IntDef({
|
||||
TIMEBASE_MONOTONIC,
|
||||
TIMEBASE_BOOTTIME,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Timebase {}
|
||||
|
||||
/**
|
||||
* Position in frames relative to start of an assumed audio stream.
|
||||
* The low-order 32 bits of position is in wrapping frame units similar to
|
||||
* {@link AudioTrack#getPlaybackHeadPosition}.
|
||||
* <p>
|
||||
* When obtained through
|
||||
* {@link AudioRecord#getTimestamp AudioRecord.getTimestamp(AudioTimestamp, int)},
|
||||
* all 64 bits of position are valid.
|
||||
* <p>
|
||||
* When obtained through
|
||||
* {@link AudioTrack#getTimestamp AudioTrack.getTimestamp(AudioTimestamp)},
|
||||
* the low-order 32 bits of position is in wrapping frame units similar to
|
||||
* {@link AudioTrack#getPlaybackHeadPosition AudioTrack.getPlaybackHeadPosition()}.
|
||||
*/
|
||||
public long framePosition;
|
||||
|
||||
/**
|
||||
* The estimated time when frame was presented or is committed to be presented,
|
||||
* in the same units and timebase as {@link java.lang.System#nanoTime}.
|
||||
* Time associated with the frame in the audio pipeline.
|
||||
* <p>
|
||||
* When obtained through
|
||||
* {@link AudioRecord#getTimestamp AudioRecord.getTimestamp(AudioTimestamp, int)},
|
||||
* this is the estimated time in nanoseconds when the frame referred to by
|
||||
* {@link #framePosition} was captured. The timebase is either
|
||||
* {@link #TIMEBASE_MONOTONIC} or {@link #TIMEBASE_BOOTTIME}, depending
|
||||
* on the timebase parameter used in
|
||||
* {@link AudioRecord#getTimestamp AudioRecord.getTimestamp(AudioTimestamp, int)}.
|
||||
* <p>
|
||||
* When obtained through
|
||||
* {@link AudioTrack#getTimestamp AudioTrack.getTimestamp(AudioTimestamp)},
|
||||
* this is the estimated time when the frame was presented or is committed to be presented,
|
||||
* with a timebase of {@link #TIMEBASE_MONOTONIC}.
|
||||
*/
|
||||
public long nanoTime;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user