使用 Android MediaRecorder 暂停和恢复(API 级别 < 24)

Posted

技术标签:

【中文标题】使用 Android MediaRecorder 暂停和恢复(API 级别 < 24)【英文标题】:Pause & Resume with Android MediaRecorder (API level < 24) 【发布时间】:2017-01-12 06:47:30 【问题描述】:

在使用MediaRecorder 时,对于低于 24 的 API 级别,我们没有暂停/恢复。 所以有一种方法可以做到这一点:

    在暂停事件时停止记录器并创建记录文件。 然后在恢复时再次开始录制并创建另一个文件并继续这样做,直到用户按下停止。 最后合并所有文件。

很多人在 SO 上问过这个问题,但无论如何都找不到解决这个问题。人们谈论通过在暂停操作时停止录制并在恢复时重新启动来创建多个媒体文件。所以我的问题是我们如何以编程方式合并/加入所有媒体文件?

注意:在我的例子中是 MPEG4 容器 - m4a 用于音频,mp4 用于视频。

我尝试使用SequenceInputStream 来合并各个生成的记录文件的多个 InputStream。但它总是只产生第一个文件。

代码片段:

Enumeration<InputStream> enu = Collections.enumeration(inputStreams);
        SequenceInputStream sqStream = new SequenceInputStream(enu);
        while ((oneByte = sqStream.read(buffer)) != -1) 
            fileOutputStream.write(buffer, 0, oneByte);

        
        sqStream.close();
        while (enu.hasMoreElements()) 
            InputStream element = enu.nextElement();
            element.close();
        
        fileOutputStream.flush();
        fileOutputStream.close();

【问题讨论】:

Pause and Resume Audio recording in android的可能重复 【参考方案1】:

我可以使用mp4parser 库来解决这个问题。非常感谢这个库的作者 :)

在您的 gradle 文件中添加以下依赖项:

compile 'com.googlecode.mp4parser:isoparser:1.0.2'

解决方案是当用户暂停并重新开始时停止记录器,正如在 *** 中的许多其他答案中已经提到的那样。将生成的所有音频/视频文件存储在一个数组中,并使用以下方法合并所有媒体文件。该示例也取自 mp4parser 库,并根据我的需要进行了一些修改。

public static boolean mergeMediaFiles(boolean isAudio, String sourceFiles[], String targetFile) 
        try 
            String mediaKey = isAudio ? "soun" : "vide";
            List<Movie> listMovies = new ArrayList<>();
            for (String filename : sourceFiles) 
                listMovies.add(MovieCreator.build(filename));
            
            List<Track> listTracks = new LinkedList<>();
            for (Movie movie : listMovies) 
                for (Track track : movie.getTracks()) 
                    if (track.getHandler().equals(mediaKey)) 
                        listTracks.add(track);
                    
                
            
            Movie outputMovie = new Movie();
            if (!listTracks.isEmpty()) 
                outputMovie.addTrack(new AppendTrack(listTracks.toArray(new Track[listTracks.size()])));
            
            Container container = new DefaultMp4Builder().build(outputMovie);
            FileChannel fileChannel = new RandomAccessFile(String.format(targetFile), "rw").getChannel();
            container.writeContainer(fileChannel);
            fileChannel.close();
            return true;
        
        catch (IOException e) 
            Log.e(LOG_TAG, "Error merging media files. exception: "+e.getMessage());
            return false;
        
    

使用标志 isAudio 为音频文件为 true,为视频文件为 false。

【讨论】:

【参考方案2】:

另一个解决方案是与FFmpeg合并

将此行添加到您的应用build.gradle

implementation 'com.writingminds:FFmpegAndroid:0.3.2'

并使用以下代码合并视频。

String textFile = "";
try 
    textFile = getTextFile().getAbsolutePath();
 catch (IOException e) 
    e.printStackTrace();

String[] cmd = new String[]
        "-y",
        "-f",
        "concat",
        "-safe",
        "0",
        "-i",
        textFile,
        "-c",
        "copy",
        "-preset",
        "ultrafast",
        getVideoFilePath();

mergeVideos(cmd);

getTextFile()

 private File getTextFile() throws IOException 
        videoFiles = new String[]firstPath, secondPath, thirdPatch;
        File file = new File(getActivity().getExternalFilesDir(null), System.currentTimeMillis() + "inputFiles.txt");
        FileOutputStream out = new FileOutputStream(file, false);
        PrintWriter writer = new PrintWriter(out);
        StringBuilder builder = new StringBuilder();
        for (String path : videoFiles) 
            if (path != null) 
            builder.append("file ");
            builder.append("\'");
            builder.append(path);
            builder.append("\'\n");
            
        
        builder.deleteCharAt(builder.length() - 1);
        String text = builder.toString();
        writer.print(text);
        writer.close();
        out.close();
        return file;
    

getVideoFilePath()

 private String getVideoFilePath() 
        final File dir = getActivity().getExternalFilesDir(null);
        return (dir == null ? "" : (dir.getAbsolutePath() + "/"))
                + System.currentTimeMillis() + ".mp4";
    

mergeVideos()

private void mergeVideos(String[] cmd) 
        FFmpeg ffmpeg = FFmpeg.getInstance(getActivity());
        try 
            ffmpeg.execute(cmd, new ExecuteBinaryResponseHandler() 

                @Override
                public void onStart() 
                    startTime = System.currentTimeMillis();
                

                @Override
                public void onProgress(String message) 

                

                @Override
                public void onFailure(String message) 
                    Toast.makeText(getActivity(), "Failed " + message, Toast.LENGTH_SHORT).show();
                

                @Override
                public void onSuccess(String message) 

                

                @Override
                public void onFinish() 
                    Toast.makeText(getActivity(), "Videos are merged", Toast.LENGTH_SHORT).show();
                
            );
         catch (FFmpegCommandAlreadyRunningException e) 
            // Handle if FFmpeg is already running
        
    

在合并前运行此代码

private void checkFfmpegSupport() 
        FFmpeg ffmpeg = FFmpeg.getInstance(this);
        try 
            ffmpeg.loadBinary(new LoadBinaryResponseHandler() 

                @Override
                public void onStart() 

                

                @Override
                public void onFailure() 
                    Toast.makeText(VouchActivity.this, "FFmpeg not supported on this device :(", Toast.LENGTH_SHORT).show();
                

                @Override
                public void onSuccess() 

                

                @Override
                public void onFinish() 

                
            );
         catch (FFmpegNotSupportedException e) 
            // Handle if FFmpeg is not supported by device
        
    

【讨论】:

以上是关于使用 Android MediaRecorder 暂停和恢复(API 级别 < 24)的主要内容,如果未能解决你的问题,请参考以下文章

Android-MediaRecorder录像机(视频)

如何使用 mediaRecorder 流式传输 android 的内部音频+屏幕?

Android:使用 MediaRecorder 录制音频 - 文件不播放

使用android的mediaRecorder的文件大小限制?

在 Android 中使用 AudioRecorder/MediaRecorder 录制 FLAC 音频

Android 使用 MediaRecorder 捕获音频。