如何基于TextureView播放的RTSP流录制MP4片段?
嘿,刚好我之前做过类似的需求,用Android自带的MediaRecorder就能完美实现你要的录制RTSP流片段并保存为MP4的功能,而且完全能控制开始/停止按钮触发期间的内容。下面是具体的实现方案,我帮你整合到你的现有代码里:
核心思路
我们用MediaRecorder直接绑定RTSP流数据源,通过按钮点击控制它的start()和stop()方法,就能精准录制你想要的时间段内容。如果需要边播边录,也可以把MediaPlayer的播放Surface绑定到MediaRecorder的Surface上,实现同步播放和录制。
步骤1:添加必要权限
先在AndroidManifest.xml里补上权限(Android 10+还要额外加存储权限):
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <!-- Android 10及以上需要这个权限 --> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
注意:Android 6.0+ 必须动态申请WRITE_EXTERNAL_STORAGE和RECORD_AUDIO权限,不然会直接崩溃,记得在代码里做权限申请逻辑哦!
步骤2:添加全局变量
在你的Activity/Fragment里添加几个全局变量,用来管理录制状态和录制实例:
private MediaRecorder mMediaRecorder; private boolean isRecording = false; // 录制文件的输出路径,你可以自己调整存储目录 private String mOutputFilePath;
步骤3:封装MediaRecorder初始化方法
把录制的配置逻辑封装成一个方法,方便调用,这里要特别注意配置顺序不能乱:
private void initMediaRecorder() throws IOException { mMediaRecorder = new MediaRecorder(); // 设置RTSP流数据源 mMediaRecorder.setDataSource(String.valueOf(Uri.parse("rtsp://........./"))); // 配置视频参数 mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); // 分辨率建议和RTSP流实际分辨率一致,避免拉伸或模糊 mMediaRecorder.setVideoSize(1280, 720); // 帧率匹配RTSP流的实际帧率,常见的是25或30 mMediaRecorder.setVideoFrameRate(25); // 配置音频参数(不需要录音频的话可以直接注释掉这部分) mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); mMediaRecorder.setAudioSamplingRate(44100); mMediaRecorder.setAudioChannels(1); // 设置输出文件路径,这里存在系统的Movies目录下 File outputDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES); mOutputFilePath = outputDir.getAbsolutePath() + "/RTSP_Record_" + System.currentTimeMillis() + ".mp4"; mMediaRecorder.setOutputFile(mOutputFilePath); // 准备录制 mMediaRecorder.prepare(); }
步骤4:修改录制按钮的点击事件
替换你原来的recordVideo点击事件逻辑,实现开始/停止录制的切换:
recordVideo.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!isRecording) { // 开始录制 try { // 先停止当前的MediaPlayer播放(如果不需要边播边录的话) if (mMediaPlayer != null && mMediaPlayer.isPlaying()) { mMediaPlayer.stop(); mMediaPlayer.release(); mMediaPlayer = null; } initMediaRecorder(); mMediaRecorder.start(); isRecording = true; recordVideo.setText("停止录制"); // 切换按钮文字 // 【可选】如果需要边播边录,取消下面注释,用MediaRecorder的Surface播放 /* mMediaPlayer = new MediaPlayer(); mMediaPlayer.setDataSource(String.valueOf(Uri.parse("rtsp://........./"))); // 将MediaPlayer的播放Surface绑定到MediaRecorder的Surface mMediaPlayer.setSurface(mMediaRecorder.getSurface()); mMediaPlayer.prepareAsync(); mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { mp.start(); } }); */ } catch (IOException e) { e.printStackTrace(); Toast.makeText(getApplicationContext(), "录制初始化失败", Toast.LENGTH_SHORT).show(); } } else { // 停止录制 if (mMediaRecorder != null) { try { mMediaRecorder.stop(); mMediaRecorder.release(); mMediaRecorder = null; isRecording = false; recordVideo.setText("开始录制"); // 切换按钮文字 Toast.makeText(getApplicationContext(), "录制完成!文件路径:" + mOutputFilePath, Toast.LENGTH_LONG).show(); // 【可选】停止录制后重启MediaPlayer播放 restartMediaPlayer(); } catch (IllegalStateException e) { e.printStackTrace(); Toast.makeText(getApplicationContext(), "录制停止失败", Toast.LENGTH_SHORT).show(); } } } } });
步骤5:添加重启播放的方法(可选)
如果停止录制后需要恢复RTSP流播放,可以加这个方法:
private void restartMediaPlayer() { if (mMediaPlayer == null) { mMediaPlayer = new MediaPlayer(); try { mMediaPlayer.setDataSource(String.valueOf(Uri.parse("rtsp://........./"))); mMediaPlayer.setSurface(surface); mMediaPlayer.setLooping(true); mMediaPlayer.prepareAsync(); mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mediaPlayer) { mediaPlayer.start(); } }); } catch (IOException e) { e.printStackTrace(); } } }
最后:别忘了释放资源
在Activity的onDestroy方法里,一定要释放MediaPlayer和MediaRecorder的资源,避免内存泄漏:
@Override protected void onDestroy() { super.onDestroy(); if (mMediaPlayer != null) { mMediaPlayer.stop(); mMediaPlayer.release(); mMediaPlayer = null; } if (mMediaRecorder != null) { mMediaRecorder.release(); mMediaRecorder = null; } }
内容的提问来源于stack exchange,提问作者Andrea Avesani
相关产品推荐
相关产品推荐

