Я реализую приложение для записи видео, используя Camera2 API. Я использовал примеры Google для записи видео. Однако на некоторых устройствах, таких как Samsung J5, J6, возникает проблема рассинхронизации звука и видео. Я изменил MediaRecorder.AudioEncoder, MediaRecorder.VideoEncoder, VideoEncodingBitrate
, но мне это не помогло. Как справиться с проблемой синхронизации звука и видео?
Как исправить проблему рассинхронизации видео и аудио при записи видео на Android с помощью Camera API 2?
Ответы (1)
Я нашел решение из этой статьи а>. Возможно, это не лучшее решение, но оно работает. Для решения проблемы рассинхронизации используется библиотека mp4parser. Во-первых, рабочий процесс записи видео такой же, как и при обычной записи видео, но для проблемных устройств есть дополнительный шаг. Ниже я приведу свой ответ. Первый шаг записи видео — это подготовка MediaRecorder, чтобы сократить ответ, я пропущу некоторые шаги.
private void setupMediaRecorder(){
mMediaRecorder = new MediaRecorder();
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setOutputFile(mVideoFilePath);
mMediaRecorder.setVideoSize(mVideoSize.getWidth(), mVideoSize.getHeight());
CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P);
mMediaRecorder.setVideoFrameRate(profile.videoFrameRate);
mMediaRecorder.setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight);
mMediaRecorder.setVideoEncodingBitRate(profile.videoBitRate);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mMediaRecorder.setAudioChannels(2);
mMediaRecorder.setAudioEncodingBitRate(profile.audioBitRate);
mMediaRecorder.setAudioSamplingRate(profile.audioSampleRate);
int rotation = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
switch (getRotation()) {
case SENSOR_ORIENTATION_DEFAULT_DEGREES:
mMediaRecorder.setOrientationHint(ORIENTATIONS.get(rotation));
break;
case SENSOR_ORIENTATION_INVERSE_DEGREES:
mMediaRecorder.setOrientationHint(INVERSE_ORIENTATIONS.get(rotation));
break;
case SENSOR_ORIENTATION_DEFAULT_LAND_DEGREES:
mMediaRecorder.setOrientationHint((ORIENTATIONS.get(rotation)+270)%360);
break;
case SENSOR_ORIENTATION_INVERSE_LAND_DEGREES:
mMediaRecorder.setOrientationHint((INVERSE_ORIENTATIONS.get(rotation)+270)%360);
break;
}
try{
mMediaRecorder.prepare();
}catch (IllegalStateException | IOException exc){
exc.printStackTrace();
}
}
Чтобы остановить запись видео
public void stopVideo(){
//Stop recording
try {
mMediaRecorder.stop();
mMediaRecorder.release();
parseVideo(mVideoFilePath);
}catch (RuntimeException e){
e.printStackTrace();
}
closePreviewSession();
createCameraPreviewSession();
if (mListener!=null){
mListener.onPrepareRecorder();
}
}
Последний и важный шаг — вызов функции parseVideo.
private String parseVideo(String mFilePath) {
try {
DataSource channel = new FileDataSourceImpl(mFilePath);
IsoFile isoFile = new IsoFile(channel);
List<TrackBox> trackBoxes = isoFile.getMovieBox().getBoxes(TrackBox.class);
boolean isError = false;
for (TrackBox trackBox : trackBoxes) {
TimeToSampleBox.Entry firstEntry = trackBox.getMediaBox().getMediaInformationBox().getSampleTableBox().getTimeToSampleBox().getEntries().get(0);
// Detect if first sample is a problem and fix it in isoFile
// This is a hack. The audio deltas are 1024 for my files, and video deltas about 3000
// 10000 seems sufficient since for 30 fps the normal delta is about 3000
if (firstEntry.getDelta() > 10000) {
isError = true;
firstEntry.setDelta(3000);
}
}
if (isError) {
Movie movie = new Movie();
for (TrackBox trackBox : trackBoxes) {
movie.addTrack(new Mp4TrackImpl(channel.toString() + "[" + trackBox.getTrackHeaderBox().getTrackId() + "]", trackBox));
}
movie.setMatrix(isoFile.getMovieBox().getMovieHeaderBox().getMatrix());
Container out = new DefaultMp4Builder().build(movie);
//delete file first!
FileChannel fc = new RandomAccessFile(mPostProcessingFilePath, "rw").getChannel();
out.writeContainer(fc);
fc.close();
deleteFile(mVideoFilePath);
mListener.onVideoStop(mPostProcessingFilePath);
return mPostProcessingFilePath;
}
mListener.onVideoStop(mVideoFilePath);
return mFilePath;
}catch (IOException e){
mListener.onVideoError("");
return mPostProcessingFilePath;
}
}
В функции парсера проверяет значение дельты, если оно больше 10000, обрабатывает записанное видео и возвращает результат. В случае, если значение дельты меньше 10000, он просто возвращает видео без обработки. Дополнительные сведения см. по ссылке а>. Надеюсь, это поможет вам.