Как изменить выходной файл медиарекордера, не останавливая медиазапись

У меня есть требование в моем проекте, где видео записывается и загружается на сервер, но поскольку мобильные сети ненадежны, вначале то, что я решил делать, было каждые 30 секунд

  • остановить рекордер

  • reset состояние регистратора

  • извлекает файл, записанный записывающим устройством, и загружает (данные с несколькими формами) в другой поток.

  • изменить outfile рекордера на новый файл на основе хеша текущей метки времени.

  • повторять процесс каждые 30 секунд

Выполнение этого полностью соответствует моим потребностям, так как каждый размер видеофайлов 30 сек составляет не более 1 МБ, и загрузка выполняется плавно.

Но проблема, с которой я сталкиваюсь, заключается в том, что каждый раз, когда медиа-рекордер останавливается и запускается снова, происходит задержка около 500 мс, поэтому видео, которое я получаю на сервере, имеет эти перерывы в 500 мс каждые 30 секунд, что очень плохо для моего текущего ситуация, поэтому я подумал, можно ли просто изменить файл, который записывает рекордер на лету?

Соответствующий код:

GenericCallback onTickListener = new GenericCallback() {
        @Override
        public void execute(Object data) {
            int timeElapsedInSecs = (int) data;
            if (timeElapsedInSecs % pingIntervalInSecs == 0) {
                new API(getActivity().getApplicationContext()).pingServer(objInterviewQuestion.getCurrentAccessToken(),
                        new NetworkCallback() {
                    @Override
                    public void execute(int response_code, Object result) {
                        // TODO: HANDLE callback
                    }
                });
            }
            if (timeElapsedInSecs % uploadIntervalInSecs == 0 && timeElapsedInSecs < maxTimeInSeconds) {
                if (timeElapsedInSecs / uploadIntervalInSecs >= 1) {
                    if(stopAndResetRecorder()) {
                        openConnectionToUploadQueue();
                        uploadQueue.add(
                                new InterviewAnswer(0,
                                        objInterviewQuestion.getQid(),
                                        objInterviewQuestion.getAvf(),
                                        objInterviewQuestion.getNext(),
                                        objInterviewQuestion.getCurrentAccessToken()));
                        objInterviewQuestion.setAvf(MiscHelpers.getOutputMediaFilePath());
                        initializeAndStartRecording();
                    }
                }
            }
        }
    };

здесь initializeAndStartRecording():

private boolean initializeAndStartRecording() {
        Log.i("INFO", "initializeAndStartRecording");
        if (mCamera != null) {
            try {

                mMediaRecorder = CameraHelpers.initializeRecorder(mCamera,
                        mCameraPreview,
                        desiredVideoWidth,
                        desiredVideoHeight);

                mMediaRecorder.setOutputFile(objInterviewQuestion.getAvf());
                mMediaRecorder.prepare();
                mMediaRecorder.start();
                img_recording.setVisibility(View.VISIBLE);

                is_recording = true;
                return true;
            } catch (Exception ex) {
                MiscHelpers.showMsg(getActivity(),
                        getString(R.string.err_cannot_start_recorder),
                        AppMsg.STYLE_ALERT);
                return false;
            }


        } else {
            MiscHelpers.showMsg(getActivity(), getString(R.string.err_camera_not_available),
                    AppMsg.STYLE_ALERT);
            return false;
        }
    }

Вот stopAndResetRecorder:

boolean stopAndResetRecorder() {
        boolean success = false;
        try {
            if (mMediaRecorder != null) {
                try {
                    //stop recording
                    mMediaRecorder.stop();
                    mMediaRecorder.reset();
                    mMediaRecorder.release();
                    mMediaRecorder = null;
                    Log.d("MediaRecorder", "Recorder Stopped");
                    success = true;
                } catch (Exception ex) {
                    if(ex != null && ex.getMessage()!=null && ex.getMessage().isEmpty()){
                        Crashlytics.log(Log.ERROR, "Failed to stop MediaRecorder", ex.getMessage());
                        Crashlytics.logException(ex);
                    }
                    success = false;
                } finally {
                    mMediaRecorder = null;
                    is_recording = false;
                    is_recording = false;
                }
            }
        } catch (Exception ex) {
            success = false;
        }
        Log.d("MediaRecorder", "Success = " + String.valueOf(success));
        return success;
    }

Ответ 1

Вы можете немного ускорить его, не называя метод release() и все остальное уничтожение, которое вы делаете в stopAndResetRecorder() (см. документация для конечного автомата MediaRecorder). Вам также не нужно вызывать как stop(), так и reset().

Вместо этого вы можете иметь промежуточную функцию resetRecorder(), которая только что выполнила reset(), затем вызовите initializeAndStartRecording(). Когда вы закончите всю запись, вы можете вызвать stopRecorder(), который выполнит уничтожение вашего mMediaRecorder.

Как я уже сказал, это поможет вам сэкономить некоторое время, но есть ли дополнительные накладные расходы, которые вы в настоящее время имеете для уничтожения и повторной инициализации MediaRecorder, является значительной частью задержки, которую я не знаю. Попробуйте, и если это не решит вашу проблему, мне было бы интересно узнать, сколько времени она не делала/не сохраняла.

Ответ 2

Мне кажется, что setOutputFile вызывает собственный метод, относящийся к источнику MediaRecorder, поэтому я не думаю, что есть простой способ написать в отдельные файлы одновременно.

Как насчет того, чтобы загрузить его в один кусок в конце, но разрешить пользователю делать что-либо после запуска процесса загрузки? Тогда пользователь не заметит, сколько времени занимает загрузка, и вы можете оповестить его позже, когда загрузка прошла успешно/не удалось.

[Изменить:] Попробуйте передать потоки на сервер, где сервер выполняет механизм блокировки для разделения файлов. Здесь вы можете иметь краткое объяснение, как это сделать.

Ответ 3

По-видимому MediaRecorder.setOutputFile() также принимает FileDescriptor.

Итак, если вы программировали на низком уровне (JNI), вы могли бы представить поток входных данных процесса в качестве файлового дескриптора и, в свою очередь, если бы этот процесс записывался в разные файлы по желанию. Но это будет связано с управлением этим родным "маршрутизатором" процессом из java.

К сожалению, на стороне API java вам не повезло.