summaryrefslogtreecommitdiffstats
path: root/media/java/android/media/MediaSync.java
diff options
context:
space:
mode:
Diffstat (limited to 'media/java/android/media/MediaSync.java')
-rw-r--r--media/java/android/media/MediaSync.java287
1 files changed, 193 insertions, 94 deletions
diff --git a/media/java/android/media/MediaSync.java b/media/java/android/media/MediaSync.java
index 74a2fb2..c1f1a73 100644
--- a/media/java/android/media/MediaSync.java
+++ b/media/java/android/media/MediaSync.java
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.media.AudioTrack;
+import android.media.PlaybackSettings;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -38,13 +39,13 @@ import java.util.List;
* <p>MediaSync is generally used like this:
* <pre>
* MediaSync sync = new MediaSync();
- * sync.configureSurface(surface);
+ * sync.setSurface(surface);
* Surface inputSurface = sync.createInputSurface();
* ...
* // MediaCodec videoDecoder = ...;
* videoDecoder.configure(format, inputSurface, ...);
* ...
- * sync.configureAudioTrack(audioTrack);
+ * sync.setAudioTrack(audioTrack);
* sync.setCallback(new MediaSync.Callback() {
* {@literal @Override}
* public void onReturnAudioBuffer(MediaSync sync, ByteBuffer audioBuffer, int bufferIndex) {
@@ -94,8 +95,8 @@ import java.util.List;
*
* </pre>
*
- * The client needs to configure corresponding sink (i.e., Surface and AudioTrack) based on
- * the stream type it will play.
+ * The client needs to configure corresponding sink by setting the Surface and/or AudioTrack
+ * based on the stream type it will play.
* <p>
* For video, the client needs to call {@link #createInputSurface} to obtain a surface on
* which it will render video frames.
@@ -233,29 +234,33 @@ final public class MediaSync {
}
/**
- * Configures the output surface for MediaSync.
+ * Sets the output surface for MediaSync.
+ * <p>
+ * Currently, this is only supported in the Initialized state.
*
* @param surface Specify a surface on which to render the video data.
- * @throws IllegalArgumentException if the surface has been released, or is invalid.
+ * @throws IllegalArgumentException if the surface has been released, is invalid,
* or can not be connected.
- * @throws IllegalStateException if not in the Initialized state, or another surface
- * has already been configured.
+ * @throws IllegalStateException if setting the surface is not supported, e.g.
+ * not in the Initialized state, or another surface has already been configured.
*/
- public void configureSurface(@Nullable Surface surface) {
+ public void setSurface(@Nullable Surface surface) {
native_configureSurface(surface);
}
private native final void native_configureSurface(@Nullable Surface surface);
/**
- * Configures the audio track for MediaSync.
+ * Sets the audio track for MediaSync.
+ * <p>
+ * Currently, this is only supported in the Initialized state.
*
* @param audioTrack Specify an AudioTrack through which to render the audio data.
* @throws IllegalArgumentException if the audioTrack has been released, or is invalid.
- * @throws IllegalStateException if not in the Initialized state, or another audio track
- * has already been configured.
+ * @throws IllegalStateException if setting the audio track is not supported, e.g.
+ * not in the Initialized state, or another audio track has already been configured.
*/
- public void configureAudioTrack(@Nullable AudioTrack audioTrack) {
+ public void setAudioTrack(@Nullable AudioTrack audioTrack) {
// AudioTrack has sanity check for configured sample rate.
int nativeSampleRateInHz = (audioTrack == null ? 0 : audioTrack.getSampleRate());
@@ -271,7 +276,7 @@ final public class MediaSync {
/**
* Requests a Surface to use as the input. This may only be called after
- * {@link #configureSurface}.
+ * {@link #setSurface}.
* <p>
* The application is responsible for calling release() on the Surface when
* done.
@@ -282,141 +287,235 @@ final public class MediaSync {
public native final Surface createInputSurface();
/**
- * Specifies resampling as audio mode for variable rate playback, i.e.,
- * resample the waveform based on the requested playback rate to get
+ * Resample audio data when changing playback speed.
+ * <p>
+ * Resample the waveform based on the requested playback rate to get
* a new waveform, and play back the new waveform at the original sampling
* frequency.
- * When rate is larger than 1.0, pitch becomes higher.
- * When rate is smaller than 1.0, pitch becomes lower.
+ * <p><ul>
+ * <li>When rate is larger than 1.0, pitch becomes higher.
+ * <li>When rate is smaller than 1.0, pitch becomes lower.
+ * </ul>
*/
- public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 0;
+ public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2;
/**
- * Specifies time stretching as audio mode for variable rate playback.
+ * Time stretch audio when changing playback speed.
+ * <p>
* Time stretching changes the duration of the audio samples without
- * affecting its pitch.
- * FIXME: implement time strectching.
- * @hide
+ * affecting their pitch. This is only supported for a limited range
+ * of playback speeds, e.g. from 1/2x to 2x. If the rate is adjusted
+ * beyond this limit, the rate change will fail.
*/
public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1;
+ /**
+ * Time stretch audio when changing playback speed, and may mute if
+ * stretching is no longer supported.
+ * <p>
+ * Time stretching changes the duration of the audio samples without
+ * affecting their pitch. This is only supported for a limited range
+ * of playback speeds, e.g. from 1/2x to 2x. When it is no longer
+ * supported, the audio may be muted. Using this mode will not fail
+ * for non-negative playback rates.
+ */
+ public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0;
+
/** @hide */
@IntDef(
value = {
+ PLAYBACK_RATE_AUDIO_MODE_DEFAULT,
+ PLAYBACK_RATE_AUDIO_MODE_STRETCH,
PLAYBACK_RATE_AUDIO_MODE_RESAMPLE,
- PLAYBACK_RATE_AUDIO_MODE_STRETCH })
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface PlaybackRateAudioMode {}
/**
- * Sets playback rate. It does same as {@link #setPlaybackRate(float, int)},
- * except that it always uses {@link #PLAYBACK_RATE_AUDIO_MODE_STRETCH} for audioMode.
+ * Sets playback rate and audio mode.
*
* @param rate the ratio between desired playback rate and normal one. 1.0 means normal
- * playback speed. 0.0 means stop or pause. Value larger than 1.0 means faster playback,
- * while value between 0.0 and 1.0 for slower playback.
+ * playback speed. 0.0 means pause. Value larger than 1.0 means faster playback,
+ * while value between 0.0 and 1.0 for slower playback. <b>Note:</b> the normal rate
+ * does not change as a result of this call. To restore the original rate at any time,
+ * use 1.0.
+ * @param audioMode audio playback mode. Must be one of the supported
+ * audio modes.
*
* @throws IllegalStateException if the internal sync engine or the audio track has not
* been initialized.
- * TODO: unhide when PLAYBACK_RATE_AUDIO_MODE_STRETCH is supported.
- * @hide
+ * @throws IllegalArgumentException if audioMode is not supported.
*/
- public void setPlaybackRate(float rate) {
- setPlaybackRate(rate, PLAYBACK_RATE_AUDIO_MODE_STRETCH);
+ public void setPlaybackRate(float rate, @PlaybackRateAudioMode int audioMode) {
+ PlaybackSettings rateSettings = new PlaybackSettings();
+ rateSettings.allowDefaults();
+ switch (audioMode) {
+ case PLAYBACK_RATE_AUDIO_MODE_DEFAULT:
+ rateSettings.setSpeed(rate).setPitch(1.0f);
+ break;
+ case PLAYBACK_RATE_AUDIO_MODE_STRETCH:
+ rateSettings.setSpeed(rate).setPitch(1.0f)
+ .setAudioFallbackMode(rateSettings.AUDIO_FALLBACK_MODE_FAIL);
+ break;
+ case PLAYBACK_RATE_AUDIO_MODE_RESAMPLE:
+ rateSettings.setSpeed(rate).setPitch(rate);
+ break;
+ default:
+ {
+ final String msg = "Audio playback mode " + audioMode + " is not supported";
+ throw new IllegalArgumentException(msg);
+ }
+ }
+ setPlaybackSettings(rateSettings);
}
/**
- * Sets playback rate and audio mode.
+ * Sets playback rate using {@link PlaybackSettings}.
+ * <p>
+ * When using MediaSync with {@link AudioTrack}, set playback settings using this
+ * call instead of calling it directly on the track, so that the sync is aware of
+ * the settings change.
+ * <p>
+ * This call also works if there is no audio track.
*
- * <p> The supported audio modes are:
- * <ul>
- * <li> {@link #PLAYBACK_RATE_AUDIO_MODE_RESAMPLE}
- * </ul>
- *
- * @param rate the ratio between desired playback rate and normal one. 1.0 means normal
- * playback speed. 0.0 means stop or pause. Value larger than 1.0 means faster playback,
- * while value between 0.0 and 1.0 for slower playback.
- * @param audioMode audio playback mode. Must be one of the supported
- * audio modes.
+ * @param settings the playback settings to use. {@link PlaybackSettings#getSpeed
+ * Speed} is the ratio between desired playback rate and normal one. 1.0 means
+ * normal playback speed. 0.0 means pause. Value larger than 1.0 means faster playback,
+ * while value between 0.0 and 1.0 for slower playback. <b>Note:</b> the normal rate
+ * does not change as a result of this call. To restore the original rate at any time,
+ * use speed of 1.0.
*
* @throws IllegalStateException if the internal sync engine or the audio track has not
* been initialized.
- * @throws IllegalArgumentException if audioMode is not supported.
+ * @throws IllegalArgumentException if the settings are not supported.
*/
- public void setPlaybackRate(float rate, @PlaybackRateAudioMode int audioMode) {
- if (!isAudioPlaybackModeSupported(audioMode)) {
- final String msg = "Audio playback mode " + audioMode + " is not supported";
- throw new IllegalArgumentException(msg);
- }
-
- int status = AudioTrack.SUCCESS;
- if (mAudioTrack != null) {
- int nativeSampleRateInHz = mAudioTrack.getSampleRate();
- int playbackSampleRate = (int)(rate * nativeSampleRateInHz + 0.5);
- rate = playbackSampleRate / (float)nativeSampleRateInHz;
-
- try {
- if (rate == 0.0) {
- mAudioTrack.pause();
- } else {
- status = mAudioTrack.setPlaybackRate(playbackSampleRate);
- mAudioTrack.play();
+ public void setPlaybackSettings(@NonNull PlaybackSettings settings) {
+ float rate;
+ try {
+ rate = settings.getSpeed();
+
+ // rate is specified
+ if (mAudioTrack != null) {
+ try {
+ if (rate == 0.0) {
+ mAudioTrack.pause();
+ } else {
+ mAudioTrack.setPlaybackSettings(settings);
+ mAudioTrack.play();
+ }
+ } catch (IllegalStateException e) {
+ throw e;
}
- } catch (IllegalStateException e) {
- throw e;
}
- }
- if (status != AudioTrack.SUCCESS) {
- throw new IllegalArgumentException("Fail to set playback rate in audio track");
+ synchronized(mAudioLock) {
+ mPlaybackRate = rate;
+ }
+ if (mPlaybackRate != 0.0 && mAudioThread != null) {
+ postRenderAudio(0);
+ }
+ native_setPlaybackRate(mPlaybackRate);
+ } catch (IllegalStateException e) {
+ // rate is not specified; still, propagate settings to audio track
+ if (mAudioTrack != null) {
+ mAudioTrack.setPlaybackSettings(settings);
+ }
}
+ }
- synchronized(mAudioLock) {
- mPlaybackRate = rate;
- }
- if (mPlaybackRate != 0.0 && mAudioThread != null) {
- postRenderAudio(0);
+ /**
+ * Gets the playback rate using {@link PlaybackSettings}.
+ *
+ * @return the playback rate being used.
+ *
+ * @throws IllegalStateException if the internal sync engine or the audio track has not
+ * been initialized.
+ */
+ @NonNull
+ public PlaybackSettings getPlaybackSettings() {
+ if (mAudioTrack != null) {
+ return mAudioTrack.getPlaybackSettings();
+ } else {
+ PlaybackSettings settings = new PlaybackSettings();
+ settings.allowDefaults();
+ settings.setSpeed(mPlaybackRate);
+ return settings;
}
- native_setPlaybackRate(mPlaybackRate);
}
private native final void native_setPlaybackRate(float rate);
- /*
- * Test whether a given audio playback mode is supported.
- * TODO query supported AudioPlaybackMode from audio track.
+ /**
+ * 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.
*/
- private boolean isAudioPlaybackModeSupported(int mode) {
- return (mode == PLAYBACK_RATE_AUDIO_MODE_RESAMPLE);
+ 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>
- * The MediaTimestamp represents a clock ticking during media playback. It's represented
- * by an anchor frame ({@link MediaTimestamp#mediaTimeUs} and {@link MediaTimestamp#nanoTime})
- * and clock speed ({@link MediaTimestamp#clockRate}). For continous playback with
- * constant speed, its anchor frame doesn't change that often. Thereafter, it's recommended
- * to not call this method often.
+ * The MediaTimestamp represents how the media time correlates to the system time in
+ * a linear fashion. It contains the media time and system timestamp of an anchor frame
+ * ({@link MediaTimestamp#mediaTimeUs} and {@link MediaTimestamp#nanoTime})
+ * and the speed of the media clock ({@link MediaTimestamp#clockRate}).
+ * <p>
+ * During regular playback, the media time moves fairly constantly (though the
+ * anchor frame may be rebased to a current system time, the linear correlation stays
+ * steady). Therefore, this method does not need to be called often.
* <p>
* To help users to get current playback position, this method always returns the timestamp of
* just-rendered frame, i.e., {@link System#nanoTime} and its corresponding media time. They
* can be used as current playback position.
*
- * @param timestamp a reference to a non-null MediaTimestamp instance allocated
- * and owned by caller.
- * @return true if a timestamp is available, or false if no timestamp is available.
- * If a timestamp if available, the MediaTimestamp instance is filled in with
- * playback rate, together with the current media timestamp and the system nanoTime
- * corresponding to the measured media timestamp.
- * In the case that no timestamp is available, any supplied instance is left unaltered.
+ * @return a MediaTimestamp object if a timestamp is available, or {@code null} if no timestamp
+ * is available, e.g. because the media sync has not been initialized.
*/
- public boolean getTimestamp(@NonNull MediaTimestamp timestamp)
+ @Nullable
+ public MediaTimestamp getTimestamp()
{
- if (timestamp == null) {
- throw new IllegalArgumentException();
+ try {
+ // TODO: create the timestamp in native
+ MediaTimestamp timestamp = new MediaTimestamp();
+ if (native_getTimestamp(timestamp)) {
+ return timestamp;
+ } else {
+ return null;
+ }
+ } catch (IllegalStateException e) {
+ return null;
}
- return native_getTimestamp(timestamp);
}
private native final boolean native_getTimestamp(@NonNull MediaTimestamp timestamp);