diff --git a/api/current.txt b/api/current.txt index b017f9d2f5b94..4da92f52314e4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -21784,6 +21784,8 @@ package android.media { method public void setLocation(float, float); method public void setMaxDuration(int) throws java.lang.IllegalArgumentException; method public void setMaxFileSize(long) throws java.lang.IllegalArgumentException; + method public void setNextOutputFile(java.io.FileDescriptor) throws java.io.IOException, java.lang.IllegalStateException; + method public void setNextOutputFile(java.lang.String) throws java.io.IOException, java.lang.IllegalStateException; method public void setOnErrorListener(android.media.MediaRecorder.OnErrorListener); method public void setOnInfoListener(android.media.MediaRecorder.OnInfoListener); method public void setOrientationHint(int); @@ -21802,7 +21804,9 @@ package android.media { field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64 field public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1; // 0x1 field public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800; // 0x320 + field public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING = 802; // 0x322 field public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED = 801; // 0x321 + field public static final int MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED = 803; // 0x323 field public static final int MEDIA_RECORDER_INFO_UNKNOWN = 1; // 0x1 } diff --git a/api/system-current.txt b/api/system-current.txt index a5534deebd93e..067fcf961b43b 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -23322,6 +23322,8 @@ package android.media { method public void setLocation(float, float); method public void setMaxDuration(int) throws java.lang.IllegalArgumentException; method public void setMaxFileSize(long) throws java.lang.IllegalArgumentException; + method public void setNextOutputFile(java.io.FileDescriptor) throws java.io.IOException, java.lang.IllegalStateException; + method public void setNextOutputFile(java.lang.String) throws java.io.IOException, java.lang.IllegalStateException; method public void setOnErrorListener(android.media.MediaRecorder.OnErrorListener); method public void setOnInfoListener(android.media.MediaRecorder.OnInfoListener); method public void setOrientationHint(int); @@ -23340,7 +23342,9 @@ package android.media { field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64 field public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1; // 0x1 field public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800; // 0x320 + field public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING = 802; // 0x322 field public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED = 801; // 0x321 + field public static final int MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED = 803; // 0x323 field public static final int MEDIA_RECORDER_INFO_UNKNOWN = 1; // 0x1 } diff --git a/api/test-current.txt b/api/test-current.txt index 5db134a289646..0817a02f48597 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -21871,6 +21871,8 @@ package android.media { method public void setLocation(float, float); method public void setMaxDuration(int) throws java.lang.IllegalArgumentException; method public void setMaxFileSize(long) throws java.lang.IllegalArgumentException; + method public void setNextOutputFile(java.io.FileDescriptor) throws java.io.IOException, java.lang.IllegalStateException; + method public void setNextOutputFile(java.lang.String) throws java.io.IOException, java.lang.IllegalStateException; method public void setOnErrorListener(android.media.MediaRecorder.OnErrorListener); method public void setOnInfoListener(android.media.MediaRecorder.OnInfoListener); method public void setOrientationHint(int); @@ -21889,7 +21891,9 @@ package android.media { field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64 field public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1; // 0x1 field public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800; // 0x320 + field public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING = 802; // 0x322 field public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED = 801; // 0x321 + field public static final int MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED = 803; // 0x323 field public static final int MEDIA_RECORDER_INFO_UNKNOWN = 1; // 0x1 } diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 64576eca25169..e62dfaab26b5d 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -800,6 +800,28 @@ public class MediaRecorder mFd = fd; } + /** + * Sets the next output file descriptor to be used when the maximum filesize is reached + * on the prior output {@link #setOutputFile} or {@link #setNextOutputFile}). File descriptor + * must be seekable and in read-write mode. After setting the next output file, application + * should not use the file referenced by this file descriptor until {@link #stop}. Application + * must call this after receiving on the {@link android.media.MediaRecorder.OnInfoListener} a + * "what" code of {@link #MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING} and before receiving + * a "what" code of {@link #MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED}. The file is not used + * until switching to that output. Application will receive + * {@link #MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED} when the next output file is used. + * Application will not be able to set a new output file if the previous one has not been used. + * Application is responsible for cleaning up unused files after {@link #stop} is called. + * + * @param fd an open file descriptor to be written into. + * @throws IllegalStateException if it is called before prepare(). + * @throws IOException if setNextOutputFile fails otherwise. + */ + public void setNextOutputFile(FileDescriptor fd) throws IllegalStateException, IOException + { + _setNextOutputFile(fd); + } + /** * Sets the path of the output file to be produced. Call this after * setOutputFormat() but before prepare(). @@ -814,9 +836,38 @@ public class MediaRecorder mPath = path; } + /** + * Sets the next output file path to be used when the maximum filesize is reached + * on the prior output {@link #setOutputFile} or {@link #setNextOutputFile}). File should + * be seekable. After setting the next output file, application should not use the file + * referenced by this file descriptor until {@link #stop}. Application must call this + * after receiving on the {@link android.media.MediaRecorder.OnInfoListener} a "what" code of + * {@link #MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING} and before receiving a "what" code of + * {@link #MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED}. The file is not used until switching to + * that output. Application will receive {@link #MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED} + * when the next output file is used. Application will not be able to set a new output file if + * the previous one has not been used. Application is responsible for cleaning up unused files + * after {@link #stop} is called. + * + * @param path The pathname to use. + * @throws IllegalStateException if it is called before prepare(). + * @throws IOException if setNextOutputFile fails otherwise. + */ + public void setNextOutputFile(String path) throws IllegalStateException, IOException + { + if (path != null) { + RandomAccessFile file = new RandomAccessFile(path, "rws"); + try { + _setNextOutputFile(file.getFD()); + } finally { + file.close(); + } + } + } + // native implementation - private native void _setOutputFile(FileDescriptor fd, long offset, long length) - throws IllegalStateException, IOException; + private native void _setOutputFile(FileDescriptor fd) throws IllegalStateException, IOException; + private native void _setNextOutputFile(FileDescriptor fd) throws IllegalStateException, IOException; private native void _prepare() throws IllegalStateException, IOException; /** @@ -833,12 +884,12 @@ public class MediaRecorder if (mPath != null) { RandomAccessFile file = new RandomAccessFile(mPath, "rws"); try { - _setOutputFile(file.getFD(), 0, 0); + _setOutputFile(file.getFD()); } finally { file.close(); } } else if (mFd != null) { - _setOutputFile(mFd, 0, 0); + _setOutputFile(mFd); } else { throw new IOException("No valid output file"); } @@ -980,9 +1031,26 @@ public class MediaRecorder */ public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800; /** A maximum filesize had been setup and has now been reached. + * Note: This event will not be sent if application already set + * next output file through {@link #setNextOutputFile}. * @see android.media.MediaRecorder.OnInfoListener */ public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED = 801; + /** A maximum filesize had been setup and current recorded file size + * has reached 90% of the limit. This is sent once per file upon + * reaching/passing the 90% limit. To continue the recording, applicaiton + * should use {@link #setNextOutputFile} to set the next output file. + * Otherwise, recording will stop when reaching maximum file size. + * @see android.media.MediaRecorder.OnInfoListener + */ + public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING = 802; + /** A maximum filesize had been reached and MediaRecorder has switched + * output to a new file set by application {@link #setNextOutputFile}. + * For best practice, application should use this event to keep track + * of whether the file previously set has been used or not. + * @see android.media.MediaRecorder.OnInfoListener + */ + public static final int MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED = 803; /** informational events for individual tracks, for testing purpose. * The track informational event usually contains two parts in the ext1 diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp index 6c79ab790a3de..7c509d297dbfc 100644 --- a/media/jni/android_media_MediaRecorder.cpp +++ b/media/jni/android_media_MediaRecorder.cpp @@ -290,7 +290,7 @@ android_media_MediaRecorder_setParameter(JNIEnv *env, jobject thiz, jstring para } static void -android_media_MediaRecorder_setOutputFileFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length) +android_media_MediaRecorder_setOutputFileFD(JNIEnv *env, jobject thiz, jobject fileDescriptor) { ALOGV("setOutputFile"); if (fileDescriptor == NULL) { @@ -303,7 +303,25 @@ android_media_MediaRecorder_setOutputFileFD(JNIEnv *env, jobject thiz, jobject f jniThrowException(env, "java/lang/IllegalStateException", NULL); return; } - status_t opStatus = mr->setOutputFile(fd, offset, length); + status_t opStatus = mr->setOutputFile(fd); + process_media_recorder_call(env, opStatus, "java/io/IOException", "setOutputFile failed."); +} + +static void +android_media_MediaRecorder_setNextOutputFileFD(JNIEnv *env, jobject thiz, jobject fileDescriptor) +{ + ALOGV("setNextOutputFile"); + if (fileDescriptor == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + sp mr = getMediaRecorder(env, thiz); + if (mr == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + status_t opStatus = mr->setNextOutputFile(fd); process_media_recorder_call(env, opStatus, "java/io/IOException", "setOutputFile failed."); } @@ -617,7 +635,8 @@ static const JNINativeMethod gMethods[] = { {"setVideoEncoder", "(I)V", (void *)android_media_MediaRecorder_setVideoEncoder}, {"setAudioEncoder", "(I)V", (void *)android_media_MediaRecorder_setAudioEncoder}, {"setParameter", "(Ljava/lang/String;)V", (void *)android_media_MediaRecorder_setParameter}, - {"_setOutputFile", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaRecorder_setOutputFileFD}, + {"_setOutputFile", "(Ljava/io/FileDescriptor;)V", (void *)android_media_MediaRecorder_setOutputFileFD}, + {"_setNextOutputFile", "(Ljava/io/FileDescriptor;)V", (void *)android_media_MediaRecorder_setNextOutputFileFD}, {"setVideoSize", "(II)V", (void *)android_media_MediaRecorder_setVideoSize}, {"setVideoFrameRate", "(I)V", (void *)android_media_MediaRecorder_setVideoFrameRate}, {"setMaxDuration", "(I)V", (void *)android_media_MediaRecorder_setMaxDuration},