You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

iOS项目集成静态构建FFmpeg:CLI接口与库调用方法问询

解决iOS集成FFmpeg后的命令式调用与API使用问题

我之前也折腾过这个预编译FFmpeg库,给你分享几个实用的思路,帮你快速实现和Windows/Android一致的功能:

一、直接复用FFmpeg命令行逻辑(和独立应用同款体验)

如果你想直接用熟悉的命令字符串来执行操作,其实可以把FFmpeg的命令行入口函数暴露出来调用:

  1. 确认库编译配置:首先检查你用的编译脚本是否启用了ffmpeg工具组件——默认情况下kewlbear的脚本可能没开这个选项,需要重新运行脚本时,在build-ffmpeg.sh里确保添加了--enable-ffmpeg参数,这样才会包含命令行相关的代码。
  2. 封装命令调用函数:在iOS项目里写一个简单的OC/C wrapper,把OC的参数数组转换成C风格的argv,调用FFmpeg的主函数:
#include "ffmpeg.h"

- (int)runFFmpegCommand:(NSArray<NSString *> *)arguments {
    int argc = (int)arguments.count + 1;
    char **argv = malloc(sizeof(char *) * argc);
    argv[0] = "ffmpeg"; // 模拟命令名,FFmpeg内部会用到
    for (int i = 0; i < arguments.count; i++) {
        argv[i+1] = (char *)[arguments[i] UTF8String];
    }
    // 调用FFmpeg命令行入口
    int result = ffmpeg_main(argc, argv);
    free(argv);
    return result;
}

调用示例:

NSArray *cmdArgs = @["-i", inputFilePath, "-c:v", "libx264", "-crf", "23", outputFilePath];
int ret = [self runFFmpegCommand:cmdArgs];
if (ret == 0) {
    NSLog(@"命令执行成功");
} else {
    NSLog(@"命令执行失败,错误码:%d", ret);
}

二、直接调用FFmpeg原生API(无需命令行)

如果想更灵活地控制流程(比如实时处理、自定义回调),直接用FFmpeg的原生API是更好的选择——这也是你在Windows/Android上实现功能的底层逻辑,跨平台的API用法基本一致:
核心的API模块包括:

  • avformat:处理媒体文件的封装/解封装(打开、读取、写入文件)
  • avcodec:处理音视频编解码
  • avfilter:处理滤镜效果(裁剪、转码、水印等)
  • swscale:处理图像格式转换与缩放

举个简单的转码流程伪代码(带基础错误处理):

#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>

