// // FFmpegKitIOS.mm // iOS 端 .264 -> .mp4 转码桥(extern "C"),供 C# 经 [DllImport("__Internal")] 调用。 // // 原理: 调用 native ffmpeg-kit(ffmpegkit.framework)的 executeWithArguments,用 libx264 // 重编码重新生成干净的 SPS/PPS + 时间戳,修复设备(OPPO/MTK 等)产出裸流坏 PPS 导致的 // 解码卡死("non-existing PPS 0 referenced")。 // // 依赖: 需将 ffmpeg-kit iOS full-gpl 的 .xcframework(含 ffmpegkit.framework)放进 // Assets/Plugins/iOS/FFmpegKit/(Unity 会在构建 Xcode 工程时自动链接)。 // 版本需与本项目 Android 端一致: dev.ffmpegkit-maintained:ffmpeg-kit-full-gpl:6.0.3 // (FFmpeg n6.1.6,含 libx264)。缺少该 framework 时本文件编译期即报错找不到头文件,属预期。 // #import #import #import #import static BOOL ffmpegKitConfigured = NO; // 一次性配置:忽略 Unity/IL2CPP 运行时可能被 ffmpeg-kit 信号处理干扰的信号 static void FFmpegKitConfigureOnce(void) { if (ffmpegKitConfigured) return; ffmpegKitConfigured = YES; [FFmpegKitConfig ignoreSignal:SIGXCPU]; [FFmpegKitConfig ignoreSignal:SIGPIPE]; } extern "C" { /// 执行 ffmpeg 转码。 /// @param argv 参数数组(C# string[] 会作为 UTF-8 char** 传入) /// @param argc 参数个数 /// @return 0=成功;否则为 ffmpeg 退出码;-1=参数或内部错误 int FFmpegKitExecuteWithArguments(char **argv, int argc) { @autoreleasepool { FFmpegKitConfigureOnce(); if (argv == NULL || argc <= 0) { NSLog(@"[FFmpegKitIOS] bad args argv=%p argc=%d", argv, argc); return -1; } NSMutableArray *arguments = [NSMutableArray arrayWithCapacity:(NSUInteger)argc]; for (int i = 0; i < argc; i++) { if (argv[i] == NULL) continue; [arguments addObject:[NSString stringWithUTF8String:argv[i]]]; } NSLog(@"[FFmpegKitIOS] execute: %@", [arguments componentsJoinedByString:@" "]); FFmpegSession *session = [FFmpegKit executeWithArguments:arguments]; ReturnCode *returnCode = [session getReturnCode]; if ([ReturnCode isSuccess:returnCode]) { return 0; } if (returnCode == nil) { NSLog(@"[FFmpegKitIOS] returnCode nil, treat as failure."); return -1; } return (int)[returnCode getValue]; } } }