FFLivekit 0.1.0

FFLivekit 0.1.0

Sudan Suwal 维护。



FFLivekit 0.1.0

  • 作者:
  • Sudan Suwal

概述

这项研究旨在挖掘 FFmpegKit 的潜力,以实现对设备摄像头的 RTMP 或 RTSP 服务器进行直播。FFmpegKit 提供了一个方便的 FFmpeg 封装,使捕获、编码和传输音频和视频流变得容易。

这同时也是为了检查它与现有的直播套餐(如 Haishinkit)的表现如何

功能

  • 从设备的摄像头实时视频和音频流到 RTMP 或 RTSP 服务器。
  • 自定义 FFmpeg 命令以满足特定的直播要求。
  • 无缝集成 AVCaptureSession 以访问摄像头和麦克风。
  • 异步执行以平滑播放在不影响主线程的情况下进行。

动机

我参与了很多直播应用的开发。我一直在使用诸如 HaishinKit 和 LFLiveKit 这样的库。我一直在想,我们是否能在移动应用中通过 ffmpeg 发布直播流。FFmpeg 确实能够将直播流发送到服务器,这是此目的的一个常用工具。FFmpeg 是一个功能强大的多媒体处理工具,能够实时捕获、编码和传输音频和视频。但是我不确定我们是否能在移动端做到这一点。

我正在使用 FFmpeg-kit https://github.com/arthenica/ffmpeg-kit

第一阶段(AVFoundation)

FFmpeg 的 avfoundation 输入格式允许您使用 AVFoundation 从 macOS 和 iOS 设备捕获视频和音频。

ffmpeg -f avfoundation -i "0:0" -c:v libx264 -c:a aac -f flv rtmp://your-rtmp-server/app/stream

尽管它支持将 avfoundation 作为输入设备,但它本身并不提供摄像头流的预览。avfoundation 更专注于捕获和处理音频和视频数据,而不是渲染实时预览。

第二阶段(命名管道)

在我对在 iOS 上的 ffmpeg 上使用命名管道进行研究时,我发现了一个由 dji_flutter 完成的精彩示例。

image

您可以创建如下所示的命名管道

let videoPipe = FFmpegKitConfig.registerNewFFmpegPipe()
let audioPipe = FFmpegKitConfig.registerNewFFmpegPipe()
let ffmpegCommand = "-re -f rawvideo -pixel_format bgra -video_size 1920x1080 -framerate 30 -i \(videoPipe!) 
-f s16le -ar 48000 -ac 1 -itsoffset -5 -i \(audioPipe!) 
-framerate 30 -pixel_format yuv420p -c:v h264 -c:a aac -vf "transpose=1,scale=360:640" -b:v 640k -b:a 64k -vsync 1 
-f flv \(url!)"

// Execute FFmpeg command
FFmpegKit.executeAsync(ffmpegCommand) { session in
    // Handle FFmpeg execution completion
    print("FFmpeg execution completed with return code \(session.returnCode)")
}

写入管道

要写入管道,我们只需简单地使用 FileHandle 并指定管道路径。

if let currentPipe = self.videoPipe, let fileHandle = try? FileHandle(forWritingTo: URL(fileURLWithPath: currentPipe)) {
    if #available(iOS 13.4, *) {
        try? fileHandle.write(contentsOf: data)
    } else {
        fileHandle.write(data)
    }
    fileHandle.closeFile()
} else {
    print("Failed to open file handle for writing")
}

由于我没有使用任何类型的缓冲区,输出视频出现了卡顿。使用缓冲区时,ffmpeg 在流传输过程中会退出,因为在缓冲过程中命名管道会达到 EOF。

第三阶段

我查阅了此问题的解决方案,发现了一个这么做的技巧 - https://unix.stackexchange.com/questions/483359/how-can-i-stop-ffmpeg-from-quitting-when-it-reaches-the-end-of-a-named-pipe

我们只需打开命名管道。在 Swift 中,我们可以这样操作

let videoFileDescriptor = open(videoPipe!, O_RDWR)
let audioFileDescriptor = open(audioPipe!, O_RDWR)

这效果非常好!!

用法

let cameraSource = CameraSource(position: .front)
let microphoneSource = MicrophoneSource()
let ffLiveKit = FFLiveKit()
try? ffLiveKit.connect(connection: RTMPConnection(baseUrl: "rtmp://192.168.1.100:1935"))
ffLiveKit.addSource(camera: cameraSource, microphone: microphoneSource)
cameraSource.startPreview(previewView: self.view)
ffLiveKit.prepare(delegate: self)
if !isRecording {
    try? ffLiveKit.publish(name: "mystream")
} else {
    ffLiveKit.stop()
}

示例

out.mp4

与 HaishinKit 相比的性能 🤔🤔

FFmpeg

image


HaishinKit

image

待办事项

  • CPU 优化