如何在flutter_webrtc中实现移动端CCTV直播视频录制功能?
基于flutter_webrtc实现CCTV直播录制及替代方案
一、用flutter_webrtc实现录制的具体步骤
你已经通过flutter_webrtc拿到了CCTV直播的MediaStream,接下来可以通过结合原生MediaRecorder的方式实现录制,步骤如下:
传递MediaStream到原生层
flutter_webrtc的MediaStream持有原生端的句柄,你可以通过nativeHandle属性获取这个句柄,然后通过MethodChannel传递给Android/iOS原生代码:import 'package:flutter/services.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart'; MediaStream? _liveStream; static const _recorderChannel = MethodChannel('com.your.app/video_recorder'); // 开始录制 Future<void> startRecording() async { if (_liveStream == null) return; final streamHandle = await _liveStream?.nativeHandle; await _recorderChannel.invokeMethod('start', {'streamHandle': streamHandle}); } // 停止录制并获取文件路径 Future<String?> stopRecording() async { return await _recorderChannel.invokeMethod('stop'); }原生端实现录制逻辑
- Android端:拿到MediaStream句柄后,用系统的
MediaRecorder绑定视频/音频轨道进行录制:import org.webrtc.MediaStream; import org.webrtc.VideoTrack; import android.media.MediaRecorder; import android.os.Environment; public class VideoRecorderHandler implements MethodCallHandler { private MediaRecorder recorder; private String outputPath; @Override public void onMethodCall(MethodCall call, Result result) { switch (call.method) { case "start": long streamHandle = call.argument("streamHandle"); MediaStream stream = WebRTCUtils.getMediaStream(streamHandle); VideoTrack videoTrack = stream.videoTracks.get(0); // 初始化MediaRecorder recorder = new MediaRecorder(); outputPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES) + "/cctv_recording_" + System.currentTimeMillis() + ".mp4"; recorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); if (!stream.audioTracks.isEmpty()) { recorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION); } recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); recorder.setOutputFile(outputPath); // 将视频轨道的帧传递给MediaRecorder videoTrack.addSink(frame -> { // 这里需要将VideoFrame渲染到MediaRecorder的Surface,可通过SurfaceTexture实现 // 具体实现需处理Surface的创建与帧的转换 }); try { recorder.prepare(); recorder.start(); result.success(null); } catch (Exception e) { result.error("RECORD_ERROR", e.getMessage(), null); } break; case "stop": if (recorder != null) { try { recorder.stop(); recorder.release(); } catch (Exception e) { // 处理停止异常 } } result.success(outputPath); break; } } } - iOS端:类似地,用
AVCaptureSession捕获MediaStream的视频/音频帧,进行录制:import WebRTC import AVFoundation class VideoRecorder: NSObject, FlutterMethodCallHandler { var captureSession: AVCaptureSession? var movieOutput: AVCaptureMovieFileOutput? var outputURL: URL? func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case "start": guard let streamHandle = call.arguments as? Int64 else { result(FlutterError(code: "INVALID_ARG", message: "Missing stream handle", details: nil)) return } let stream = RTCCameraVideoCapturer.mediaStream(for: streamHandle) guard let videoTrack = stream.videoTracks.first else { result(FlutterError(code: "NO_VIDEO_TRACK", message: "No video track found", details: nil)) return } captureSession = AVCaptureSession() movieOutput = AVCaptureMovieFileOutput() // 添加视频/音频输入到session // 需将WebRTC的VideoTrack转换为AVCaptureInput // 具体实现可通过RTCCVPixelBuffer创建AVCaptureVideoDataOutput captureSession?.addOutput(movieOutput!) outputURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] .appendingPathComponent("cctv_recording_\(Date().timeIntervalSince1970).mp4") movieOutput?.startRecording(to: outputURL!, recordingDelegate: self) captureSession?.startRunning() result(nil) case "stop": movieOutput?.stopRecording() captureSession?.stopRunning() result(outputURL?.path) default: result(FlutterMethodNotImplemented) } } } extension VideoRecorder: AVCaptureFileOutputRecordingDelegate { func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) { // 处理录制完成后的逻辑 } }
- Android端:拿到MediaStream句柄后,用系统的
权限与异常处理
- Android需要申请
RECORD_AUDIO、WRITE_EXTERNAL_STORAGE(Android 13+用READ_MEDIA_VIDEO/READ_MEDIA_AUDIO)权限;iOS需要NSCameraUsageDescription、NSMicrophoneUsageDescription权限。 - 录制过程中要处理设备不支持的编码格式、磁盘空间不足等异常。
- Android需要申请
二、其他可行方案
第三方录制插件
直接使用成熟的录制插件,比如record或flutter_video_recorder,这类插件封装了原生录制逻辑。如果是录制屏幕上的RTCVideoView,可以用屏幕录制权限捕获整个屏幕或指定区域,但要注意WebRTC视图的渲染是否支持屏幕捕获。手动编码视频帧
监听MediaStream的视频帧回调,将每一帧转换为原始数据,用dart的视频编码库(如video_encoder)手动编码为视频文件。这种方式灵活性极高,但需要处理帧时序、编码性能优化,适合对录制质量有特殊要求的场景。服务器端录制
如果CCTV直播流是通过中转服务器分发的,直接在服务器端完成录制,录制完成后将文件推送给客户端。这种方式完全避免客户端的性能开销,但需要服务器支持流录制功能(比如用FFmpeg对RTSP/RTMP流进行录制)。
内容的提问来源于stack exchange,提问作者viki
相关产品推荐
相关产品推荐

