Merge "Add AudioRecord timestamps"

This commit is contained in:
Andy Hung
2016-01-29 03:23:19 +00:00
committed by Android (Google) Code Review
6 changed files with 139 additions and 9 deletions

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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));
}

View File

@@ -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
//------------------

View File

@@ -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;
}