int transcodeVideo(const char *inputPath, const char *outputPath) {
    // 1. 注册所有组件
    av_register_all();
    avfilter_register_all();

    // 2. 打开输入文件
    AVFormatContext *inputCtx = NULL;
    if (avformat_open_input(&inputCtx, inputPath, NULL, NULL) != 0) {
        fprintf(stderr, "无法打开输入文件\n");
        return -1;
    }
    if (avformat_find_stream_info(inputCtx, NULL) < 0) {
        fprintf(stderr, "无法获取流信息\n");
        avformat_close_input(&inputCtx);
        return -1;
    }

    // 3. 找到视频流索引
    int videoStreamIdx = av_find_best_stream(inputCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if (videoStreamIdx < 0) {
        fprintf(stderr, "未找到视频流\n");
        avformat_close_input(&inputCtx);
        return -1;
    }

    // 4. 初始化解码器
    AVCodecParameters *codecPar = inputCtx->streams[videoStreamIdx]->codecpar;
    AVCodec *decoder = avcodec_find_decoder(codecPar->codec_id);
    AVCodecContext *decoderCtx = avcodec_alloc_context3(decoder);
    avcodec_parameters_to_context(decoderCtx, codecPar);
    if (avcodec_open2(decoderCtx, decoder, NULL) != 0) {
        fprintf(stderr, "无法打开解码器\n");
        avcodec_free_context(&decoderCtx);
        avformat_close_input(&inputCtx);
        return -1;
    }

    // 5. 初始化输出格式与编码器
    AVFormatContext *outputCtx = NULL;
    if (avformat_alloc_output_context2(&outputCtx, NULL, NULL, outputPath) < 0) {
        fprintf(stderr, "无法初始化输出上下文\n");
        avcodec_free_context(&decoderCtx);
        avformat_close_input(&inputCtx);
        return -1;
    }
    AVStream *outputStream = avformat_new_stream(outputCtx, NULL);
    AVCodec *encoder = avcodec_find_encoder(AV_CODEC_ID_H264);
    AVCodecContext *encoderCtx = avcodec_alloc_context3(encoder);
    // 配置编码器参数(根据你的需求调整)
    encoderCtx->width = decoderCtx->width;
    encoderCtx->height = decoderCtx->height;
    encoderCtx->pix_fmt = AV_PIX_FMT_YUV420P;
    encoderCtx->bit_rate = 5000000; // 5Mbps
    encoderCtx->time_base = av_inv_q(inputCtx->streams[videoStreamIdx]->r_frame_rate);
    encoderCtx->framerate = inputCtx->streams[videoStreamIdx]->r_frame_rate;
    if (avcodec_open2(encoderCtx, encoder, NULL) != 0) {
        fprintf(stderr, "无法打开编码器\n");
        avcodec_free_context(&encoderCtx);
        avformat_free_context(outputCtx);
        avcodec_free_context(&decoderCtx);
        avformat_close_input(&inputCtx);
        return -1;
    }
    avcodec_parameters_from_context(outputStream->codecpar, encoderCtx);

    // 6. 打开输出文件并写入头信息
    if (avio_open(&outputCtx->pb, outputPath, AVIO_FLAG_WRITE) < 0) {
        fprintf(stderr, "无法打开输出文件\n");
        avcodec_free_context(&encoderCtx);
        avformat_free_context(outputCtx);
        avcodec_free_context(&decoderCtx);
        avformat_close_input(&inputCtx);
        return -1;
    }
    if (avformat_write_header(outputCtx, NULL) < 0) {
        fprintf(stderr, "无法写入输出头\n");
        avio_close(outputCtx->pb);
        avcodec_free_context(&encoderCtx);
        avformat_free_context(outputCtx);
        avcodec_free_context(&decoderCtx);
        avformat_close_input(&inputCtx);
        return -1;
    }

    // 7. 循环处理:读取->解码->编码->写入
    AVPacket pkt;
    AVFrame *frame = av_frame_alloc();
    while (av_read_frame(inputCtx, &pkt) == 0) {
        if (pkt.stream_index == videoStreamIdx) {
            if (avcodec_send_packet(decoderCtx, &pkt) == 0) {
                while (avcodec_receive_frame(decoderCtx, frame) == 0) {
                    if (avcodec_send_frame(encoderCtx, frame) == 0) {
                        AVPacket outputPkt = {0};
                        while (avcodec_receive_packet(encoderCtx, &outputPkt) == 0) {
                            // 调整时间基
                            av_packet_rescale_ts(&outputPkt, encoderCtx->time_base, outputStream->time_base);
                            av_interleaved_write_frame(outputCtx, &outputPkt);
                            av_packet_unref(&outputPkt);
                        }
                    }
                }
            }
        }
        av_packet_unref(&pkt);
    }

    // 8. 写入尾信息并释放资源
    av_write_trailer(outputCtx);
    avio_close(outputCtx->pb);
    av_frame_free(&frame);
    avcodec_free_context(&encoderCtx);
    avcodec_free_context(&decoderCtx);
    avformat_free_context(outputCtx);
    avformat_close_input(&inputCtx);

    return 0;
}

你可以把这个C函数封装成OC方法,方便在iOS项目里调用,同时记得添加完整的错误处理和内存管理。

三、获取FFmpeg原生API的参考文档

  • 官方Doxygen文档:这是最权威的参考,里面详细说明了每个API的参数、返回值和使用场景,覆盖了所有核心模块。
  • 开源项目参考:比如ijkplayer(B站的开源播放器),它在iOS上深度封装了FFmpeg,你可以参考它的源码学习如何在移动端优雅地调用FFmpeg API。
  • FFmpeg官方示例:官方仓库里有很多example代码(比如doc/examples目录下的转码、滤镜示例),可以直接拿来参考适配iOS平台。

最后提醒一句:iOS上使用FFmpeg要注意协议合规(这个库是LGPL协议),如果是静态链接,需要确保你的代码符合协议要求,避免审核问题。

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

火山引擎 最新活动