当前位置: 首页 > news >正文

利用ffmpeg库实现音频Opus编解码

一、编译与环境配置
  1. libopus库集成
    需在编译FFmpeg时添加--enable-libopus参数,编译前需先安装libopus源码并配置动态库路径‌。最新FFmpeg 7.1版本默认支持Opus的浮点运算优化和VBR/CVBR模式‌。

  2. 多平台兼容性
    Opus支持Windows/Linux/macOS平台,编译时需注意不同系统的依赖库路径差异‌。

二、命令行编解码操作
  1. 编码

    # PCM转Opus(48kHz双通道)
    ffmpeg -f s16le -ar 48000 -ac 2 -i input.pcm -c:a libopus -b:a 128k -vbr on output.opus

    -ar:指定输入采样率(支持8k/16k/48k等)‌
    -ac:设置通道数(WebRTC场景强制要求双通道)‌

  2. 解码

    # Opus转PCM并重采样至16kHz单通道
    ffmpeg -i input.opus -ar 16000 -ac 1 -f s16le output.pcm

    支持动态调整输出采样率(8k/16k/44.1k/48k)
    解码后需通过nb_samples获取实际音频帧大小‌

三、代码编解码实现
1、Opus编码代码 
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>

int decode_opus_to_pcm(const char* input_file, const char* output_file) {
    AVFormatContext *fmt_ctx = NULL;
    AVCodecContext *codec_ctx = NULL;
    const AVCodec *codec = NULL;
    FILE *pcm_out = fopen(output_file, "wb");

    // 1. 注册编解码器
    av_register_all();
    avcodec_register_all();

    // 2. 打开输入文件
    if(avformat_open_input(&fmt_ctx, input_file, NULL, NULL) < 0) {
        fprintf(stderr, "无法打开输入文件\n");
        return -1;
    }

    // 3. 查找音频流
    if(avformat_find_stream_info(fmt_ctx, NULL) < 0) {
        fprintf(stderr, "无法找到流信息\n");
        return -1;
    }

    // 4. 获取Opus解码器
    codec = avcodec_find_decoder(AV_CODEC_ID_OPUS);
    if(!codec) {
        fprintf(stderr, "Opus解码器未找到\n");
        return -1;
    }

    codec_ctx = avcodec_alloc_context3(codec);
    avcodec_parameters_to_context(codec_ctx, fmt_ctx->streams->codecpar);

    // 5. 打开解码器
    if(avcodec_open2(codec_ctx, codec, NULL) < 0) {
        fprintf(stderr, "无法打开解码器\n");
        return -1;
    }

    AVPacket packet;
    AVFrame *frame = av_frame_alloc();
    
    // 6. 解码循环
    while(av_read_frame(fmt_ctx, &packet) >= 0) {
        if(packet.stream_index == 0) {
            int ret = avcodec_send_packet(codec_ctx, &packet);
            while(ret >= 0) {
                ret = avcodec_receive_frame(codec_ctx, frame);
                if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                    break;
                else if(ret < 0) {
                    fprintf(stderr, "解码错误\n");
                    break;
                }

                // 7. 输出PCM数据(16-bit小端)
                fwrite(frame->data, 1, frame->nb_samples * av_get_bytes_per_sample(codec_ctx->sample_fmt), pcm_out);
            }
        }
        av_packet_unref(&packet);
    }

    // 8. 清理资源
    av_frame_free(&frame);
    avcodec_free_context(&codec_ctx);
    avformat_close_input(&fmt_ctx);
    fclose(pcm_out);
    return 0;
}
2、Opus解码代码 
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>

