diff options
Diffstat (limited to 'media')
-rw-r--r-- | media/java/android/media/AudioRecord.java | 172 | ||||
-rw-r--r-- | media/java/android/media/AudioService.java | 218 | ||||
-rw-r--r-- | media/java/android/media/AudioTrack.java | 73 | ||||
-rw-r--r-- | media/java/android/media/MediaCodec.java | 18 | ||||
-rw-r--r-- | media/java/android/media/MediaCodecList.java | 2 | ||||
-rw-r--r-- | media/java/android/media/MediaFormat.java | 4 | ||||
-rw-r--r-- | media/java/android/media/MediaRouter.java | 16 | ||||
-rw-r--r-- | media/java/android/media/MediaScanner.java | 1 | ||||
-rw-r--r-- | media/jni/android_media_MediaCodec.cpp | 40 | ||||
-rw-r--r-- | media/jni/android_media_MediaCodec.h | 2 | ||||
-rw-r--r-- | media/jni/android_media_MediaCodecList.cpp | 22 |
11 files changed, 382 insertions, 186 deletions
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 81e8028..772cb4c 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -17,11 +17,8 @@ package android.media; import java.lang.ref.WeakReference; -import java.io.OutputStream; -import java.io.IOException; import java.lang.IllegalArgumentException; import java.lang.IllegalStateException; -import java.lang.Thread; import java.nio.ByteBuffer; import android.os.Handler; @@ -33,12 +30,12 @@ import android.util.Log; * The AudioRecord class manages the audio resources for Java applications * to record audio from the audio input hardware of the platform. This is * achieved by "pulling" (reading) the data from the AudioRecord object. The - * application is responsible for polling the AudioRecord object in time using one of + * application is responsible for polling the AudioRecord object in time using one of * the following three methods: {@link #read(byte[],int, int)}, {@link #read(short[], int, int)} - * or {@link #read(ByteBuffer, int)}. The choice of which method to use will be based + * or {@link #read(ByteBuffer, int)}. The choice of which method to use will be based * on the audio data storage format that is the most convenient for the user of AudioRecord. * <p>Upon creation, an AudioRecord object initializes its associated audio buffer that it will - * fill with the new audio data. The size of this buffer, specified during the construction, + * fill with the new audio data. The size of this buffer, specified during the construction, * determines how long an AudioRecord can record before "over-running" data that has not * been read yet. Data should be read from the audio hardware in chunks of sizes inferior to * the total recording buffer size. @@ -49,20 +46,20 @@ public class AudioRecord // Constants //-------------------- /** - * indicates AudioRecord state is not successfully initialized. + * indicates AudioRecord state is not successfully initialized. */ public static final int STATE_UNINITIALIZED = 0; /** - * indicates AudioRecord state is ready to be used + * indicates AudioRecord state is ready to be used */ public static final int STATE_INITIALIZED = 1; /** - * indicates AudioRecord recording state is not recording + * indicates AudioRecord recording state is not recording */ public static final int RECORDSTATE_STOPPED = 1; // matches SL_RECORDSTATE_STOPPED /** - * indicates AudioRecord recording state is recording + * indicates AudioRecord recording state is recording */ public static final int RECORDSTATE_RECORDING = 3;// matches SL_RECORDSTATE_RECORDING @@ -84,15 +81,15 @@ public class AudioRecord * Denotes a failure due to the improper use of a method. */ public static final int ERROR_INVALID_OPERATION = -3; - + private static final int AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT = -16; private static final int AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK = -17; private static final int AUDIORECORD_ERROR_SETUP_INVALIDFORMAT = -18; private static final int AUDIORECORD_ERROR_SETUP_INVALIDSOURCE = -19; private static final int AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED = -20; - + // Events: - // to keep in sync with frameworks/base/include/media/AudioRecord.h + // to keep in sync with frameworks/base/include/media/AudioRecord.h /** * Event id denotes when record head has reached a previously set marker. */ @@ -101,29 +98,29 @@ public class AudioRecord * Event id denotes when previously set update period has elapsed during recording. */ private static final int NATIVE_EVENT_NEW_POS = 3; - + private final static String TAG = "AudioRecord-Java"; //--------------------------------------------------------- // Used exclusively by native code //-------------------- - /** - * Accessed by native methods: provides access to C++ AudioRecord object + /** + * Accessed by native methods: provides access to C++ AudioRecord object */ @SuppressWarnings("unused") private int mNativeRecorderInJavaObj; - /** + /** * Accessed by native methods: provides access to the callback data. */ @SuppressWarnings("unused") private int mNativeCallbackCookie; - + //--------------------------------------------------------- // Member variables - //-------------------- + //-------------------- /** * The audio data sampling rate in Hz. */ @@ -200,26 +197,26 @@ public class AudioRecord * @param sampleRateInHz the sample rate expressed in Hertz. 44100Hz is currently the only * rate that is guaranteed to work on all devices, but other rates such as 22050, * 16000, and 11025 may work on some devices. - * @param channelConfig describes the configuration of the audio channels. + * @param channelConfig describes the configuration of the audio channels. * See {@link AudioFormat#CHANNEL_IN_MONO} and * {@link AudioFormat#CHANNEL_IN_STEREO}. {@link AudioFormat#CHANNEL_IN_MONO} is guaranteed * to work on all devices. - * @param audioFormat the format in which the audio data is represented. - * See {@link AudioFormat#ENCODING_PCM_16BIT} and + * @param audioFormat the format in which the audio data is represented. + * See {@link AudioFormat#ENCODING_PCM_16BIT} and * {@link AudioFormat#ENCODING_PCM_8BIT} * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is written - * to during the recording. New audio data can be read from this buffer in smaller chunks + * to during the recording. New audio data can be read from this buffer in smaller chunks * than this size. See {@link #getMinBufferSize(int, int, int)} to determine the minimum * required buffer size for the successful creation of an AudioRecord instance. Using values * smaller than getMinBufferSize() will result in an initialization failure. * @throws java.lang.IllegalArgumentException */ - public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, + public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes) - throws IllegalArgumentException { + throws IllegalArgumentException { mState = STATE_UNINITIALIZED; mRecordingState = RECORDSTATE_STOPPED; - + // remember which looper is associated with the AudioRecord instanciation if ((mInitializationLooper = Looper.myLooper()) == null) { mInitializationLooper = Looper.getMainLooper(); @@ -234,7 +231,7 @@ public class AudioRecord session[0] = 0; //TODO: update native initialization when information about hardware init failure // due to capture device already open is available. - int initResult = native_setup( new WeakReference<AudioRecord>(this), + int initResult = native_setup( new WeakReference<AudioRecord>(this), mRecordSource, mSampleRate, mChannels, mAudioFormat, mNativeBufferSizeInBytes, session); if (initResult != SUCCESS) { @@ -256,7 +253,7 @@ public class AudioRecord // mChannels is valid // mAudioFormat is valid // mSampleRate is valid - private void audioParamCheck(int audioSource, int sampleRateInHz, + private void audioParamCheck(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat) { //-------------- @@ -267,13 +264,13 @@ public class AudioRecord } else { mRecordSource = audioSource; } - + //-------------- // sample rate if ( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) ) { throw (new IllegalArgumentException(sampleRateInHz + "Hz is not a supported sample rate.")); - } else { + } else { mSampleRate = sampleRateInHz; } @@ -312,7 +309,7 @@ public class AudioRecord break; default: mAudioFormat = AudioFormat.ENCODING_INVALID; - throw (new IllegalArgumentException("Unsupported sample encoding." + throw (new IllegalArgumentException("Unsupported sample encoding." + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT.")); } } @@ -325,16 +322,16 @@ public class AudioRecord // postcondition: // mNativeBufferSizeInBytes is valid (multiple of frame size, positive) private void audioBuffSizeCheck(int audioBufferSize) { - // NB: this section is only valid with PCM data. + // NB: this section is only valid with PCM data. // To update when supporting compressed formats - int frameSizeInBytes = mChannelCount + int frameSizeInBytes = mChannelCount * (mAudioFormat == AudioFormat.ENCODING_PCM_8BIT ? 1 : 2); if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) { throw (new IllegalArgumentException("Invalid audio buffer size.")); } mNativeBufferSizeInBytes = audioBufferSize; - } + } @@ -346,7 +343,7 @@ public class AudioRecord public void release() { try { stop(); - } catch(IllegalStateException ise) { + } catch(IllegalStateException ise) { // don't raise an exception, we're releasing the resources. } native_release(); @@ -357,7 +354,7 @@ public class AudioRecord @Override protected void finalize() { native_finalize(); - } + } //-------------------------------------------------------------------------- @@ -369,9 +366,9 @@ public class AudioRecord public int getSampleRate() { return mSampleRate; } - + /** - * Returns the audio recording source. + * Returns the audio recording source. * @see MediaRecorder.AudioSource */ public int getAudioSource() { @@ -387,7 +384,7 @@ public class AudioRecord } /** - * Returns the configured channel configuration. + * Returns the configured channel configuration. * See {@link AudioFormat#CHANNEL_IN_MONO} * and {@link AudioFormat#CHANNEL_IN_STEREO}. */ @@ -404,7 +401,7 @@ public class AudioRecord /** * Returns the state of the AudioRecord instance. This is useful after the - * AudioRecord instance has been created to check if it was initialized + * AudioRecord instance has been created to check if it was initialized * properly. This ensures that the appropriate hardware resources have been * acquired. * @see AudioRecord#STATE_INITIALIZED @@ -444,22 +441,22 @@ public class AudioRecord * should be chosen according to the expected frequency at which the AudioRecord instance * will be polled for new data. * @param sampleRateInHz the sample rate expressed in Hertz. - * @param channelConfig describes the configuration of the audio channels. + * @param channelConfig describes the configuration of the audio channels. * See {@link AudioFormat#CHANNEL_IN_MONO} and * {@link AudioFormat#CHANNEL_IN_STEREO} - * @param audioFormat the format in which the audio data is represented. + * @param audioFormat the format in which the audio data is represented. * See {@link AudioFormat#ENCODING_PCM_16BIT}. - * @return {@link #ERROR_BAD_VALUE} if the recording parameters are not supported by the + * @return {@link #ERROR_BAD_VALUE} if the recording parameters are not supported by the * hardware, or an invalid parameter was passed, - * or {@link #ERROR} if the implementation was unable to query the hardware for its - * output properties, + * or {@link #ERROR} if the implementation was unable to query the hardware for its + * output properties, * or the minimum buffer size expressed in bytes. * @see #AudioRecord(int, int, int, int, int) for more information on valid * configuration values. */ static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) { int channelCount = 0; - switch(channelConfig) { + switch (channelConfig) { case AudioFormat.CHANNEL_IN_DEFAULT: // AudioFormat.CHANNEL_CONFIGURATION_DEFAULT case AudioFormat.CHANNEL_IN_MONO: case AudioFormat.CHANNEL_CONFIGURATION_MONO: @@ -474,17 +471,17 @@ public class AudioRecord loge("getMinBufferSize(): Invalid channel configuration."); return AudioRecord.ERROR_BAD_VALUE; } - + // PCM_8BIT is not supported at the moment if (audioFormat != AudioFormat.ENCODING_PCM_16BIT) { loge("getMinBufferSize(): Invalid audio format."); return AudioRecord.ERROR_BAD_VALUE; } - + int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat); if (size == 0) { return AudioRecord.ERROR_BAD_VALUE; - } + } else if (size == -1) { return AudioRecord.ERROR; } @@ -506,7 +503,7 @@ public class AudioRecord // Transport control methods //-------------------- /** - * Starts recording from the AudioRecord instance. + * Starts recording from the AudioRecord instance. * @throws IllegalStateException */ public void startRecording() @@ -576,13 +573,13 @@ public class AudioRecord * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if * the parameters don't resolve to valid data and indexes. * The number of bytes will not exceed sizeInBytes. - */ + */ public int read(byte[] audioData, int offsetInBytes, int sizeInBytes) { if (mState != STATE_INITIALIZED) { return ERROR_INVALID_OPERATION; } - - if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0) + + if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0) || (offsetInBytes + sizeInBytes > audioData.length)) { return ERROR_BAD_VALUE; } @@ -600,13 +597,13 @@ public class AudioRecord * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if * the parameters don't resolve to valid data and indexes. * The number of shorts will not exceed sizeInShorts. - */ + */ public int read(short[] audioData, int offsetInShorts, int sizeInShorts) { if (mState != STATE_INITIALIZED) { return ERROR_INVALID_OPERATION; } - - if ( (audioData == null) || (offsetInShorts < 0 ) || (sizeInShorts < 0) + + if ( (audioData == null) || (offsetInShorts < 0 ) || (sizeInShorts < 0) || (offsetInShorts + sizeInShorts > audioData.length)) { return ERROR_BAD_VALUE; } @@ -618,18 +615,20 @@ public class AudioRecord /** * Reads audio data from the audio hardware for recording into a direct buffer. If this buffer * is not a direct buffer, this method will always return 0. + * Note that the value returned by {@link java.nio.Buffer#position()} on this buffer is + * unchanged after a call to this method. * @param audioBuffer the direct buffer to which the recorded audio data is written. * @param sizeInBytes the number of requested bytes. * @return the number of bytes that were read or or {@link #ERROR_INVALID_OPERATION} * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if * the parameters don't resolve to valid data and indexes. * The number of bytes will not exceed sizeInBytes. - */ + */ public int read(ByteBuffer audioBuffer, int sizeInBytes) { if (mState != STATE_INITIALIZED) { return ERROR_INVALID_OPERATION; } - + if ( (audioBuffer == null) || (sizeInBytes < 0) ) { return ERROR_BAD_VALUE; } @@ -640,7 +639,7 @@ public class AudioRecord //-------------------------------------------------------------------------- // Initialization / configuration - //-------------------- + //-------------------- /** * Sets the listener the AudioRecord notifies when a previously set marker is reached or * for each periodic record head position update. @@ -658,12 +657,12 @@ public class AudioRecord * @param listener * @param handler the Handler that will receive the event notification messages. */ - public void setRecordPositionUpdateListener(OnRecordPositionUpdateListener listener, + public void setRecordPositionUpdateListener(OnRecordPositionUpdateListener listener, Handler handler) { synchronized (mPositionListenerLock) { - + mPositionListener = listener; - + if (listener != null) { if (handler != null) { mEventHandler = new NativeEventHandler(this, handler.getLooper()); @@ -675,17 +674,17 @@ public class AudioRecord mEventHandler = null; } } - + } /** - * Sets the marker position at which the listener is called, if set with - * {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener)} or + * Sets the marker position at which the listener is called, if set with + * {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener)} or * {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener, Handler)}. * @param markerInFrames marker position expressed in frames * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE}, - * {@link #ERROR_INVALID_OPERATION} + * {@link #ERROR_INVALID_OPERATION} */ public int setNotificationMarkerPosition(int markerInFrames) { return native_set_marker_pos(markerInFrames); @@ -694,7 +693,7 @@ public class AudioRecord /** * Sets the period at which the listener is called, if set with - * {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener)} or + * {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener)} or * {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener, Handler)}. * @param periodInFrames update period expressed in frames * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_INVALID_OPERATION} @@ -719,7 +718,7 @@ public class AudioRecord * by the recording head. */ void onMarkerReached(AudioRecord recorder); - + /** * Called on the listener to periodically notify it that the record head has reached * a multiple of the notification period. @@ -732,13 +731,13 @@ public class AudioRecord //--------------------------------------------------------- // Inner classes //-------------------- - + /** * Helper class to handle the forwarding of native events to the appropriate listener * (potentially) handled in a different thread - */ + */ private class NativeEventHandler extends Handler { - + private final AudioRecord mAudioRecord; NativeEventHandler(AudioRecord recorder, Looper looper) { @@ -752,8 +751,8 @@ public class AudioRecord synchronized (mPositionListenerLock) { listener = mAudioRecord.mPositionListener; } - - switch(msg.what) { + + switch (msg.what) { case NATIVE_EVENT_MARKER: if (listener != null) { listener.onMarkerReached(mAudioRecord); @@ -771,8 +770,8 @@ public class AudioRecord } } }; - - + + //--------------------------------------------------------- // Java methods called from the native side //-------------------- @@ -784,50 +783,50 @@ public class AudioRecord if (recorder == null) { return; } - + if (recorder.mEventHandler != null) { - Message m = + Message m = recorder.mEventHandler.obtainMessage(what, arg1, arg2, obj); recorder.mEventHandler.sendMessage(m); } } - + //--------------------------------------------------------- // Native methods called from the Java side //-------------------- - private native final int native_setup(Object audiorecord_this, + private native final int native_setup(Object audiorecord_this, int recordSource, int sampleRate, int nbChannels, int audioFormat, int buffSizeInBytes, int[] sessionId); private native final void native_finalize(); - + private native final void native_release(); private native final int native_start(int syncEvent, int sessionId); private native final void native_stop(); - private native final int native_read_in_byte_array(byte[] audioData, + private native final int native_read_in_byte_array(byte[] audioData, int offsetInBytes, int sizeInBytes); - private native final int native_read_in_short_array(short[] audioData, + private native final int native_read_in_short_array(short[] audioData, int offsetInShorts, int sizeInShorts); private native final int native_read_in_direct_buffer(Object jBuffer, int sizeInBytes); - + private native final int native_set_marker_pos(int marker); private native final int native_get_marker_pos(); - + private native final int native_set_pos_update_period(int updatePeriod); private native final int native_get_pos_update_period(); - + static private native final int native_get_min_buff_size( int sampleRateInHz, int channelCount, int audioFormat); - + //--------------------------------------------------------- // Utility methods //------------------ @@ -841,4 +840,3 @@ public class AudioRecord } } - diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 2ad9de9..653081d 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -63,7 +63,6 @@ import android.provider.Settings; import android.provider.Settings.System; import android.speech.RecognizerIntent; import android.telephony.PhoneStateListener; -import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -157,6 +156,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 25; private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME = 26; private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED = 27; + private static final int MSG_PERSIST_SAFE_VOLUME_STATE = 28; // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be // persisted @@ -438,6 +438,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished { private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + // Used when safe volume warning message display is requested by setStreamVolume(). In this + // case, the new requested volume, stream type and device are stored in mPendingVolumeCommand + // and used later when/if disableSafeMediaVolume() is called. + private StreamVolumeCommand mPendingVolumeCommand; + /////////////////////////////////////////////////////////////////////////// // Construction /////////////////////////////////////////////////////////////////////////// @@ -481,6 +486,14 @@ public class AudioService extends IAudioService.Stub implements OnFinished { null, 0); + mSafeMediaVolumeState = new Integer(Settings.Global.getInt(mContentResolver, + Settings.Global.AUDIO_SAFE_VOLUME_STATE, + SAFE_MEDIA_VOLUME_NOT_CONFIGURED)); + // The default safe volume index read here will be replaced by the actual value when + // the mcc is read by onConfigureSafeVolume() + mSafeMediaVolumeIndex = mContext.getResources().getInteger( + com.android.internal.R.integer.config_safe_media_volume_index) * 10; + readPersistedSettings(); mSettingsObserver = new SettingsObserver(); updateStreamVolumeAlias(false /*updateVolumes*/); @@ -488,8 +501,6 @@ public class AudioService extends IAudioService.Stub implements OnFinished { mMediaServerOk = true; - mSafeMediaVolumeState = new Integer(SAFE_MEDIA_VOLUME_NOT_CONFIGURED); - // Call setRingerModeInt() to apply correct mute // state on streams affected by ringer mode. mRingerModeMutedStreams = 0; @@ -814,28 +825,49 @@ public class AudioService extends IAudioService.Stub implements OnFinished { VolumeStreamState streamState = mStreamStates[streamTypeAlias]; final int device = getDeviceForStream(streamTypeAlias); + // get last audible index if stream is muted, current index otherwise - final int aliasIndex = streamState.getIndex(device, - (streamState.muteCount() != 0) /* lastAudible */); + int aliasIndex = streamState.getIndex(device, + (streamState.muteCount() != 0) /* lastAudible */); boolean adjustVolume = true; - // convert one UI step (+/-1) into a number of internal units on the stream alias - int step = rescaleIndex(10, streamType, streamTypeAlias); - - if ((direction == AudioManager.ADJUST_RAISE) && - !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) { - return; - } - + int step; int index; int oldIndex; + // reset any pending volume command + synchronized (mSafeMediaVolumeState) { + mPendingVolumeCommand = null; + } + flags &= ~AudioManager.FLAG_FIXED_VOLUME; if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) && ((device & mFixedVolumeDevices) != 0)) { flags |= AudioManager.FLAG_FIXED_VOLUME; - index = mStreamStates[streamType].getMaxIndex(); + + // Always toggle between max safe volume and 0 for fixed volume devices where safe + // volume is enforced, and max and 0 for the others. + // This is simulated by stepping by the full allowed volume range + if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE && + (device & mSafeMediaVolumeDevices) != 0) { + step = mSafeMediaVolumeIndex; + } else { + step = streamState.getMaxIndex(); + } + if (aliasIndex != 0) { + aliasIndex = step; + } + } else { + // convert one UI step (+/-1) into a number of internal units on the stream alias + step = rescaleIndex(10, streamType, streamTypeAlias); + } + + if ((direction == AudioManager.ADJUST_RAISE) && + !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) { + index = mStreamStates[streamType].getIndex(device, + (streamState.muteCount() != 0) /* lastAudible */); oldIndex = index; + mVolumePanel.postDisplaySafeVolumeWarning(flags); } else { // If either the client forces allowing ringer modes for this adjustment, // or the stream type is one that is affected by ringer modes @@ -907,6 +939,44 @@ public class AudioService extends IAudioService.Stub implements OnFinished { setMasterVolume(volume, flags); } + // StreamVolumeCommand contains the information needed to defer the process of + // setStreamVolume() in case the user has to acknowledge the safe volume warning message. + class StreamVolumeCommand { + public final int mStreamType; + public final int mIndex; + public final int mFlags; + public final int mDevice; + + StreamVolumeCommand(int streamType, int index, int flags, int device) { + mStreamType = streamType; + mIndex = index; + mFlags = flags; + mDevice = device; + } + }; + + private void onSetStreamVolume(int streamType, int index, int flags, int device) { + // setting volume on master stream type also controls silent mode + if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || + (mStreamVolumeAlias[streamType] == getMasterStreamType())) { + int newRingerMode; + if (index == 0) { + newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE + : AudioManager.RINGER_MODE_SILENT; + setStreamVolumeInt(mStreamVolumeAlias[streamType], + index, + device, + false, + true); + } else { + newRingerMode = AudioManager.RINGER_MODE_NORMAL; + } + setRingerMode(newRingerMode); + } + + setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, false, true); + } + /** @see AudioManager#setStreamVolume(int, int, int) */ public void setStreamVolume(int streamType, int index, int flags) { ensureValidStreamType(streamType); @@ -915,45 +985,42 @@ public class AudioService extends IAudioService.Stub implements OnFinished { final int device = getDeviceForStream(streamType); int oldIndex; - flags &= ~AudioManager.FLAG_FIXED_VOLUME; - if ((mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) && - ((device & mFixedVolumeDevices) != 0)) { - flags |= AudioManager.FLAG_FIXED_VOLUME; - index = mStreamStates[streamType].getMaxIndex(); - oldIndex = index; - } else { + synchronized (mSafeMediaVolumeState) { + // reset any pending volume command + mPendingVolumeCommand = null; + // get last audible index if stream is muted, current index otherwise oldIndex = streamState.getIndex(device, (streamState.muteCount() != 0) /* lastAudible */); index = rescaleIndex(index * 10, streamType, mStreamVolumeAlias[streamType]); - if (!checkSafeMediaVolume(mStreamVolumeAlias[streamType], index, device)) { - return; - } + flags &= ~AudioManager.FLAG_FIXED_VOLUME; + if ((mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) && + ((device & mFixedVolumeDevices) != 0)) { + flags |= AudioManager.FLAG_FIXED_VOLUME; - // setting volume on master stream type also controls silent mode - if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || - (mStreamVolumeAlias[streamType] == getMasterStreamType())) { - int newRingerMode; - if (index == 0) { - newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE - : AudioManager.RINGER_MODE_SILENT; - setStreamVolumeInt(mStreamVolumeAlias[streamType], - index, - device, - false, - true); - } else { - newRingerMode = AudioManager.RINGER_MODE_NORMAL; + // volume is either 0 or max allowed for fixed volume devices + if (index != 0) { + if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE && + (device & mSafeMediaVolumeDevices) != 0) { + index = mSafeMediaVolumeIndex; + } else { + index = streamState.getMaxIndex(); + } } - setRingerMode(newRingerMode); } - setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, false, true); - // get last audible index if stream is muted, current index otherwise - index = mStreamStates[streamType].getIndex(device, + if (!checkSafeMediaVolume(mStreamVolumeAlias[streamType], index, device)) { + mVolumePanel.postDisplaySafeVolumeWarning(flags); + mPendingVolumeCommand = new StreamVolumeCommand( + streamType, index, flags, device); + } else { + onSetStreamVolume(streamType, index, flags, device); + // get last audible index if stream is muted, current index otherwise + index = mStreamStates[streamType].getIndex(device, (mStreamStates[streamType].muteCount() != 0) /* lastAudible */); + } } sendVolumeUpdate(streamType, oldIndex, index, flags); } @@ -1193,13 +1260,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished { public int getStreamVolume(int streamType) { ensureValidStreamType(streamType); int device = getDeviceForStream(streamType); - int index; + int index = mStreamStates[streamType].getIndex(device, false /* lastAudible */); - if ((mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) && + if (index != 0 && (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) && (device & mFixedVolumeDevices) != 0) { index = mStreamStates[streamType].getMaxIndex(); - } else { - index = mStreamStates[streamType].getIndex(device, false /* lastAudible */); } return (index + 5) / 10; } @@ -2290,7 +2355,6 @@ public class AudioService extends IAudioService.Stub implements OnFinished { if (mMusicActiveMs > UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX) { setSafeMediaVolumeEnabled(true); mMusicActiveMs = 0; - mVolumePanel.postDisplaySafeVolumeWarning(); } } } @@ -2306,13 +2370,31 @@ public class AudioService extends IAudioService.Stub implements OnFinished { com.android.internal.R.integer.config_safe_media_volume_index) * 10; boolean safeMediaVolumeEnabled = mContext.getResources().getBoolean( com.android.internal.R.bool.config_safe_media_volume_enabled); + + // The persisted state is either "disabled" or "active": this is the state applied + // next time we boot and cannot be "inactive" + int persistedState; if (safeMediaVolumeEnabled) { - mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_ACTIVE; - enforceSafeMediaVolume(); + persistedState = SAFE_MEDIA_VOLUME_ACTIVE; + // The state can already be "inactive" here if the user has forced it before + // the 30 seconds timeout for forced configuration. In this case we don't reset + // it to "active". + if (mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_INACTIVE) { + mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_ACTIVE; + enforceSafeMediaVolume(); + } } else { + persistedState = SAFE_MEDIA_VOLUME_DISABLED; mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_DISABLED; } mMcc = mcc; + sendMsg(mAudioHandler, + MSG_PERSIST_SAFE_VOLUME_STATE, + SENDMSG_QUEUE, + persistedState, + 0, + null, + 0); } } } @@ -2675,15 +2757,6 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } remainingDevices &= ~device; - // ignore settings for fixed volume devices: volume should always be at max - if ((mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) && - ((device & mFixedVolumeDevices) != 0)) { - if (muteCount() == 0) { - mIndex.put(device, mIndexMax); - } - mLastAudibleIndex.put(device, mIndexMax); - continue; - } // retrieve current volume for device String name = getSettingNameForDevice(false /* lastAudible */, device); // if no volume stored for current stream and device, use default volume if default @@ -2696,6 +2769,18 @@ public class AudioService extends IAudioService.Stub implements OnFinished { continue; } + // ignore settings for fixed volume devices: volume should always be at max or 0 + if ((mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) && + ((device & mFixedVolumeDevices) != 0)) { + if ((muteCount()) == 0 && (index != 0)) { + mIndex.put(device, mIndexMax); + } else { + mIndex.put(device, 0); + } + mLastAudibleIndex.put(device, mIndexMax); + continue; + } + // retrieve last audible volume for device name = getSettingNameForDevice(true /* lastAudible */, device); // use stored last audible index if present, otherwise use current index if not 0 @@ -3224,6 +3309,12 @@ public class AudioService extends IAudioService.Stub implements OnFinished { AudioSystem.setForceUse(usage, config); } + private void onPersistSafeVolumeState(int state) { + Settings.Global.putInt(mContentResolver, + Settings.Global.AUDIO_SAFE_VOLUME_STATE, + state); + } + @Override public void handleMessage(Message msg) { @@ -3433,6 +3524,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished { case MSG_CONFIGURE_SAFE_MEDIA_VOLUME: onConfigureSafeVolume((msg.what == MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED)); break; + case MSG_PERSIST_SAFE_VOLUME_STATE: + onPersistSafeVolumeState(msg.arg1); + break; } } } @@ -5936,7 +6030,6 @@ public class AudioService extends IAudioService.Stub implements OnFinished { (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) && ((device & mSafeMediaVolumeDevices) != 0) && (index > mSafeMediaVolumeIndex)) { - mVolumePanel.postDisplaySafeVolumeWarning(); return false; } return true; @@ -5946,6 +6039,13 @@ public class AudioService extends IAudioService.Stub implements OnFinished { public void disableSafeMediaVolume() { synchronized (mSafeMediaVolumeState) { setSafeMediaVolumeEnabled(false); + if (mPendingVolumeCommand != null) { + onSetStreamVolume(mPendingVolumeCommand.mStreamType, + mPendingVolumeCommand.mIndex, + mPendingVolumeCommand.mFlags, + mPendingVolumeCommand.mDevice); + mPendingVolumeCommand = null; + } } } diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 9769f30..eff4f16 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -17,13 +17,10 @@ package android.media; import java.lang.ref.WeakReference; -import java.lang.IllegalArgumentException; -import java.lang.IllegalStateException; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.media.AudioManager; import android.util.Log; @@ -69,6 +66,11 @@ public class AudioTrack /** Maximum value for a channel volume */ private static final float VOLUME_MAX = 1.0f; + /** Minimum value for sample rate */ + private static final int SAMPLE_RATE_HZ_MIN = 4000; + /** Maximum value for sample rate */ + private static final int SAMPLE_RATE_HZ_MAX = 48000; + /** indicates AudioTrack state is stopped */ public static final int PLAYSTATE_STOPPED = 1; // matches SL_PLAYSTATE_STOPPED /** indicates AudioTrack state is paused */ @@ -138,7 +140,7 @@ public class AudioTrack */ private static final int NATIVE_EVENT_NEW_POS = 4; - private final static String TAG = "AudioTrack-Java"; + private final static String TAG = "android.media.AudioTrack"; //-------------------------------------------------------------------------- @@ -177,7 +179,7 @@ public class AudioTrack /** * Looper associated with the thread that creates the AudioTrack instance. */ - private Looper mInitializationLooper = null; + private final Looper mInitializationLooper; /** * The audio data sampling rate in Hz. */ @@ -308,16 +310,18 @@ public class AudioTrack mState = STATE_UNINITIALIZED; // remember which looper is associated with the AudioTrack instantiation - if ((mInitializationLooper = Looper.myLooper()) == null) { - mInitializationLooper = Looper.getMainLooper(); + Looper looper; + if ((looper = Looper.myLooper()) == null) { + looper = Looper.getMainLooper(); } + mInitializationLooper = looper; audioParamCheck(streamType, sampleRateInHz, channelConfig, audioFormat, mode); audioBuffSizeCheck(bufferSizeInBytes); if (sessionId < 0) { - throw (new IllegalArgumentException("Invalid audio session ID: "+sessionId)); + throw new IllegalArgumentException("Invalid audio session ID: "+sessionId); } int[] session = new int[1]; @@ -370,7 +374,7 @@ public class AudioTrack && (streamType != AudioManager.STREAM_NOTIFICATION) && (streamType != AudioManager.STREAM_BLUETOOTH_SCO) && (streamType != AudioManager.STREAM_DTMF)) { - throw (new IllegalArgumentException("Invalid stream type.")); + throw new IllegalArgumentException("Invalid stream type."); } else { mStreamType = streamType; } @@ -378,8 +382,8 @@ public class AudioTrack //-------------- // sample rate, note these values are subject to change if ( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) ) { - throw (new IllegalArgumentException(sampleRateInHz - + "Hz is not a supported sample rate.")); + throw new IllegalArgumentException(sampleRateInHz + + "Hz is not a supported sample rate."); } else { mSampleRate = sampleRateInHz; } @@ -406,7 +410,7 @@ public class AudioTrack mChannelCount = 0; mChannels = AudioFormat.CHANNEL_INVALID; mChannelConfiguration = AudioFormat.CHANNEL_INVALID; - throw(new IllegalArgumentException("Unsupported channel configuration.")); + throw new IllegalArgumentException("Unsupported channel configuration."); } else { mChannels = channelConfig; mChannelCount = Integer.bitCount(channelConfig); @@ -425,14 +429,14 @@ public class AudioTrack break; default: mAudioFormat = AudioFormat.ENCODING_INVALID; - throw(new IllegalArgumentException("Unsupported sample encoding." - + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT.")); + throw new IllegalArgumentException("Unsupported sample encoding." + + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT."); } //-------------- // audio load mode if ( (mode != MODE_STREAM) && (mode != MODE_STATIC) ) { - throw(new IllegalArgumentException("Invalid mode.")); + throw new IllegalArgumentException("Invalid mode."); } else { mDataLoadMode = mode; } @@ -446,7 +450,7 @@ public class AudioTrack private static boolean isMultichannelConfigSupported(int channelConfig) { // check for unsupported channels if ((channelConfig & SUPPORTED_OUT_CHANNELS) != channelConfig) { - Log.e(TAG, "Channel configuration features unsupported channels"); + loge("Channel configuration features unsupported channels"); return false; } // check for unsupported multichannel combinations: @@ -455,14 +459,14 @@ public class AudioTrack final int frontPair = AudioFormat.CHANNEL_OUT_FRONT_LEFT | AudioFormat.CHANNEL_OUT_FRONT_RIGHT; if ((channelConfig & frontPair) != frontPair) { - Log.e(TAG, "Front channels must be present in multichannel configurations"); + loge("Front channels must be present in multichannel configurations"); return false; } final int backPair = AudioFormat.CHANNEL_OUT_BACK_LEFT | AudioFormat.CHANNEL_OUT_BACK_RIGHT; if ((channelConfig & backPair) != 0) { if ((channelConfig & backPair) != backPair) { - Log.e(TAG, "Rear channels can't be used independently"); + loge("Rear channels can't be used independently"); return false; } } @@ -482,7 +486,7 @@ public class AudioTrack int frameSizeInBytes = mChannelCount * (mAudioFormat == AudioFormat.ENCODING_PCM_8BIT ? 1 : 2); if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) { - throw (new IllegalArgumentException("Invalid audio buffer size.")); + throw new IllegalArgumentException("Invalid audio buffer size."); } mNativeBufferSizeInBytes = audioBufferSize; @@ -518,7 +522,7 @@ public class AudioTrack * @return the minimum volume expressed as a linear attenuation. */ static public float getMinVolume() { - return AudioTrack.VOLUME_MIN; + return VOLUME_MIN; } /** @@ -527,7 +531,7 @@ public class AudioTrack * @return the maximum volume expressed as a linear attenuation. */ static public float getMaxVolume() { - return AudioTrack.VOLUME_MAX; + return VOLUME_MAX; } /** @@ -672,7 +676,7 @@ public class AudioTrack if ((channelConfig & SUPPORTED_OUT_CHANNELS) != channelConfig) { // input channel configuration features unsupported channels loge("getMinBufferSize(): Invalid channel configuration."); - return AudioTrack.ERROR_BAD_VALUE; + return ERROR_BAD_VALUE; } else { channelCount = Integer.bitCount(channelConfig); } @@ -681,19 +685,19 @@ public class AudioTrack if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT) && (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) { loge("getMinBufferSize(): Invalid audio format."); - return AudioTrack.ERROR_BAD_VALUE; + return ERROR_BAD_VALUE; } // sample rate, note these values are subject to change - if ( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) ) { - loge("getMinBufferSize(): " + sampleRateInHz +"Hz is not a supported sample rate."); - return AudioTrack.ERROR_BAD_VALUE; + if ( (sampleRateInHz < SAMPLE_RATE_HZ_MIN) || (sampleRateInHz > SAMPLE_RATE_HZ_MAX) ) { + loge("getMinBufferSize(): " + sampleRateInHz + " Hz is not a supported sample rate."); + return ERROR_BAD_VALUE; } int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat); if ((size == -1) || (size == 0)) { loge("getMinBufferSize(): error querying hardware"); - return AudioTrack.ERROR; + return ERROR; } else { return size; @@ -889,7 +893,7 @@ public class AudioTrack public void play() throws IllegalStateException { if (mState != STATE_INITIALIZED) { - throw(new IllegalStateException("play() called on uninitialized AudioTrack.")); + throw new IllegalStateException("play() called on uninitialized AudioTrack."); } synchronized(mPlayStateLock) { @@ -909,7 +913,7 @@ public class AudioTrack public void stop() throws IllegalStateException { if (mState != STATE_INITIALIZED) { - throw(new IllegalStateException("stop() called on uninitialized AudioTrack.")); + throw new IllegalStateException("stop() called on uninitialized AudioTrack."); } // stop playing @@ -929,7 +933,7 @@ public class AudioTrack public void pause() throws IllegalStateException { if (mState != STATE_INITIALIZED) { - throw(new IllegalStateException("pause() called on uninitialized AudioTrack.")); + throw new IllegalStateException("pause() called on uninitialized AudioTrack."); } //logd("pause()"); @@ -1170,8 +1174,7 @@ public class AudioTrack } break; default: - Log.e(TAG, "[ android.media.AudioTrack.NativeEventHandler ] " + - "Unknown event type: " + msg.what); + loge("Unknown native event type: " + msg.what); break; } } @@ -1258,8 +1261,6 @@ public class AudioTrack static private native final int native_get_min_buff_size( int sampleRateInHz, int channelConfig, int audioFormat); - private native final int native_get_session_id(); - private native final int native_attachAuxEffect(int effectId); private native final void native_setAuxEffectSendLevel(float level); @@ -1268,11 +1269,11 @@ public class AudioTrack //------------------ private static void logd(String msg) { - Log.d(TAG, "[ android.media.AudioTrack ] " + msg); + Log.d(TAG, msg); } private static void loge(String msg) { - Log.e(TAG, "[ android.media.AudioTrack ] " + msg); + Log.e(TAG, msg); } } diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 99db066..d5515eb 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -16,6 +16,8 @@ package android.media; +import android.media.MediaCodecInfo; +import android.media.MediaCodecList; import android.media.MediaCrypto; import android.media.MediaFormat; import android.view.Surface; @@ -498,6 +500,22 @@ final public class MediaCodec { */ public native final void setVideoScalingMode(int mode); + /** + * Get the component name. If the codec was created by createDecoderByType + * or createEncoderByType, what component is chosen is not known beforehand. + */ + public native final String getName(); + + /** + * Get the codec info. If the codec was created by createDecoderByType + * or createEncoderByType, what component is chosen is not known beforehand, + * and thus the caller does not have the MediaCodecInfo. + */ + public MediaCodecInfo getCodecInfo() { + return MediaCodecList.getCodecInfoAt( + MediaCodecList.findCodecByName(getName())); + } + private native final ByteBuffer[] getBuffers(boolean input); private static native final void native_init(); diff --git a/media/java/android/media/MediaCodecList.java b/media/java/android/media/MediaCodecList.java index 1749934..2a60113 100644 --- a/media/java/android/media/MediaCodecList.java +++ b/media/java/android/media/MediaCodecList.java @@ -46,6 +46,8 @@ final public class MediaCodecList { /* package private */ static native final MediaCodecInfo.CodecCapabilities getCodecCapabilities(int index, String type); + /* package private */ static native final int findCodecByName(String codec); + private static native final void native_init(); private MediaCodecList() {} diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index 169502b..a2eb8d9 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -26,7 +26,7 @@ import java.util.Map; * * The format of the media data is specified as string/value pairs. * - * Keys common to all formats: + * Keys common to all formats, <b>all keys not marked optional are mandatory</b>: * * <table> * <tr><th>Name</th><th>Value Type</th><th>Description</th></tr> @@ -52,7 +52,7 @@ import java.util.Map; * <tr><td>{@link #KEY_SAMPLE_RATE}</td><td>Integer</td><td></td></tr> * <tr><td>{@link #KEY_IS_ADTS}</td><td>Integer</td><td>optional, if <em>decoding</em> AAC audio content, setting this key to 1 indicates that each audio frame is prefixed by the ADTS header.</td></tr> * <tr><td>{@link #KEY_AAC_PROFILE}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is AAC audio, specifies the desired profile.</td></tr> - * <tr><td>{@link #KEY_CHANNEL_MASK}</td><td>Integer</td><td>A mask of audio channel assignments</td></tr> + * <tr><td>{@link #KEY_CHANNEL_MASK}</td><td>Integer</td><td>optional, a mask of audio channel assignments</td></tr> * <tr><td>{@link #KEY_FLAC_COMPRESSION_LEVEL}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is FLAC audio, specifies the desired compression level.</td></tr> * </table> * diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index 8701f36..8b489b1 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -313,13 +313,25 @@ public class MediaRouter { } /** - * Return the currently selected route for the given types + * Return the currently selected route for any of the given types * * @param type route types * @return the selected route */ public RouteInfo getSelectedRoute(int type) { - return sStatic.mSelectedRoute; + if (sStatic.mSelectedRoute != null && + (sStatic.mSelectedRoute.mSupportedTypes & type) != 0) { + // If the selected route supports any of the types supplied, it's still considered + // 'selected' for that type. + return sStatic.mSelectedRoute; + } else if (type == ROUTE_TYPE_USER) { + // The caller specifically asked for a user route and the currently selected route + // doesn't qualify. + return null; + } + // If the above didn't match and we're not specifically asking for a user route, + // consider the default selected. + return sStatic.mDefaultAudioVideo; } /** diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 959bf0a..423a109 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -958,6 +958,7 @@ public class MediaScanner // If the rowId of the inserted file is needed, it gets inserted immediately, // bypassing the bulk inserter. if (inserter == null || needToSetSettings) { + inserter.flushAll(); result = mMediaProvider.insert(tableUri, values); } else if (entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) { inserter.insertwithPriority(tableUri, values); diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index f91c9a0..dab2de1 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -264,6 +264,20 @@ status_t JMediaCodec::getBuffers( return OK; } +status_t JMediaCodec::getName(JNIEnv *env, jstring *nameStr) const { + AString name; + + status_t err = mCodec->getName(&name); + + if (err != OK) { + return err; + } + + *nameStr = env->NewStringUTF(name.c_str()); + + return OK; +} + void JMediaCodec::setVideoScalingMode(int mode) { if (mSurfaceTextureClient != NULL) { native_window_set_scaling_mode(mSurfaceTextureClient.get(), mode); @@ -706,6 +720,29 @@ static jobjectArray android_media_MediaCodec_getBuffers( return NULL; } +static jobject android_media_MediaCodec_getName( + JNIEnv *env, jobject thiz) { + ALOGV("android_media_MediaCodec_getName"); + + sp<JMediaCodec> codec = getMediaCodec(env, thiz); + + if (codec == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return NULL; + } + + jstring name; + status_t err = codec->getName(env, &name); + + if (err == OK) { + return name; + } + + throwExceptionAsNecessary(env, err); + + return NULL; +} + static void android_media_MediaCodec_setVideoScalingMode( JNIEnv *env, jobject thiz, jint mode) { sp<JMediaCodec> codec = getMediaCodec(env, thiz); @@ -826,6 +863,9 @@ static JNINativeMethod gMethods[] = { { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;", (void *)android_media_MediaCodec_getBuffers }, + { "getName", "()Ljava/lang/String;", + (void *)android_media_MediaCodec_getName }, + { "setVideoScalingMode", "(I)V", (void *)android_media_MediaCodec_setVideoScalingMode }, diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h index 4936b53..bc9ad50 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -81,6 +81,8 @@ struct JMediaCodec : public RefBase { status_t getBuffers( JNIEnv *env, bool input, jobjectArray *bufArray) const; + status_t getName(JNIEnv *env, jstring *name) const; + void setVideoScalingMode(int mode); protected: diff --git a/media/jni/android_media_MediaCodecList.cpp b/media/jni/android_media_MediaCodecList.cpp index 0638b4a..04430ec 100644 --- a/media/jni/android_media_MediaCodecList.cpp +++ b/media/jni/android_media_MediaCodecList.cpp @@ -44,6 +44,25 @@ static jstring android_media_MediaCodecList_getCodecName( return env->NewStringUTF(name); } +static jint android_media_MediaCodecList_findCodecByName( + JNIEnv *env, jobject thiz, jstring name) { + if (name == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return -ENOENT; + } + + const char *nameStr = env->GetStringUTFChars(name, NULL); + + if (nameStr == NULL) { + // Out of memory exception already pending. + return -ENOENT; + } + + jint ret = MediaCodecList::getInstance()->findCodecByName(nameStr); + env->ReleaseStringUTFChars(name, nameStr); + return ret; +} + static jboolean android_media_MediaCodecList_isEncoder( JNIEnv *env, jobject thiz, jint index) { return MediaCodecList::getInstance()->isEncoder(index); @@ -180,6 +199,9 @@ static JNINativeMethod gMethods[] = { "(ILjava/lang/String;)Landroid/media/MediaCodecInfo$CodecCapabilities;", (void *)android_media_MediaCodecList_getCodecCapabilities }, + { "findCodecByName", "(Ljava/lang/String;)I", + (void *)android_media_MediaCodecList_findCodecByName }, + { "native_init", "()V", (void *)android_media_MediaCodecList_native_init }, }; |