Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e176ee12 authored by Hangyu Kuang's avatar Hangyu Kuang
Browse files

media: Add new API to set next output file.

Only support recording to MP4 file now. When the recorded file is approcaching
file size limit, application will receive
MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING callback from mediarecorder.
Application could use setNextOutputFile to set the next output file before or
after receiving this callback. Upon reaching filesize limit, recorder will swap
the output file internally and notify application with
MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED.

Test: Hack camera app to record more than filesize limit.
Bug: 28150626
Change-Id: I2daf8f798fe3631d6b7ef48ebea3a64ab4566f2d
parent 11fa7f3d
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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
  }
+4 −0
Original line number Diff line number Diff line
@@ -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
  }
+4 −0
Original line number Diff line number Diff line
@@ -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
  }
+72 −4
Original line number Diff line number Diff line
@@ -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
+22 −3
Original line number Diff line number Diff line
@@ -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<MediaRecorder> 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},