Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.media;
     18 
     19 import android.media.CamcorderProfile;
     20 import android.hardware.Camera;
     21 import android.os.Handler;
     22 import android.os.Looper;
     23 import android.os.Message;
     24 import android.util.Log;
     25 import android.view.Surface;
     26 import java.io.IOException;
     27 import java.io.FileNotFoundException;
     28 import java.io.FileOutputStream;
     29 import java.io.FileDescriptor;
     30 import java.lang.ref.WeakReference;
     31 
     32 /**
     33  * Used to record audio and video. The recording control is based on a
     34  * simple state machine (see below).
     35  *
     36  * <p><img src="{@docRoot}images/mediarecorder_state_diagram.gif" border="0" />
     37  * </p>
     38  *
     39  * <p>A common case of using MediaRecorder to record audio works as follows:
     40  *
     41  * <pre>MediaRecorder recorder = new MediaRecorder();
     42  * recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
     43  * recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
     44  * recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
     45  * recorder.setOutputFile(PATH_NAME);
     46  * recorder.prepare();
     47  * recorder.start();   // Recording is now started
     48  * ...
     49  * recorder.stop();
     50  * recorder.reset();   // You can reuse the object by going back to setAudioSource() step
     51  * recorder.release(); // Now the object cannot be reused
     52  * </pre>
     53  *
     54  * <p>See the <a href="{@docRoot}guide/topics/media/index.html">Audio and Video</a>
     55  * documentation for additional help with using MediaRecorder.
     56  * <p>Note: Currently, MediaRecorder does not work on the emulator.
     57  */
     58 public class MediaRecorder
     59 {
     60     static {
     61         System.loadLibrary("media_jni");
     62         native_init();
     63     }
     64     private final static String TAG = "MediaRecorder";
     65 
     66     // The two fields below are accessed by native methods
     67     @SuppressWarnings("unused")
     68     private int mNativeContext;
     69 
     70     @SuppressWarnings("unused")
     71     private Surface mSurface;
     72 
     73     private String mPath;
     74     private FileDescriptor mFd;
     75     private EventHandler mEventHandler;
     76     private OnErrorListener mOnErrorListener;
     77     private OnInfoListener mOnInfoListener;
     78 
     79     /**
     80      * Default constructor.
     81      */
     82     public MediaRecorder() {
     83 
     84         Looper looper;
     85         if ((looper = Looper.myLooper()) != null) {
     86             mEventHandler = new EventHandler(this, looper);
     87         } else if ((looper = Looper.getMainLooper()) != null) {
     88             mEventHandler = new EventHandler(this, looper);
     89         } else {
     90             mEventHandler = null;
     91         }
     92 
     93         /* Native setup requires a weak reference to our object.
     94          * It's easier to create it here than in C++.
     95          */
     96         native_setup(new WeakReference<MediaRecorder>(this));
     97     }
     98 
     99     /**
    100      * Sets a Camera to use for recording. Use this function to switch
    101      * quickly between preview and capture mode without a teardown of
    102      * the camera object. Must call before prepare().
    103      *
    104      * @param c the Camera to use for recording
    105      */
    106     public native void setCamera(Camera c);
    107 
    108     /**
    109      * Sets a Surface to show a preview of recorded media (video). Calls this
    110      * before prepare() to make sure that the desirable preview display is
    111      * set.
    112      *
    113      * @param sv the Surface to use for the preview
    114      */
    115     public void setPreviewDisplay(Surface sv) {
    116         mSurface = sv;
    117     }
    118 
    119     /**
    120      * Defines the audio source. These constants are used with
    121      * {@link MediaRecorder#setAudioSource(int)}.
    122      */
    123     public final class AudioSource {
    124       /* Do not change these values without updating their counterparts
    125        * in include/media/mediarecorder.h!
    126        */
    127         private AudioSource() {}
    128         public static final int DEFAULT = 0;
    129         /** Microphone audio source */
    130         public static final int MIC = 1;
    131 
    132         /** Voice call uplink (Tx) audio source */
    133         public static final int VOICE_UPLINK = 2;
    134 
    135         /** Voice call downlink (Rx) audio source */
    136         public static final int VOICE_DOWNLINK = 3;
    137 
    138         /** Voice call uplink + downlink audio source */
    139         public static final int VOICE_CALL = 4;
    140 
    141         /** Microphone audio source with same orientation as camera if available, the main
    142          *  device microphone otherwise */
    143         public static final int CAMCORDER = 5;
    144 
    145         /** Microphone audio source tuned for voice recognition if available, behaves like
    146          *  {@link #DEFAULT} otherwise. */
    147         public static final int VOICE_RECOGNITION = 6;
    148 
    149         /**
    150          * @hide
    151          * Microphone audio source tuned for voice communications such as VoIP. It
    152          * will for instance take advantage of echo cancellation or automatic gain control
    153          * if available. It otherwise behaves like {@link #DEFAULT} if no voice processing
    154          * is available.
    155          */
    156         public static final int VOICE_COMMUNICATION = 7;
    157     }
    158 
    159     /**
    160      * Defines the video source. These constants are used with
    161      * {@link MediaRecorder#setVideoSource(int)}.
    162      */
    163     public final class VideoSource {
    164       /* Do not change these values without updating their counterparts
    165        * in include/media/mediarecorder.h!
    166        */
    167         private VideoSource() {}
    168         public static final int DEFAULT = 0;
    169         /** Camera video source */
    170         public static final int CAMERA = 1;
    171     }
    172 
    173     /**
    174      * Defines the output format. These constants are used with
    175      * {@link MediaRecorder#setOutputFormat(int)}.
    176      */
    177     public final class OutputFormat {
    178       /* Do not change these values without updating their counterparts
    179        * in include/media/mediarecorder.h!
    180        */
    181         private OutputFormat() {}
    182         public static final int DEFAULT = 0;
    183         /** 3GPP media file format*/
    184         public static final int THREE_GPP = 1;
    185         /** MPEG4 media file format*/
    186         public static final int MPEG_4 = 2;
    187 
    188         /** The following formats are audio only .aac or .amr formats **/
    189         /** @deprecated  Deprecated in favor of AMR_NB */
    190         /** Deprecated in favor of MediaRecorder.OutputFormat.AMR_NB */
    191         /** AMR NB file format */
    192         public static final int RAW_AMR = 3;
    193         /** AMR NB file format */
    194         public static final int AMR_NB = 3;
    195         /** AMR WB file format */
    196         public static final int AMR_WB = 4;
    197         /** @hide AAC ADIF file format */
    198         public static final int AAC_ADIF = 5;
    199         /** @hide AAC ADTS file format */
    200         public static final int AAC_ADTS = 6;
    201 
    202         /** @hide Stream over a socket, limited to a single stream */
    203         public static final int OUTPUT_FORMAT_RTP_AVP = 7;
    204 
    205         /** @hide H.264/AAC data encapsulated in MPEG2/TS */
    206         public static final int OUTPUT_FORMAT_MPEG2TS = 8;
    207     };
    208 
    209     /**
    210      * Defines the audio encoding. These constants are used with
    211      * {@link MediaRecorder#setAudioEncoder(int)}.
    212      */
    213     public final class AudioEncoder {
    214       /* Do not change these values without updating their counterparts
    215        * in include/media/mediarecorder.h!
    216        */
    217         private AudioEncoder() {}
    218         public static final int DEFAULT = 0;
    219         /** AMR (Narrowband) audio codec */
    220         public static final int AMR_NB = 1;
    221         /** AMR (Wideband) audio codec */
    222         public static final int AMR_WB = 2;
    223         /** AAC audio codec */
    224         public static final int AAC = 3;
    225         /** @hide enhanced AAC audio codec */
    226         public static final int AAC_PLUS = 4;
    227         /** @hide enhanced AAC plus audio codec */
    228         public static final int EAAC_PLUS = 5;
    229     }
    230 
    231     /**
    232      * Defines the video encoding. These constants are used with
    233      * {@link MediaRecorder#setVideoEncoder(int)}.
    234      */
    235     public final class VideoEncoder {
    236       /* Do not change these values without updating their counterparts
    237        * in include/media/mediarecorder.h!
    238        */
    239         private VideoEncoder() {}
    240         public static final int DEFAULT = 0;
    241         public static final int H263 = 1;
    242         public static final int H264 = 2;
    243         public static final int MPEG_4_SP = 3;
    244     }
    245 
    246     /**
    247      * Sets the audio source to be used for recording. If this method is not
    248      * called, the output file will not contain an audio track. The source needs
    249      * to be specified before setting recording-parameters or encoders. Call
    250      * this only before setOutputFormat().
    251      *
    252      * @param audio_source the audio source to use
    253      * @throws IllegalStateException if it is called after setOutputFormat()
    254      * @see android.media.MediaRecorder.AudioSource
    255      */
    256     public native void setAudioSource(int audio_source)
    257             throws IllegalStateException;
    258 
    259     /**
    260      * Gets the maximum value for audio sources.
    261      * @see android.media.MediaRecorder.AudioSource
    262      */
    263     public static final int getAudioSourceMax() { return AudioSource.VOICE_RECOGNITION; }
    264 
    265     /**
    266      * Sets the video source to be used for recording. If this method is not
    267      * called, the output file will not contain an video track. The source needs
    268      * to be specified before setting recording-parameters or encoders. Call
    269      * this only before setOutputFormat().
    270      *
    271      * @param video_source the video source to use
    272      * @throws IllegalStateException if it is called after setOutputFormat()
    273      * @see android.media.MediaRecorder.VideoSource
    274      */
    275     public native void setVideoSource(int video_source)
    276             throws IllegalStateException;
    277 
    278     /**
    279      * Uses the settings from a CamcorderProfile object for recording. This method should
    280      * be called after the video AND audio sources are set, and before setOutputFile().
    281      *
    282      * @param profile the CamcorderProfile to use
    283      * @see android.media.CamcorderProfile
    284      */
    285     public void setProfile(CamcorderProfile profile) {
    286         setOutputFormat(profile.fileFormat);
    287         setVideoFrameRate(profile.videoFrameRate);
    288         setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight);
    289         setVideoEncodingBitRate(profile.videoBitRate);
    290         setAudioEncodingBitRate(profile.audioBitRate);
    291         setAudioChannels(profile.audioChannels);
    292         setAudioSamplingRate(profile.audioSampleRate);
    293         setVideoEncoder(profile.videoCodec);
    294         setAudioEncoder(profile.audioCodec);
    295     }
    296 
    297     /**
    298      * Sets the orientation hint for output video playback.
    299      * This method should be called before prepare(). This method will not
    300      * trigger the source video frame to rotate during video recording, but to
    301      * add a composition matrix containing the rotation angle in the output
    302      * video if the output format is OutputFormat.THREE_GPP or
    303      * OutputFormat.MPEG_4 so that a video player can choose the proper
    304      * orientation for playback. Note that some video players may choose
    305      * to ignore the compostion matrix in a video during playback.
    306      *
    307      * @param degrees the angle to be rotated clockwise in degrees.
    308      * The supported angles are 0, 90, 180, and 270 degrees.
    309      * @throws IllegalArgumentException if the angle is not supported.
    310      *
    311      */
    312     public void setOrientationHint(int degrees) {
    313         if (degrees != 0   &&
    314             degrees != 90  &&
    315             degrees != 180 &&
    316             degrees != 270) {
    317             throw new IllegalArgumentException("Unsupported angle: " + degrees);
    318         }
    319         setParameter(String.format("video-param-rotation-angle-degrees=%d", degrees));
    320     }
    321 
    322     /**
    323      * Sets the format of the output file produced during recording. Call this
    324      * after setAudioSource()/setVideoSource() but before prepare().
    325      *
    326      * <p>It is recommended to always use 3GP format when using the H.263
    327      * video encoder and AMR audio encoder. Using an MPEG-4 container format
    328      * may confuse some desktop players.</p>
    329      *
    330      * @param output_format the output format to use. The output format
    331      * needs to be specified before setting recording-parameters or encoders.
    332      * @throws IllegalStateException if it is called after prepare() or before
    333      * setAudioSource()/setVideoSource().
    334      * @see android.media.MediaRecorder.OutputFormat
    335      */
    336     public native void setOutputFormat(int output_format)
    337             throws IllegalStateException;
    338 
    339     /**
    340      * Sets the width and height of the video to be captured.  Must be called
    341      * after setVideoSource(). Call this after setOutFormat() but before
    342      * prepare().
    343      *
    344      * @param width the width of the video to be captured
    345      * @param height the height of the video to be captured
    346      * @throws IllegalStateException if it is called after
    347      * prepare() or before setOutputFormat()
    348      */
    349     public native void setVideoSize(int width, int height)
    350             throws IllegalStateException;
    351 
    352     /**
    353      * Sets the frame rate of the video to be captured.  Must be called
    354      * after setVideoSource(). Call this after setOutFormat() but before
    355      * prepare().
    356      *
    357      * @param rate the number of frames per second of video to capture
    358      * @throws IllegalStateException if it is called after
    359      * prepare() or before setOutputFormat().
    360      *
    361      * NOTE: On some devices that have auto-frame rate, this sets the
    362      * maximum frame rate, not a constant frame rate. Actual frame rate
    363      * will vary according to lighting conditions.
    364      */
    365     public native void setVideoFrameRate(int rate) throws IllegalStateException;
    366 
    367     /**
    368      * Sets the maximum duration (in ms) of the recording session.
    369      * Call this after setOutFormat() but before prepare().
    370      * After recording reaches the specified duration, a notification
    371      * will be sent to the {@link android.media.MediaRecorder.OnInfoListener}
    372      * with a "what" code of {@link #MEDIA_RECORDER_INFO_MAX_DURATION_REACHED}
    373      * and recording will be stopped. Stopping happens asynchronously, there
    374      * is no guarantee that the recorder will have stopped by the time the
    375      * listener is notified.
    376      *
    377      * @param max_duration_ms the maximum duration in ms (if zero or negative, disables the duration limit)
    378      *
    379      */
    380     public native void setMaxDuration(int max_duration_ms) throws IllegalArgumentException;
    381 
    382     /**
    383      * Sets the maximum filesize (in bytes) of the recording session.
    384      * Call this after setOutFormat() but before prepare().
    385      * After recording reaches the specified filesize, a notification
    386      * will be sent to the {@link android.media.MediaRecorder.OnInfoListener}
    387      * with a "what" code of {@link #MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED}
    388      * and recording will be stopped. Stopping happens asynchronously, there
    389      * is no guarantee that the recorder will have stopped by the time the
    390      * listener is notified.
    391      *
    392      * @param max_filesize_bytes the maximum filesize in bytes (if zero or negative, disables the limit)
    393      *
    394      */
    395     public native void setMaxFileSize(long max_filesize_bytes) throws IllegalArgumentException;
    396 
    397     /**
    398      * Sets the audio encoder to be used for recording. If this method is not
    399      * called, the output file will not contain an audio track. Call this after
    400      * setOutputFormat() but before prepare().
    401      *
    402      * @param audio_encoder the audio encoder to use.
    403      * @throws IllegalStateException if it is called before
    404      * setOutputFormat() or after prepare().
    405      * @see android.media.MediaRecorder.AudioEncoder
    406      */
    407     public native void setAudioEncoder(int audio_encoder)
    408             throws IllegalStateException;
    409 
    410     /**
    411      * Sets the video encoder to be used for recording. If this method is not
    412      * called, the output file will not contain an video track. Call this after
    413      * setOutputFormat() and before prepare().
    414      *
    415      * @param video_encoder the video encoder to use.
    416      * @throws IllegalStateException if it is called before
    417      * setOutputFormat() or after prepare()
    418      * @see android.media.MediaRecorder.VideoEncoder
    419      */
    420     public native void setVideoEncoder(int video_encoder)
    421             throws IllegalStateException;
    422 
    423     /**
    424      * Sets the audio sampling rate for recording. Call this method before prepare().
    425      * Prepare() may perform additional checks on the parameter to make sure whether
    426      * the specified audio sampling rate is applicable. The sampling rate really depends
    427      * on the format for the audio recording, as well as the capabilities of the platform.
    428      * For instance, the sampling rate supported by AAC audio coding standard ranges
    429      * from 8 to 96 kHz. Please consult with the related audio coding standard for the
    430      * supported audio sampling rate.
    431      *
    432      * @param samplingRate the sampling rate for audio in samples per second.
    433      */
    434     public void setAudioSamplingRate(int samplingRate) {
    435         if (samplingRate <= 0) {
    436             throw new IllegalArgumentException("Audio sampling rate is not positive");
    437         }
    438         setParameter(String.format("audio-param-sampling-rate=%d", samplingRate));
    439     }
    440 
    441     /**
    442      * Sets the number of audio channels for recording. Call this method before prepare().
    443      * Prepare() may perform additional checks on the parameter to make sure whether the
    444      * specified number of audio channels are applicable.
    445      *
    446      * @param numChannels the number of audio channels. Usually it is either 1 (mono) or 2
    447      * (stereo).
    448      */
    449     public void setAudioChannels(int numChannels) {
    450         if (numChannels <= 0) {
    451             throw new IllegalArgumentException("Number of channels is not positive");
    452         }
    453         setParameter(String.format("audio-param-number-of-channels=%d", numChannels));
    454     }
    455 
    456     /**
    457      * Sets the audio encoding bit rate for recording. Call this method before prepare().
    458      * Prepare() may perform additional checks on the parameter to make sure whether the
    459      * specified bit rate is applicable, and sometimes the passed bitRate will be clipped
    460      * internally to ensure the audio recording can proceed smoothly based on the
    461      * capabilities of the platform.
    462      *
    463      * @param bitRate the audio encoding bit rate in bits per second.
    464      */
    465     public void setAudioEncodingBitRate(int bitRate) {
    466         if (bitRate <= 0) {
    467             throw new IllegalArgumentException("Audio encoding bit rate is not positive");
    468         }
    469         setParameter(String.format("audio-param-encoding-bitrate=%d", bitRate));
    470     }
    471 
    472     /**
    473      * Sets the video encoding bit rate for recording. Call this method before prepare().
    474      * Prepare() may perform additional checks on the parameter to make sure whether the
    475      * specified bit rate is applicable, and sometimes the passed bitRate will be
    476      * clipped internally to ensure the video recording can proceed smoothly based on
    477      * the capabilities of the platform.
    478      *
    479      * @param bitRate the video encoding bit rate in bits per second.
    480      */
    481     public void setVideoEncodingBitRate(int bitRate) {
    482         if (bitRate <= 0) {
    483             throw new IllegalArgumentException("Video encoding bit rate is not positive");
    484         }
    485         setParameter(String.format("video-param-encoding-bitrate=%d", bitRate));
    486     }
    487 
    488     /**
    489      * Pass in the file descriptor of the file to be written. Call this after
    490      * setOutputFormat() but before prepare().
    491      *
    492      * @param fd an open file descriptor to be written into.
    493      * @throws IllegalStateException if it is called before
    494      * setOutputFormat() or after prepare()
    495      */
    496     public void setOutputFile(FileDescriptor fd) throws IllegalStateException
    497     {
    498         mPath = null;
    499         mFd = fd;
    500     }
    501 
    502     /**
    503      * Sets the path of the output file to be produced. Call this after
    504      * setOutputFormat() but before prepare().
    505      *
    506      * @param path The pathname to use.
    507      * @throws IllegalStateException if it is called before
    508      * setOutputFormat() or after prepare()
    509      */
    510     public void setOutputFile(String path) throws IllegalStateException
    511     {
    512         mFd = null;
    513         mPath = path;
    514     }
    515 
    516     // native implementation
    517     private native void _setOutputFile(FileDescriptor fd, long offset, long length)
    518         throws IllegalStateException, IOException;
    519     private native void _prepare() throws IllegalStateException, IOException;
    520 
    521     /**
    522      * Prepares the recorder to begin capturing and encoding data. This method
    523      * must be called after setting up the desired audio and video sources,
    524      * encoders, file format, etc., but before start().
    525      *
    526      * @throws IllegalStateException if it is called after
    527      * start() or before setOutputFormat().
    528      * @throws IOException if prepare fails otherwise.
    529      */
    530     public void prepare() throws IllegalStateException, IOException
    531     {
    532         if (mPath != null) {
    533             FileOutputStream fos = new FileOutputStream(mPath);
    534             try {
    535                 _setOutputFile(fos.getFD(), 0, 0);
    536             } finally {
    537                 fos.close();
    538             }
    539         } else if (mFd != null) {
    540             _setOutputFile(mFd, 0, 0);
    541         } else {
    542             throw new IOException("No valid output file");
    543         }
    544         _prepare();
    545     }
    546 
    547     /**
    548      * Begins capturing and encoding data to the file specified with
    549      * setOutputFile(). Call this after prepare().
    550      *
    551      * @throws IllegalStateException if it is called before
    552      * prepare().
    553      */
    554     public native void start() throws IllegalStateException;
    555 
    556     /**
    557      * Stops recording. Call this after start(). Once recording is stopped,
    558      * you will have to configure it again as if it has just been constructed.
    559      *
    560      * @throws IllegalStateException if it is called before start()
    561      */
    562     public native void stop() throws IllegalStateException;
    563 
    564     /**
    565      * Restarts the MediaRecorder to its idle state. After calling
    566      * this method, you will have to configure it again as if it had just been
    567      * constructed.
    568      */
    569     public void reset() {
    570         native_reset();
    571 
    572         // make sure none of the listeners get called anymore
    573         mEventHandler.removeCallbacksAndMessages(null);
    574     }
    575 
    576     private native void native_reset();
    577 
    578     /**
    579      * Returns the maximum absolute amplitude that was sampled since the last
    580      * call to this method. Call this only after the setAudioSource().
    581      *
    582      * @return the maximum absolute amplitude measured since the last call, or
    583      * 0 when called for the first time
    584      * @throws IllegalStateException if it is called before
    585      * the audio source has been set.
    586      */
    587     public native int getMaxAmplitude() throws IllegalStateException;
    588 
    589     /* Do not change this value without updating its counterpart
    590      * in include/media/mediarecorder.h!
    591      */
    592     /** Unspecified media recorder error.
    593      * @see android.media.MediaRecorder.OnErrorListener
    594      */
    595     public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1;
    596 
    597     /**
    598      * Interface definition for a callback to be invoked when an error
    599      * occurs while recording.
    600      */
    601     public interface OnErrorListener
    602     {
    603         /**
    604          * Called when an error occurs while recording.
    605          *
    606          * @param mr the MediaRecorder that encountered the error
    607          * @param what    the type of error that has occurred:
    608          * <ul>
    609          * <li>{@link #MEDIA_RECORDER_ERROR_UNKNOWN}
    610          * </ul>
    611          * @param extra   an extra code, specific to the error type
    612          */
    613         void onError(MediaRecorder mr, int what, int extra);
    614     }
    615 
    616     /**
    617      * Register a callback to be invoked when an error occurs while
    618      * recording.
    619      *
    620      * @param l the callback that will be run
    621      */
    622     public void setOnErrorListener(OnErrorListener l)
    623     {
    624         mOnErrorListener = l;
    625     }
    626 
    627     /* Do not change these values without updating their counterparts
    628      * in include/media/mediarecorder.h!
    629      */
    630     /** Unspecified media recorder error.
    631      * @see android.media.MediaRecorder.OnInfoListener
    632      */
    633     public static final int MEDIA_RECORDER_INFO_UNKNOWN              = 1;
    634     /** A maximum duration had been setup and has now been reached.
    635      * @see android.media.MediaRecorder.OnInfoListener
    636      */
    637     public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800;
    638     /** A maximum filesize had been setup and has now been reached.
    639      * @see android.media.MediaRecorder.OnInfoListener
    640      */
    641     public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED = 801;
    642 
    643     /**
    644      * Interface definition for a callback to be invoked when an error
    645      * occurs while recording.
    646      */
    647     public interface OnInfoListener
    648     {
    649         /**
    650          * Called when an error occurs while recording.
    651          *
    652          * @param mr the MediaRecorder that encountered the error
    653          * @param what    the type of error that has occurred:
    654          * <ul>
    655          * <li>{@link #MEDIA_RECORDER_INFO_UNKNOWN}
    656          * <li>{@link #MEDIA_RECORDER_INFO_MAX_DURATION_REACHED}
    657          * <li>{@link #MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED}
    658          * </ul>
    659          * @param extra   an extra code, specific to the error type
    660          */
    661         void onInfo(MediaRecorder mr, int what, int extra);
    662     }
    663 
    664     /**
    665      * Register a callback to be invoked when an informational event occurs while
    666      * recording.
    667      *
    668      * @param listener the callback that will be run
    669      */
    670     public void setOnInfoListener(OnInfoListener listener)
    671     {
    672         mOnInfoListener = listener;
    673     }
    674 
    675     private class EventHandler extends Handler
    676     {
    677         private MediaRecorder mMediaRecorder;
    678 
    679         public EventHandler(MediaRecorder mr, Looper looper) {
    680             super(looper);
    681             mMediaRecorder = mr;
    682         }
    683 
    684         /* Do not change these values without updating their counterparts
    685          * in include/media/mediarecorder.h!
    686          */
    687         private static final int MEDIA_RECORDER_EVENT_ERROR = 1;
    688         private static final int MEDIA_RECORDER_EVENT_INFO  = 2;
    689 
    690         @Override
    691         public void handleMessage(Message msg) {
    692             if (mMediaRecorder.mNativeContext == 0) {
    693                 Log.w(TAG, "mediarecorder went away with unhandled events");
    694                 return;
    695             }
    696             switch(msg.what) {
    697             case MEDIA_RECORDER_EVENT_ERROR:
    698                 if (mOnErrorListener != null)
    699                     mOnErrorListener.onError(mMediaRecorder, msg.arg1, msg.arg2);
    700 
    701                 return;
    702 
    703             case MEDIA_RECORDER_EVENT_INFO:
    704                 if (mOnInfoListener != null)
    705                     mOnInfoListener.onInfo(mMediaRecorder, msg.arg1, msg.arg2);
    706 
    707                 return;
    708 
    709             default:
    710                 Log.e(TAG, "Unknown message type " + msg.what);
    711                 return;
    712             }
    713         }
    714     }
    715 
    716     /**
    717      * Called from native code when an interesting event happens.  This method
    718      * just uses the EventHandler system to post the event back to the main app thread.
    719      * We use a weak reference to the original MediaRecorder object so that the native
    720      * code is safe from the object disappearing from underneath it.  (This is
    721      * the cookie passed to native_setup().)
    722      */
    723     private static void postEventFromNative(Object mediarecorder_ref,
    724                                             int what, int arg1, int arg2, Object obj)
    725     {
    726         MediaRecorder mr = (MediaRecorder)((WeakReference)mediarecorder_ref).get();
    727         if (mr == null) {
    728             return;
    729         }
    730 
    731         if (mr.mEventHandler != null) {
    732             Message m = mr.mEventHandler.obtainMessage(what, arg1, arg2, obj);
    733             mr.mEventHandler.sendMessage(m);
    734         }
    735     }
    736 
    737     /**
    738      * Releases resources associated with this MediaRecorder object.
    739      * It is good practice to call this method when you're done
    740      * using the MediaRecorder.
    741      */
    742     public native void release();
    743 
    744     private static native final void native_init();
    745 
    746     private native final void native_setup(Object mediarecorder_this) throws IllegalStateException;
    747 
    748     private native final void native_finalize();
    749 
    750     private native void setParameter(String nameValuePair);
    751 
    752     @Override
    753     protected void finalize() { native_finalize(); }
    754 }
    755