int encode_pcm_to_opus(const char* input_pcm, const char* output_opus) {
    const AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_OPUS);
    AVCodecContext *codec_ctx = NULL;
    FILE *opus_out = fopen(output_opus, "wb");
    FILE *pcm_in = fopen(input_pcm, "rb");
    
    // 1. 创建编码器上下文
    codec_ctx = avcodec_alloc_context3(codec);
    codec_ctx->bit_rate = 64000;         // 目标码率
    codec_ctx->sample_fmt = AV_SAMPLE_FMT_S16; // 输入格式
    codec_ctx->sample_rate = 48000;      // 采样率
    codec_ctx->channel_layout = AV_CH_LAYOUT_STEREO; // 双声道
    codec_ctx->channels = 2;

    // 2. 设置编码参数
    av_opt_set(codec_ctx->priv_data, "application", "audio", 0); // 音乐优化
    av_opt_set_int(codec_ctx->priv_data, "compression_level", 10, 0); // 最高质量

    // 3. 打开编码器
    if(avcodec_open2(codec_ctx, codec, NULL) < 0) {
        fprintf(stderr, "无法打开编码器\n");
        return -1;
    }

    AVFrame *frame = av_frame_alloc();
    frame->nb_samples = codec_ctx->frame_size; // 帧大小(如960 samples@48kHz)
    frame->format = codec_ctx->sample_fmt;
    frame->channel_layout = codec_ctx->channel_layout;
    av_frame_get_buffer(frame, 0);

    AVPacket *pkt = av_packet_alloc();

    // 4. 编码循环
    while(1) {
        // 读取PCM数据
        size_t read_size = fread(frame->data, 1, frame->nb_samples * av_get_bytes_per_sample(codec_ctx->sample_fmt), pcm_in);
        if(read_size <= 0) break;

        // 发送帧到编码器
        int ret = avcodec_send_frame(codec_ctx, frame);
        while(ret >= 0) {
            ret = avcodec_receive_packet(codec_ctx, pkt);
            if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                break;
            else if(ret < 0) {
                fprintf(stderr, "编码错误\n");
                break;
            }

            // 写入输出文件(需添加OGG封装头)
            fwrite(pkt->data, 1, pkt->size, opus_out);
            av_packet_unref(pkt);
        }
    }

    // 5. 清理资源
    av_frame_free(&frame);
    av_packet_free(&pkt);
    avcodec_free_context(&codec_ctx);
    fclose(opus_out);
    fclose(pcm_in);
    return 0;
}
3、关键代码说明
  1. 编解码器初始化

    • avcodec_find_decoder/encoder(AV_CODEC_ID_OPUS) 查找编解码器
    • avcodec_alloc_context3() 创建编解码上下文
    • avcodec_open2() 打开编解码器
  2. 数据处理流程

    • 解码‌:av_read_frame() → avcodec_send_packet() → avcodec_receive_frame()
    • 编码‌:avcodec_send_frame() → avcodec_receive_packet()
  3. 参数优化

    // 设置低延迟模式
    av_opt_set(codec_ctx->priv_data, "application", "lowdelay", 0);
    
    // 设置VBR模式(0-固定码率,1-可变码率)
    av_opt_set_int(codec_ctx->priv_data, "vbr", 1, 0);
    
    // 设置帧持续时间(单位:ms)
    av_opt_set_int(codec_ctx->priv_data, "frame_duration", 20, 0);
    4、编译与运行
    # 编译命令(需链接FFmpeg库)
    gcc opus_example.c -o opus_demo \
        -lavcodec -lavformat -lavutil -lswresample
    
    # 运行解码示例
    ./opus_demo input.opus output.pcm
    
    # 运行编码示例(输入需为48kHz双通道s16 PCM)
    ./opus_demo input.pcm output.opus

相关文章:

  • 车载以太网网络测试-18【传输层-DOIP协议-1】
  • PyTorch模型转ONNX例子
  • 深入探究 JVM 堆的垃圾回收机制(一)— 判活
  • python3 -m http.sever 8080加载不了解决办法
  • 6个常见的Python设计模式及应用场景
  • Python实战:开发经典猜拳游戏(石头剪刀布)
  • MySQL事务全解析:从概念到实战
  • 【CXX-Qt】2.1.1 为 WebAssembly 构建
  • 汽车免拆诊断案例 | 2024 款路虎发现运动版车无法正常识别智能钥匙
  • Java EE 进阶:MyBatis
  • 【NLP】 11. 神经网络,线性模型,非线性模型,激活函数,感知器优化,正则化学习方法
  • SpringBoot配置文件加载优先级
  • 最大公约数(GCD)和最小公倍数(LCM)专题练习 ——基于罗勇军老师的《蓝桥杯算法入门C/C++》
  • 蓝桥杯2023年第十四届省赛真题-接龙数列
  • Linux后门程序工作原理的详细解释,以及相应的防御措施
  • c语言数据结构 双循环链表设计(完整代码)
  • Ubuntu版免翻墙搭建BatteryHistorian
  • freeswitch(开启抓包信息)
  • 观察RenderDoc截帧UE时“Event”代表什么
  • ssh 多重验证的好处:降低密钥长度,动态密码
  • 人民日报:从“轻微免罚”看涉企执法方式转变
  • “老中青少”四代同堂,季春艳携锡剧《玲珑女》冲击梅花奖
  • 寒武纪陈天石:公司的产品力获得了行业客户广泛认可,市场有望迎来新增量需求
  • 再获殊荣!IP SH跻身上海文化品牌全球传播力TOP 6
  • 应急部:正在积极推动各地逐步科学建设改造应急避难场所
  • 做街坊们的“健康管家”,她把专科护理服务送上门