diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-02 22:54:33 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-02 22:54:33 -0800 |
commit | 3dec7d563a2f3e1eb967ce2054a00b6620e3558c (patch) | |
tree | aa3b0365c47cb3c1607c0dc76c8d32b4046fc287 /media/java | |
parent | 15ab3eae2ec3d73b3e8aa60b33ae41445bf83f4b (diff) | |
download | frameworks_base-3dec7d563a2f3e1eb967ce2054a00b6620e3558c.zip frameworks_base-3dec7d563a2f3e1eb967ce2054a00b6620e3558c.tar.gz frameworks_base-3dec7d563a2f3e1eb967ce2054a00b6620e3558c.tar.bz2 |
auto import from //depot/cupcake/@137055
Diffstat (limited to 'media/java')
-rw-r--r-- | media/java/android/media/AudioRecord.java | 38 | ||||
-rw-r--r-- | media/java/android/media/AudioTrack.java | 57 | ||||
-rw-r--r-- | media/java/android/media/JetPlayer.java | 11 | ||||
-rw-r--r-- | media/java/android/media/MediaMetadataRetriever.java | 14 | ||||
-rw-r--r-- | media/java/android/media/MediaPlayer.java | 13 | ||||
-rw-r--r-- | media/java/android/media/MediaRecorder.java | 238 | ||||
-rw-r--r-- | media/java/android/media/Ringtone.java | 13 | ||||
-rw-r--r-- | media/java/android/media/SoundPool.java | 7 |
8 files changed, 295 insertions, 96 deletions
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index fd990fe..0ef7760 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -532,12 +532,19 @@ public class AudioRecord * @param audioData the array to which the recorded audio data is written. * @param offsetInBytes index in audioData from which the data is written. * @param sizeInBytes the number of requested bytes. - * @return the number of bytes that were read or -1 if the object wasn't properly - * initialized. The number of bytes will not exceed sizeInBytes. + * @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(byte[] audioData, int offsetInBytes, int sizeInBytes) { if (mState != STATE_INITIALIZED) { - return -1; + return ERROR_INVALID_OPERATION; + } + + if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0) + || (offsetInBytes + sizeInBytes > audioData.length)) { + return ERROR_BAD_VALUE; } return native_read_in_byte_array(audioData, offsetInBytes, sizeInBytes); @@ -549,12 +556,19 @@ public class AudioRecord * @param audioData the array to which the recorded audio data is written. * @param offsetInShorts index in audioData from which the data is written. * @param sizeInShorts the number of requested shorts. - * @return the number of shorts that were read. or -1 if the object wasn't properly - * initialized. The number of shorts will not exceed sizeInShorts + * @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 shorts will not exceed sizeInShorts. */ public int read(short[] audioData, int offsetInShorts, int sizeInShorts) { if (mState != STATE_INITIALIZED) { - return -1; + return ERROR_INVALID_OPERATION; + } + + if ( (audioData == null) || (offsetInShorts < 0 ) || (sizeInShorts < 0) + || (offsetInShorts + sizeInShorts > audioData.length)) { + return ERROR_BAD_VALUE; } return native_read_in_short_array(audioData, offsetInShorts, sizeInShorts); @@ -566,12 +580,18 @@ public class AudioRecord * is not a direct buffer, this method will always return 0. * @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 -1 if the object wasn't properly - * initialized. The number of bytes will not exceed sizeInBytes. + * @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 -1; + return ERROR_INVALID_OPERATION; + } + + if ( (audioBuffer == null) || (sizeInBytes < 0) ) { + return ERROR_BAD_VALUE; } return native_read_in_direct_buffer(audioBuffer, sizeInBytes); diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index e32835c..997cd44 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -436,6 +436,15 @@ public class AudioTrack public int getSampleRate() { return mSampleRate; } + + /** + * @hide + * Returns the current playback rate in Hz. Note that this rate may differ from one set using + * {@link #setPlaybackRate(int)} as the value effectively set is implementation-dependent. + */ + public int getPlaybackRate() { + return native_get_playback_rate(); + } /** * Returns the configured audio data format. See {@link AudioFormat#ENCODING_PCM_16BIT} @@ -523,8 +532,8 @@ public class AudioTrack /** * Returns the hardware output sample rate */ - static public int getNativeOutputSampleRate() { - return native_get_output_sample_rate(); + static public int getNativeOutputSampleRate(int streamType) { + return native_get_output_sample_rate(streamType); } /** @@ -650,16 +659,19 @@ public class AudioTrack * content. Setting it to half the sample rate of the content will cause the playback to * last twice as long, but will also result result in a negative pitch shift. * The current implementation supports a maximum sample rate of twice the hardware output - * sample rate (see {@link #getNativeOutputSampleRate()}). Use {@link #getSampleRate()} to + * sample rate (see {@link #getNativeOutputSampleRate(int)}). Use {@link #getSampleRate()} to * check the rate actually used in hardware after potential clamping. * @param sampleRateInHz - * @return error code or success, see {@link #SUCCESS}, + * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE}, * {@link #ERROR_INVALID_OPERATION} */ public int setPlaybackRate(int sampleRateInHz) { if (mState != STATE_INITIALIZED) { return ERROR_INVALID_OPERATION; } + if (sampleRateInHz <= 0) { + return ERROR_BAD_VALUE; + } native_set_playback_rate(sampleRateInHz); return SUCCESS; } @@ -699,7 +711,7 @@ public class AudioTrack */ public int setPlaybackHeadPosition(int positionInFrames) { synchronized(mPlayStateLock) { - if(mPlayState == PLAYSTATE_STOPPED) { + if ((mPlayState == PLAYSTATE_STOPPED) || (mPlayState == PLAYSTATE_PAUSED)) { return native_set_position(positionInFrames); } else { return ERROR_INVALID_OPERATION; @@ -717,6 +729,9 @@ public class AudioTrack * {@link #ERROR_INVALID_OPERATION} */ public int setLoopPoints(int startInFrames, int endInFrames, int loopCount) { + if (mDataLoadMode == MODE_STREAM) { + return ERROR_INVALID_OPERATION; + } return native_set_loop(startInFrames, endInFrames, loopCount); } @@ -806,8 +821,9 @@ public class AudioTrack * @param audioData the array that holds the data to play. * @param offsetInBytes the offset in audioData where the data to play starts. * @param sizeInBytes the number of bytes to read in audioData after the offset. - * @return the number of bytes that were written or -1 if the object wasn't properly - * initialized. + * @return the number of bytes that were written 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. */ public int write(byte[] audioData,int offsetInBytes, int sizeInBytes) { @@ -816,11 +832,14 @@ public class AudioTrack && (sizeInBytes > 0)) { mState = STATE_INITIALIZED; } - //TODO check if future writes should be forbidden for static tracks - // or: how to update data for static tracks? if (mState != STATE_INITIALIZED) { - return -1; + return ERROR_INVALID_OPERATION; + } + + if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0) + || (offsetInBytes + sizeInBytes > audioData.length)) { + return ERROR_BAD_VALUE; } return native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat); @@ -832,8 +851,9 @@ public class AudioTrack * @param audioData the array that holds the data to play. * @param offsetInShorts the offset in audioData where the data to play starts. * @param sizeInShorts the number of bytes to read in audioData after the offset. - * @return the number of shorts that were written or -1 if the object wasn't properly - * initialized. + * @return the number of shorts that were written 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. */ public int write(short[] audioData, int offsetInShorts, int sizeInShorts) { @@ -842,11 +862,14 @@ public class AudioTrack && (sizeInShorts > 0)) { mState = STATE_INITIALIZED; } - //TODO check if future writes should be forbidden for static tracks - // or: how to update data for static tracks? - + if (mState != STATE_INITIALIZED) { - return -1; + return ERROR_INVALID_OPERATION; + } + + if ( (audioData == null) || (offsetInShorts < 0 ) || (sizeInShorts < 0) + || (offsetInShorts + sizeInShorts > audioData.length)) { + return ERROR_BAD_VALUE; } return native_write_short(audioData, offsetInShorts, sizeInShorts, mAudioFormat); @@ -1007,7 +1030,7 @@ public class AudioTrack private native final int native_set_loop(int start, int end, int loopCount); - static private native final int native_get_output_sample_rate(); + static private native final int native_get_output_sample_rate(int streamType); static private native final int native_get_min_buff_size( int sampleRateInHz, int channelConfig, int audioFormat); diff --git a/media/java/android/media/JetPlayer.java b/media/java/android/media/JetPlayer.java index bfa2f80..9de0eec 100644 --- a/media/java/android/media/JetPlayer.java +++ b/media/java/android/media/JetPlayer.java @@ -25,6 +25,7 @@ import android.content.res.AssetFileDescriptor; import android.os.Looper; import android.os.Handler; import android.os.Message; +import android.util.AndroidRuntimeException; import android.util.Log; /** @@ -163,8 +164,12 @@ public class JetPlayer public boolean loadJetFile(AssetFileDescriptor afd) { + long len = afd.getLength(); + if (len < 0) { + throw new AndroidRuntimeException("no length for fd"); + } return native_loadJetFromFileD( - afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); + afd.getFileDescriptor(), afd.getStartOffset(), len); } @@ -251,7 +256,9 @@ public class JetPlayer mJet, (short)((msg.arg1 & JET_EVENT_SEG_MASK) >> JET_EVENT_SEG_SHIFT), (byte) ((msg.arg1 & JET_EVENT_TRACK_MASK) >> JET_EVENT_TRACK_SHIFT), - (byte) ((msg.arg1 & JET_EVENT_CHAN_MASK) >> JET_EVENT_CHAN_SHIFT), + // JETCreator channel numbers start at 1, but the index starts at 0 + // in the .jet files + (byte)(((msg.arg1 & JET_EVENT_CHAN_MASK) >> JET_EVENT_CHAN_SHIFT) + 1), (byte) ((msg.arg1 & JET_EVENT_CTRL_MASK) >> JET_EVENT_CTRL_SHIFT), (byte) (msg.arg1 & JET_EVENT_VAL_MASK) ); } diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java index c1a0c21..3a49a5f 100644 --- a/media/java/android/media/MediaMetadataRetriever.java +++ b/media/java/android/media/MediaMetadataRetriever.java @@ -18,6 +18,7 @@ package android.media; import android.content.ContentResolver; import android.content.Context; +import android.content.res.AssetFileDescriptor; import android.graphics.Bitmap; import android.net.Uri; import android.os.ParcelFileDescriptor; @@ -137,11 +138,11 @@ public class MediaMetadataRetriever return; } - ParcelFileDescriptor fd = null; + AssetFileDescriptor fd = null; try { ContentResolver resolver = context.getContentResolver(); try { - fd = resolver.openFileDescriptor(uri, "r"); + fd = resolver.openAssetFileDescriptor(uri, "r"); } catch(FileNotFoundException e) { throw new IllegalArgumentException(); } @@ -152,7 +153,14 @@ public class MediaMetadataRetriever if (!descriptor.valid()) { throw new IllegalArgumentException(); } - setDataSource(descriptor); + // Note: using getDeclaredLength so that our behavior is the same + // as previous versions when the content provider is returning + // a full file. + if (fd.getDeclaredLength() < 0) { + setDataSource(descriptor); + } else { + setDataSource(descriptor, fd.getStartOffset(), fd.getDeclaredLength()); + } return; } catch (SecurityException ex) { } finally { diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 601557d..fe1de8e 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -584,14 +584,21 @@ public class MediaPlayer return; } - ParcelFileDescriptor fd = null; + AssetFileDescriptor fd = null; try { ContentResolver resolver = context.getContentResolver(); - fd = resolver.openFileDescriptor(uri, "r"); + fd = resolver.openAssetFileDescriptor(uri, "r"); if (fd == null) { return; } - setDataSource(fd.getFileDescriptor()); + // Note: using getDeclaredLength so that our behavior is the same + // as previous versions when the content provider is returning + // a full file. + if (fd.getDeclaredLength() < 0) { + setDataSource(fd.getFileDescriptor()); + } else { + setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getDeclaredLength()); + } return; } catch (SecurityException ex) { } catch (IOException ex) { diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 3609826..4906cbb 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -16,23 +16,27 @@ package android.media; -import android.view.Surface; import android.hardware.Camera; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; +import android.view.Surface; import java.io.IOException; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileDescriptor; -import android.util.Log; +import java.lang.ref.WeakReference; /** * Used to record audio and video. The recording control is based on a - * simple state machine (see below). - * + * simple state machine (see below). + * * <p><img src="{@docRoot}images/mediarecorder_state_diagram.gif" border="0" /> * </p> - * + * * <p>A common case of using MediaRecorder to record audio works as follows: - * + * * <pre>MediaRecorder recorder = new MediaRecorder(); * recorder.setAudioSource(MediaRecorder.AudioSource.MIC); * recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); @@ -45,39 +49,54 @@ import android.util.Log; * recorder.reset(); // You can reuse the object by going back to setAudioSource() step * recorder.release(); // Now the object cannot be reused * </pre> - * + * * <p>See the <a href="{@docRoot}guide/topics/media/index.html">Audio and Video</a> * documentation for additional help with using MediaRecorder. */ public class MediaRecorder -{ +{ static { System.loadLibrary("media_jni"); } private final static String TAG = "MediaRecorder"; - + // The two fields below are accessed by native methods @SuppressWarnings("unused") private int mNativeContext; - + @SuppressWarnings("unused") private Surface mSurface; private String mPath; private FileDescriptor mFd; + private EventHandler mEventHandler; + private OnErrorListener mOnErrorListener; /** * Default constructor. */ public MediaRecorder() { - native_setup(); + + Looper looper; + if ((looper = Looper.myLooper()) != null) { + mEventHandler = new EventHandler(this, looper); + } else if ((looper = Looper.getMainLooper()) != null) { + mEventHandler = new EventHandler(this, looper); + } else { + mEventHandler = null; + } + + /* Native setup requires a weak reference to our object. + * It's easier to create it here than in C++. + */ + native_setup(new WeakReference<MediaRecorder>(this)); } - + /** * Sets a Camera to use for recording. Use this function to switch * quickly between preview and capture mode without a teardown of * the camera object. Must call before prepare(). - * + * * @param c the Camera to use for recording */ public native void setCamera(Camera c); @@ -86,15 +105,15 @@ public class MediaRecorder * Sets a Surface to show a preview of recorded media (video). Calls this * before prepare() to make sure that the desirable preview display is * set. - * + * * @param sv the Surface to use for the preview */ public void setPreviewDisplay(Surface sv) { mSurface = sv; } - + /** - * Defines the audio source. These constants are used with + * Defines the audio source. These constants are used with * {@link MediaRecorder#setAudioSource(int)}. */ public final class AudioSource { @@ -108,7 +127,7 @@ public class MediaRecorder } /** - * Defines the video source. These constants are used with + * Defines the video source. These constants are used with * {@link MediaRecorder#setVideoSource(int)}. */ public final class VideoSource { @@ -122,7 +141,7 @@ public class MediaRecorder } /** - * Defines the output format. These constants are used with + * Defines the output format. These constants are used with * {@link MediaRecorder#setOutputFormat(int)}. */ public final class OutputFormat { @@ -140,7 +159,7 @@ public class MediaRecorder }; /** - * Defines the audio encoding. These constants are used with + * Defines the audio encoding. These constants are used with * {@link MediaRecorder#setAudioEncoder(int)}. */ public final class AudioEncoder { @@ -155,7 +174,7 @@ public class MediaRecorder } /** - * Defines the video encoding. These constants are used with + * Defines the video encoding. These constants are used with * {@link MediaRecorder#setVideoEncoder(int)}. */ public final class VideoEncoder { @@ -172,50 +191,50 @@ public class MediaRecorder /** * Sets the audio source to be used for recording. If this method is not * called, the output file will not contain an audio track. The source needs - * to be specified before setting recording-parameters or encoders. Call + * to be specified before setting recording-parameters or encoders. Call * this only before setOutputFormat(). - * + * * @param audio_source the audio source to use * @throws IllegalStateException if it is called after setOutputFormat() * @see android.media.MediaRecorder.AudioSource - */ + */ public native void setAudioSource(int audio_source) throws IllegalStateException; /** * Sets the video source to be used for recording. If this method is not * called, the output file will not contain an video track. The source needs - * to be specified before setting recording-parameters or encoders. Call + * to be specified before setting recording-parameters or encoders. Call * this only before setOutputFormat(). - * + * * @param video_source the video source to use * @throws IllegalStateException if it is called after setOutputFormat() * @see android.media.MediaRecorder.VideoSource - */ + */ public native void setVideoSource(int video_source) throws IllegalStateException; /** * Sets the format of the output file produced during recording. Call this * after setAudioSource()/setVideoSource() but before prepare(). - * - * @param output_format the output format to use. The output format + * + * @param output_format the output format to use. The output format * needs to be specified before setting recording-parameters or encoders. * @throws IllegalStateException if it is called after prepare() or before * setAudioSource()/setVideoSource(). * @see android.media.MediaRecorder.OutputFormat - */ + */ public native void setOutputFormat(int output_format) throws IllegalStateException; - + /** * Sets the width and height of the video to be captured. Must be called * after setVideoSource(). Call this after setOutFormat() but before * prepare(). - * + * * @param width the width of the video to be captured * @param height the height of the video to be captured - * @throws IllegalStateException if it is called after + * @throws IllegalStateException if it is called after * prepare() or before setOutputFormat() */ public native void setVideoSize(int width, int height) @@ -227,7 +246,7 @@ public class MediaRecorder * prepare(). * * @param rate the number of frames per second of video to capture - * @throws IllegalStateException if it is called after + * @throws IllegalStateException if it is called after * prepare() or before setOutputFormat(). * * NOTE: On some devices that have auto-frame rate, this sets the @@ -240,12 +259,12 @@ public class MediaRecorder * Sets the audio encoder to be used for recording. If this method is not * called, the output file will not contain an audio track. Call this after * setOutputFormat() but before prepare(). - * + * * @param audio_encoder the audio encoder to use. * @throws IllegalStateException if it is called before * setOutputFormat() or after prepare(). * @see android.media.MediaRecorder.AudioEncoder - */ + */ public native void setAudioEncoder(int audio_encoder) throws IllegalStateException; @@ -253,43 +272,43 @@ public class MediaRecorder * Sets the video encoder to be used for recording. If this method is not * called, the output file will not contain an video track. Call this after * setOutputFormat() and before prepare(). - * + * * @param video_encoder the video encoder to use. * @throws IllegalStateException if it is called before * setOutputFormat() or after prepare() * @see android.media.MediaRecorder.VideoEncoder - */ + */ public native void setVideoEncoder(int video_encoder) throws IllegalStateException; /** * Pass in the file descriptor of the file to be written. Call this after * setOutputFormat() but before prepare(). - * + * * @param fd an open file descriptor to be written into. * @throws IllegalStateException if it is called before * setOutputFormat() or after prepare() - */ + */ public void setOutputFile(FileDescriptor fd) throws IllegalStateException { mPath = null; mFd = fd; } - + /** * Sets the path of the output file to be produced. Call this after * setOutputFormat() but before prepare(). - * + * * @param path The pathname to use. * @throws IllegalStateException if it is called before * setOutputFormat() or after prepare() - */ + */ public void setOutputFile(String path) throws IllegalStateException { mFd = null; mPath = path; } - + // native implementation private native void _setOutputFile(FileDescriptor fd, long offset, long length) throws IllegalStateException, IOException; @@ -299,7 +318,7 @@ public class MediaRecorder * Prepares the recorder to begin capturing and encoding data. This method * must be called after setting up the desired audio and video sources, * encoders, file format, etc., but before start(). - * + * * @throws IllegalStateException if it is called after * start() or before setOutputFormat(). * @throws IOException if prepare fails otherwise. @@ -307,8 +326,12 @@ public class MediaRecorder public void prepare() throws IllegalStateException, IOException { if (mPath != null) { - FileOutputStream f = new FileOutputStream(mPath); - _setOutputFile(f.getFD(), 0, 0); + FileOutputStream fos = new FileOutputStream(mPath); + try { + _setOutputFile(fos.getFD(), 0, 0); + } finally { + fos.close(); + } } else if (mFd != null) { _setOutputFile(mFd, 0, 0); } else { @@ -318,9 +341,9 @@ public class MediaRecorder } /** - * Begins capturing and encoding data to the file specified with + * Begins capturing and encoding data to the file specified with * setOutputFile(). Call this after prepare(). - * + * * @throws IllegalStateException if it is called before * prepare(). */ @@ -329,7 +352,7 @@ public class MediaRecorder /** * Stops recording. Call this after start(). Once recording is stopped, * you will have to configure it again as if it has just been constructed. - * + * * @throws IllegalStateException if it is called before start() */ public native void stop() throws IllegalStateException; @@ -339,19 +362,118 @@ public class MediaRecorder * this method, you will have to configure it again as if it had just been * constructed. */ - public native void reset(); - + public void reset() { + native_reset(); + + // make sure none of the listeners get called anymore + mEventHandler.removeCallbacksAndMessages(null); + } + + private native void native_reset(); + /** - * Returns the maximum absolute amplitude that was sampled since the last + * Returns the maximum absolute amplitude that was sampled since the last * call to this method. Call this only after the setAudioSource(). - * - * @return the maximum absolute amplitude measured since the last call, or + * + * @return the maximum absolute amplitude measured since the last call, or * 0 when called for the first time * @throws IllegalStateException if it is called before * the audio source has been set. */ public native int getMaxAmplitude() throws IllegalStateException; - + + /* Do not change this value without updating its counterpart + * in include/media/mediarecorder.h! + */ + /** Unspecified media recorder error. + * @see android.media.MediaRecorder.OnErrorListener + */ + public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1; + + /** + * Interface definition for a callback to be invoked when an error + * occurs while recording. + */ + public interface OnErrorListener + { + /** + * Called when an error occurs while recording. + * + * @param mr the MediaRecorder that encountered the error + * @param what the type of error that has occurred: + * <ul> + * <li>{@link #MEDIA_RECORDER_ERROR_UNKNOWN} + * </ul> + * @param extra an extra code, specific to the error type + */ + void onError(MediaRecorder mr, int what, int extra); + } + + /** + * Register a callback to be invoked when an error occurs while + * recording. + * + * @param l the callback that will be run + */ + public void setOnErrorListener(OnErrorListener l) + { + mOnErrorListener = l; + } + + private class EventHandler extends Handler + { + private MediaRecorder mMediaRecorder; + + public EventHandler(MediaRecorder mr, Looper looper) { + super(looper); + mMediaRecorder = mr; + } + + /* Do not change this value without updating its counterpart + * in include/media/mediarecorder.h! + */ + private static final int MEDIA_RECORDER_EVENT_ERROR = 1; + + @Override + public void handleMessage(Message msg) { + if (mMediaRecorder.mNativeContext == 0) { + Log.w(TAG, "mediarecorder went away with unhandled events"); + return; + } + switch(msg.what) { + case MEDIA_RECORDER_EVENT_ERROR: + if (mOnErrorListener != null) + mOnErrorListener.onError(mMediaRecorder, msg.arg1, msg.arg2); + + return; + + default: + Log.e(TAG, "Unknown message type " + msg.what); + return; + } + } + } + + /** + * 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. + * We use a weak reference to the original MediaRecorder object so that the native + * code is safe from the object disappearing from underneath it. (This is + * the cookie passed to native_setup().) + */ + private static void postEventFromNative(Object mediarecorder_ref, + int what, int arg1, int arg2, Object obj) + { + MediaRecorder mr = (MediaRecorder)((WeakReference)mediarecorder_ref).get(); + if (mr == null) { + return; + } + + if (mr.mEventHandler != null) { + Message m = mr.mEventHandler.obtainMessage(what, arg1, arg2, obj); + mr.mEventHandler.sendMessage(m); + } + } /** * Releases resources associated with this MediaRecorder object. @@ -360,10 +482,10 @@ public class MediaRecorder */ public native void release(); - private native final void native_setup() throws IllegalStateException; - + private native final void native_setup(Object mediarecorder_this) throws IllegalStateException; + private native final void native_finalize(); - + @Override protected void finalize() { native_finalize(); } } diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java index cfcb5eb..e80d8aa 100644 --- a/media/java/android/media/Ringtone.java +++ b/media/java/android/media/Ringtone.java @@ -164,9 +164,16 @@ public class Ringtone { } else if (mFileDescriptor != null) { mAudio.setDataSource(mFileDescriptor); } else if (mAssetFileDescriptor != null) { - mAudio.setDataSource(mAssetFileDescriptor.getFileDescriptor(), - mAssetFileDescriptor.getStartOffset(), - mAssetFileDescriptor.getLength()); + // Note: using getDeclaredLength so that our behavior is the same + // as previous versions when the content provider is returning + // a full file. + if (mAssetFileDescriptor.getDeclaredLength() < 0) { + mAudio.setDataSource(mAssetFileDescriptor.getFileDescriptor()); + } else { + mAudio.setDataSource(mAssetFileDescriptor.getFileDescriptor(), + mAssetFileDescriptor.getStartOffset(), + mAssetFileDescriptor.getDeclaredLength()); + } } else { throw new IOException("No data source set."); } diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java index 427f173..000430f 100644 --- a/media/java/android/media/SoundPool.java +++ b/media/java/android/media/SoundPool.java @@ -16,6 +16,7 @@ package android.media; +import android.util.AndroidRuntimeException; import android.util.Log; import java.io.File; import java.io.FileDescriptor; @@ -79,7 +80,11 @@ public class SoundPool public int load(AssetFileDescriptor afd, int priority) { if (afd != null) { - return _load(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength(), priority); + long len = afd.getLength(); + if (len < 0) { + throw new AndroidRuntimeException("no length for fd"); + } + return _load(afd.getFileDescriptor(), afd.getStartOffset(), len, priority); } else { return 0; } |