Loading api/current.txt +6 −1 Original line number Diff line number Diff line Loading @@ -23256,16 +23256,20 @@ package android.media { field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1 } public class MediaRecorder { public class MediaRecorder implements android.media.AudioRouting { ctor public MediaRecorder(); method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler); method protected void finalize(); method public static final int getAudioSourceMax(); method public int getMaxAmplitude() throws java.lang.IllegalStateException; method public android.os.PersistableBundle getMetrics(); method public android.media.AudioDeviceInfo getPreferredDevice(); method public android.media.AudioDeviceInfo getRoutedDevice(); method public android.view.Surface getSurface(); method public void pause() throws java.lang.IllegalStateException; method public void prepare() throws java.io.IOException, java.lang.IllegalStateException; method public void release(); method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener); method public void reset(); method public void resume() throws java.lang.IllegalStateException; method public void setAudioChannels(int); Loading @@ -23288,6 +23292,7 @@ package android.media { method public void setOutputFile(java.io.File); method public void setOutputFile(java.lang.String) throws java.lang.IllegalStateException; method public void setOutputFormat(int) throws java.lang.IllegalStateException; method public boolean setPreferredDevice(android.media.AudioDeviceInfo); method public void setPreviewDisplay(android.view.Surface); method public void setProfile(android.media.CamcorderProfile); method public void setVideoEncoder(int) throws java.lang.IllegalStateException; media/java/android/media/MediaRecorder.java +164 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.PersistableBundle; import android.util.ArrayMap; import android.util.Log; import android.view.Surface; Loading @@ -34,6 +35,8 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.lang.ref.WeakReference; import com.android.internal.annotations.GuardedBy; /** * Used to record audio and video. The recording control is based on a * simple state machine (see below). Loading Loading @@ -76,7 +79,7 @@ import java.lang.ref.WeakReference; * <a href="{@docRoot}guide/topics/media/audio-capture.html">Audio Capture</a> developer guide.</p> * </div> */ public class MediaRecorder public class MediaRecorder implements AudioRouting { static { System.loadLibrary("media_jni"); Loading Loading @@ -1243,6 +1246,7 @@ public class MediaRecorder private static final int MEDIA_RECORDER_TRACK_EVENT_INFO = 101; private static final int MEDIA_RECORDER_TRACK_EVENT_LIST_END = 1000; private static final int MEDIA_RECORDER_AUDIO_ROUTING_CHANGED = 10000; @Override public void handleMessage(Message msg) { Loading @@ -1265,6 +1269,16 @@ public class MediaRecorder return; case MEDIA_RECORDER_AUDIO_ROUTING_CHANGED: AudioManager.resetAudioPortGeneration(); synchronized (mRoutingChangeListeners) { for (NativeRoutingEventHandlerDelegate delegate : mRoutingChangeListeners.values()) { delegate.notifyClient(); } } return; default: Log.e(TAG, "Unknown message type " + msg.what); return; Loading @@ -1272,6 +1286,155 @@ public class MediaRecorder } } //-------------------------------------------------------------------------- // Explicit Routing //-------------------- private AudioDeviceInfo mPreferredDevice = null; /** * Specifies an audio device (via an {@link AudioDeviceInfo} object) to route * the input from this MediaRecorder. * @param deviceInfo The {@link AudioDeviceInfo} specifying the audio source. * If deviceInfo is null, default routing is restored. * @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and * does not correspond to a valid audio input device. */ @Override public boolean setPreferredDevice(AudioDeviceInfo deviceInfo) { if (deviceInfo != null && !deviceInfo.isSource()) { return false; } int preferredDeviceId = deviceInfo != null ? deviceInfo.getId() : 0; boolean status = native_setInputDevice(preferredDeviceId); if (status == true) { synchronized (this) { mPreferredDevice = deviceInfo; } } return status; } /** * Returns the selected input device specified by {@link #setPreferredDevice}. Note that this * is not guaranteed to correspond to the actual device being used for recording. */ @Override public AudioDeviceInfo getPreferredDevice() { synchronized (this) { return mPreferredDevice; } } /** * Returns an {@link AudioDeviceInfo} identifying the current routing of this MediaRecorder * Note: The query is only valid if the MediaRecorder is currently recording. * If the recorder is not recording, the returned device can be null or correspond to previously * selected device when the recorder was last active. */ @Override public AudioDeviceInfo getRoutedDevice() { int deviceId = native_getRoutedDeviceId(); if (deviceId == 0) { return null; } AudioDeviceInfo[] devices = AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_INPUTS); for (int i = 0; i < devices.length; i++) { if (devices[i].getId() == deviceId) { return devices[i]; } } return null; } /* * Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler. */ private void enableNativeRoutingCallbacksLocked(boolean enabled) { if (mRoutingChangeListeners.size() == 0) { native_enableDeviceCallback(enabled); } } /** * The list of AudioRouting.OnRoutingChangedListener interfaces added (with * {@link #addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, Handler)} * by an app to receive (re)routing notifications. */ @GuardedBy("mRoutingChangeListeners") private ArrayMap<AudioRouting.OnRoutingChangedListener, NativeRoutingEventHandlerDelegate> mRoutingChangeListeners = new ArrayMap<>(); /** * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing * changes on this MediaRecorder. * @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive * notifications of rerouting events. * @param handler Specifies the {@link Handler} object for the thread on which to execute * the callback. If <code>null</code>, the handler on the main looper will be used. */ @Override public void addOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener, Handler handler) { synchronized (mRoutingChangeListeners) { if (listener != null && !mRoutingChangeListeners.containsKey(listener)) { enableNativeRoutingCallbacksLocked(true); mRoutingChangeListeners.put( listener, new NativeRoutingEventHandlerDelegate(this, listener, handler)); } } } /** * Removes an {@link AudioRouting.OnRoutingChangedListener} which has been previously added * to receive rerouting notifications. * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface * to remove. */ @Override public void removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener) { synchronized (mRoutingChangeListeners) { if (mRoutingChangeListeners.containsKey(listener)) { mRoutingChangeListeners.remove(listener); enableNativeRoutingCallbacksLocked(false); } } } /** * Helper class to handle the forwarding of native events to the appropriate listener * (potentially) handled in a different thread */ private class NativeRoutingEventHandlerDelegate { private MediaRecorder mMediaRecorder; private AudioRouting.OnRoutingChangedListener mOnRoutingChangedListener; private Handler mHandler; NativeRoutingEventHandlerDelegate(final MediaRecorder mediaRecorder, final AudioRouting.OnRoutingChangedListener listener, Handler handler) { mMediaRecorder = mediaRecorder; mOnRoutingChangedListener = listener; mHandler = handler != null ? handler : mEventHandler; } void notifyClient() { if (mHandler != null) { mHandler.post(new Runnable() { @Override public void run() { if (mOnRoutingChangedListener != null) { mOnRoutingChangedListener.onRoutingChanged(mMediaRecorder); } } }); } } } private native final boolean native_setInputDevice(int deviceId); private native final int native_getRoutedDeviceId(); private native final void native_enableDeviceCallback(boolean enabled); /** * Called from native code when an interesting event happens. This method * just uses the EventHandler system to post the event back to the main app thread. Loading media/jni/android_media_MediaRecorder.cpp +54 −0 Original line number Diff line number Diff line Loading @@ -657,6 +657,56 @@ android_media_MediaRecorder_native_getMetrics(JNIEnv *env, jobject thiz) return mybundle; } static jboolean android_media_MediaRecorder_setInputDevice(JNIEnv *env, jobject thiz, jint device_id) { ALOGV("android_media_MediaRecorder_setInputDevice"); sp<MediaRecorder> mr = getMediaRecorder(env, thiz); if (mr == NULL) { jniThrowException(env, "java/lang/IllegalStateException", NULL); return false; } if (process_media_recorder_call(env, mr->setInputDevice(device_id), "java/lang/RuntimeException", "setInputDevice failed.")) { return false; } return true; } static jint android_media_MediaRecorder_getRoutedDeviceId(JNIEnv *env, jobject thiz) { ALOGV("android_media_MediaRecorder_getRoutedDeviceId"); sp<MediaRecorder> mr = getMediaRecorder(env, thiz); if (mr == NULL) { jniThrowException(env, "java/lang/IllegalStateException", NULL); return AUDIO_PORT_HANDLE_NONE; } audio_port_handle_t deviceId; process_media_recorder_call(env, mr->getRoutedDeviceId(&deviceId), "java/lang/RuntimeException", "getRoutedDeviceId failed."); return (jint) deviceId; } static void android_media_MediaRecorder_enableDeviceCallback(JNIEnv *env, jobject thiz, jboolean enabled) { ALOGV("android_media_MediaRecorder_enableDeviceCallback %d", enabled); sp<MediaRecorder> mr = getMediaRecorder(env, thiz); if (mr == NULL) { jniThrowException(env, "java/lang/IllegalStateException", NULL); return; } process_media_recorder_call(env, mr->enableAudioDeviceCallback(enabled), "java/lang/RuntimeException", "enableDeviceCallback failed."); } // ---------------------------------------------------------------------------- static const JNINativeMethod gMethods[] = { Loading Loading @@ -689,6 +739,10 @@ static const JNINativeMethod gMethods[] = { {"native_setInputSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaRecorder_setInputSurface }, {"native_getMetrics", "()Landroid/os/PersistableBundle;", (void *)android_media_MediaRecorder_native_getMetrics}, {"native_setInputDevice", "(I)Z", (void *)android_media_MediaRecorder_setInputDevice}, {"native_getRoutedDeviceId", "()I", (void *)android_media_MediaRecorder_getRoutedDeviceId}, {"native_enableDeviceCallback", "(Z)V", (void *)android_media_MediaRecorder_enableDeviceCallback}, }; // This function only registers the native methods, and is called from Loading Loading
api/current.txt +6 −1 Original line number Diff line number Diff line Loading @@ -23256,16 +23256,20 @@ package android.media { field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1 } public class MediaRecorder { public class MediaRecorder implements android.media.AudioRouting { ctor public MediaRecorder(); method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler); method protected void finalize(); method public static final int getAudioSourceMax(); method public int getMaxAmplitude() throws java.lang.IllegalStateException; method public android.os.PersistableBundle getMetrics(); method public android.media.AudioDeviceInfo getPreferredDevice(); method public android.media.AudioDeviceInfo getRoutedDevice(); method public android.view.Surface getSurface(); method public void pause() throws java.lang.IllegalStateException; method public void prepare() throws java.io.IOException, java.lang.IllegalStateException; method public void release(); method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener); method public void reset(); method public void resume() throws java.lang.IllegalStateException; method public void setAudioChannels(int); Loading @@ -23288,6 +23292,7 @@ package android.media { method public void setOutputFile(java.io.File); method public void setOutputFile(java.lang.String) throws java.lang.IllegalStateException; method public void setOutputFormat(int) throws java.lang.IllegalStateException; method public boolean setPreferredDevice(android.media.AudioDeviceInfo); method public void setPreviewDisplay(android.view.Surface); method public void setProfile(android.media.CamcorderProfile); method public void setVideoEncoder(int) throws java.lang.IllegalStateException;
media/java/android/media/MediaRecorder.java +164 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.PersistableBundle; import android.util.ArrayMap; import android.util.Log; import android.view.Surface; Loading @@ -34,6 +35,8 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.lang.ref.WeakReference; import com.android.internal.annotations.GuardedBy; /** * Used to record audio and video. The recording control is based on a * simple state machine (see below). Loading Loading @@ -76,7 +79,7 @@ import java.lang.ref.WeakReference; * <a href="{@docRoot}guide/topics/media/audio-capture.html">Audio Capture</a> developer guide.</p> * </div> */ public class MediaRecorder public class MediaRecorder implements AudioRouting { static { System.loadLibrary("media_jni"); Loading Loading @@ -1243,6 +1246,7 @@ public class MediaRecorder private static final int MEDIA_RECORDER_TRACK_EVENT_INFO = 101; private static final int MEDIA_RECORDER_TRACK_EVENT_LIST_END = 1000; private static final int MEDIA_RECORDER_AUDIO_ROUTING_CHANGED = 10000; @Override public void handleMessage(Message msg) { Loading @@ -1265,6 +1269,16 @@ public class MediaRecorder return; case MEDIA_RECORDER_AUDIO_ROUTING_CHANGED: AudioManager.resetAudioPortGeneration(); synchronized (mRoutingChangeListeners) { for (NativeRoutingEventHandlerDelegate delegate : mRoutingChangeListeners.values()) { delegate.notifyClient(); } } return; default: Log.e(TAG, "Unknown message type " + msg.what); return; Loading @@ -1272,6 +1286,155 @@ public class MediaRecorder } } //-------------------------------------------------------------------------- // Explicit Routing //-------------------- private AudioDeviceInfo mPreferredDevice = null; /** * Specifies an audio device (via an {@link AudioDeviceInfo} object) to route * the input from this MediaRecorder. * @param deviceInfo The {@link AudioDeviceInfo} specifying the audio source. * If deviceInfo is null, default routing is restored. * @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and * does not correspond to a valid audio input device. */ @Override public boolean setPreferredDevice(AudioDeviceInfo deviceInfo) { if (deviceInfo != null && !deviceInfo.isSource()) { return false; } int preferredDeviceId = deviceInfo != null ? deviceInfo.getId() : 0; boolean status = native_setInputDevice(preferredDeviceId); if (status == true) { synchronized (this) { mPreferredDevice = deviceInfo; } } return status; } /** * Returns the selected input device specified by {@link #setPreferredDevice}. Note that this * is not guaranteed to correspond to the actual device being used for recording. */ @Override public AudioDeviceInfo getPreferredDevice() { synchronized (this) { return mPreferredDevice; } } /** * Returns an {@link AudioDeviceInfo} identifying the current routing of this MediaRecorder * Note: The query is only valid if the MediaRecorder is currently recording. * If the recorder is not recording, the returned device can be null or correspond to previously * selected device when the recorder was last active. */ @Override public AudioDeviceInfo getRoutedDevice() { int deviceId = native_getRoutedDeviceId(); if (deviceId == 0) { return null; } AudioDeviceInfo[] devices = AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_INPUTS); for (int i = 0; i < devices.length; i++) { if (devices[i].getId() == deviceId) { return devices[i]; } } return null; } /* * Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler. */ private void enableNativeRoutingCallbacksLocked(boolean enabled) { if (mRoutingChangeListeners.size() == 0) { native_enableDeviceCallback(enabled); } } /** * The list of AudioRouting.OnRoutingChangedListener interfaces added (with * {@link #addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, Handler)} * by an app to receive (re)routing notifications. */ @GuardedBy("mRoutingChangeListeners") private ArrayMap<AudioRouting.OnRoutingChangedListener, NativeRoutingEventHandlerDelegate> mRoutingChangeListeners = new ArrayMap<>(); /** * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing * changes on this MediaRecorder. * @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive * notifications of rerouting events. * @param handler Specifies the {@link Handler} object for the thread on which to execute * the callback. If <code>null</code>, the handler on the main looper will be used. */ @Override public void addOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener, Handler handler) { synchronized (mRoutingChangeListeners) { if (listener != null && !mRoutingChangeListeners.containsKey(listener)) { enableNativeRoutingCallbacksLocked(true); mRoutingChangeListeners.put( listener, new NativeRoutingEventHandlerDelegate(this, listener, handler)); } } } /** * Removes an {@link AudioRouting.OnRoutingChangedListener} which has been previously added * to receive rerouting notifications. * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface * to remove. */ @Override public void removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener) { synchronized (mRoutingChangeListeners) { if (mRoutingChangeListeners.containsKey(listener)) { mRoutingChangeListeners.remove(listener); enableNativeRoutingCallbacksLocked(false); } } } /** * Helper class to handle the forwarding of native events to the appropriate listener * (potentially) handled in a different thread */ private class NativeRoutingEventHandlerDelegate { private MediaRecorder mMediaRecorder; private AudioRouting.OnRoutingChangedListener mOnRoutingChangedListener; private Handler mHandler; NativeRoutingEventHandlerDelegate(final MediaRecorder mediaRecorder, final AudioRouting.OnRoutingChangedListener listener, Handler handler) { mMediaRecorder = mediaRecorder; mOnRoutingChangedListener = listener; mHandler = handler != null ? handler : mEventHandler; } void notifyClient() { if (mHandler != null) { mHandler.post(new Runnable() { @Override public void run() { if (mOnRoutingChangedListener != null) { mOnRoutingChangedListener.onRoutingChanged(mMediaRecorder); } } }); } } } private native final boolean native_setInputDevice(int deviceId); private native final int native_getRoutedDeviceId(); private native final void native_enableDeviceCallback(boolean enabled); /** * Called from native code when an interesting event happens. This method * just uses the EventHandler system to post the event back to the main app thread. Loading
media/jni/android_media_MediaRecorder.cpp +54 −0 Original line number Diff line number Diff line Loading @@ -657,6 +657,56 @@ android_media_MediaRecorder_native_getMetrics(JNIEnv *env, jobject thiz) return mybundle; } static jboolean android_media_MediaRecorder_setInputDevice(JNIEnv *env, jobject thiz, jint device_id) { ALOGV("android_media_MediaRecorder_setInputDevice"); sp<MediaRecorder> mr = getMediaRecorder(env, thiz); if (mr == NULL) { jniThrowException(env, "java/lang/IllegalStateException", NULL); return false; } if (process_media_recorder_call(env, mr->setInputDevice(device_id), "java/lang/RuntimeException", "setInputDevice failed.")) { return false; } return true; } static jint android_media_MediaRecorder_getRoutedDeviceId(JNIEnv *env, jobject thiz) { ALOGV("android_media_MediaRecorder_getRoutedDeviceId"); sp<MediaRecorder> mr = getMediaRecorder(env, thiz); if (mr == NULL) { jniThrowException(env, "java/lang/IllegalStateException", NULL); return AUDIO_PORT_HANDLE_NONE; } audio_port_handle_t deviceId; process_media_recorder_call(env, mr->getRoutedDeviceId(&deviceId), "java/lang/RuntimeException", "getRoutedDeviceId failed."); return (jint) deviceId; } static void android_media_MediaRecorder_enableDeviceCallback(JNIEnv *env, jobject thiz, jboolean enabled) { ALOGV("android_media_MediaRecorder_enableDeviceCallback %d", enabled); sp<MediaRecorder> mr = getMediaRecorder(env, thiz); if (mr == NULL) { jniThrowException(env, "java/lang/IllegalStateException", NULL); return; } process_media_recorder_call(env, mr->enableAudioDeviceCallback(enabled), "java/lang/RuntimeException", "enableDeviceCallback failed."); } // ---------------------------------------------------------------------------- static const JNINativeMethod gMethods[] = { Loading Loading @@ -689,6 +739,10 @@ static const JNINativeMethod gMethods[] = { {"native_setInputSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaRecorder_setInputSurface }, {"native_getMetrics", "()Landroid/os/PersistableBundle;", (void *)android_media_MediaRecorder_native_getMetrics}, {"native_setInputDevice", "(I)Z", (void *)android_media_MediaRecorder_setInputDevice}, {"native_getRoutedDeviceId", "()I", (void *)android_media_MediaRecorder_getRoutedDeviceId}, {"native_enableDeviceCallback", "(Z)V", (void *)android_media_MediaRecorder_enableDeviceCallback}, }; // This function only registers the native methods, and is called from Loading