You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在flutter_webrtc中实现移动端CCTV直播视频录制功能?

基于flutter_webrtc实现CCTV直播录制及替代方案

一、用flutter_webrtc实现录制的具体步骤

你已经通过flutter_webrtc拿到了CCTV直播的MediaStream,接下来可以通过结合原生MediaRecorder的方式实现录制,步骤如下:

  1. 传递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');
    }
    
  2. 原生端实现录制逻辑

    • 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?) {
              // 处理录制完成后的逻辑
          }
      }
      
  3. 权限与异常处理

    • Android需要申请RECORD_AUDIO、WRITE_EXTERNAL_STORAGE(Android 13+用READ_MEDIA_VIDEO/READ_MEDIA_AUDIO)权限;iOS需要NSCameraUsageDescription、NSMicrophoneUsageDescription权限。
    • 录制过程中要处理设备不支持的编码格式、磁盘空间不足等异常。

二、其他可行方案

  1. 第三方录制插件
    直接使用成熟的录制插件,比如record或flutter_video_recorder,这类插件封装了原生录制逻辑。如果是录制屏幕上的RTCVideoView,可以用屏幕录制权限捕获整个屏幕或指定区域,但要注意WebRTC视图的渲染是否支持屏幕捕获。

  2. 手动编码视频帧
    监听MediaStream的视频帧回调,将每一帧转换为原始数据,用dart的视频编码库(如video_encoder)手动编码为视频文件。这种方式灵活性极高,但需要处理帧时序、编码性能优化,适合对录制质量有特殊要求的场景。

  3. 服务器端录制
    如果CCTV直播流是通过中转服务器分发的,直接在服务器端完成录制,录制完成后将文件推送给客户端。这种方式完全避免客户端的性能开销,但需要服务器支持流录制功能(比如用FFmpeg对RTSP/RTMP流进行录制)。

内容的提问来源于stack exchange,提问作者viki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 19:23:20