Как изменить выходной файл медиарекордера, не останавливая медиазапись
У меня есть требование в моем проекте, где видео записывается и загружается на сервер, но поскольку мобильные сети ненадежны, вначале то, что я решил делать, было каждые 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 вам не повезло.