推流项目的ffmpeg配置和流程重点总结一下

ffmpeg的初始化配置,在合成工作都是根据这个ffmpeg的配置来做的,是和成ts流还是flv,是推动远端还是保存到本地, FFmpeg 的核心数据结构,负责协调编码、封装和写入操作。它相当于推流的"总指挥"。

先来看一下ffmpeg的推流器配置。

复制代码
typedef struct
{
    AVStream *stream;    // 输出流
    AVCodecContext *enc; // 编码器
    int64_t next_timestamp; //下一个时间戳
    int samples_count;     // 采样数
    AVPacket *packet;   // 编码数据包
} OutputStream;

typedef struct
{
    unsigned int config_id; //用与多路码流
    int protocol_type; //流媒体TYPE
    char network_addr[NETWORK_ADDR_LENGTH];//流媒体地址
    enum AVCodecID video_codec; //视频编码器ID
    enum AVCodecID audio_codec; //音频编码器ID
    OutputStream video_stream; //VIDEO的STREAM配置
    OutputStream audio_stream; //AUDIO的STREAM配置
    AVFormatContext *oc; //是存储音视频封装格式中包含的信息的结构体,也是FFmpeg中统领全局的结构体,对文件的封装、编码操作从这里开始。
} RKMEDIA_FFMPEG_CONFIG; //FFMPEG配置

定义了我们流媒体的地址,和复合流是ts/flv等等。

在使用 FFmpeg 实现推流的过程中,每个步骤都有其特定的作用,这些步骤是基于音视频处理和网络传输的基本原理设计的。以下是对每个主要步骤的详细解释,以及它们为什么必不可少:

1. 初始化网络模块 (avformat_network_init)

作用: 初始化 FFmpeg 的网络功能,支持像 RTMP、HTTP 这样的网络协议。

为什么需要: FFmpeg 默认不加载网络相关模块,此步骤启用网络支持,确保推流到远程服务器(如 RTMP 服务器)时能够正确建立连接。如果不初始化,推流会因为缺少网络协议支持而失败。

原理: FFmpeg 的网络功能依赖底层库(如 librtmp 或内置的 TCP/UDP 实现),初始化会加载这些模块并注册相关协议。


2. 分配输出格式上下文 (avformat_alloc_output_context2)

复制代码
    //FLV_PROTOCOL is RTMP TCP
    if (ffmpeg_config->protocol_type == FLV_PROTOCOL)
    {
        //初始化一个FLV的AVFormatContext
        ret = avformat_alloc_output_context2(&ffmpeg_config->oc, NULL, "flv", ffmpeg_config->network_addr); 
        if (ret < 0)
        {
            return -1;
        }
    }
    //TS_PROTOCOL is SRT UDP RTSP
    else if (ffmpeg_config->protocol_type == TS_PROTOCOL)
    {
        //初始化一个TS的AVFormatContext
        ret = avformat_alloc_output_context2(&ffmpeg_config->oc, NULL, "mpegts", ffmpeg_config->network_addr);
        if (ret < 0)
        {
            return -1;
        }
    }

作用: 创建一个用于输出的容器对象(AVFormatContext),并指定输出格式(FLV/)。

为什么需要 : 推流需要将音视频数据封装成某种格式(比如 FLV 是 RTMP 的常用容器格式),这个上下文对象管理流的元数据、格式信息和输出目标。如果没有这个对象,FFmpeg 无法知道数据要以什么形式输出到哪里。

原理: 输出格式上下文是 FFmpeg 的核心数据结构,负责协调编码、封装和写入操作。它相当于推流的"总指挥"。


3. 创建输出流 (avformat_new_stream)

复制代码
    //是否指定了视频编解码器。
    if (fmt->video_codec != AV_CODEC_ID_NONE)
    {
        // 添加一个流(如视频流或音频流),并为其分配编码参数。
        ret = add_stream(&ffmpeg_config->video_stream, ffmpeg_config->oc, &video_codec, fmt->video_codec);
        if (ret < 0)
        {
            avcodec_free_context(&ffmpeg_config->video_stream.enc);
            free_stream(ffmpeg_config->oc, &ffmpeg_config->video_stream);
            avformat_free_context(ffmpeg_config->oc);
            return -1;
        }

        ret = open_video(ffmpeg_config->oc, video_codec, &ffmpeg_config->video_stream, NULL);
        if (ret < 0)
        {
            avformat_free_context(ffmpeg_config->oc);
        }
    }

        //是否指定了音频编解码器。
    if (fmt->audio_codec != AV_CODEC_ID_NONE)
    {
        //添加一路视频流
        ret = add_stream(&ffmpeg_config->audio_stream, ffmpeg_config->oc, &audio_codec, fmt->audio_codec);
        if (ret < 0)
        {
            avcodec_free_context(&ffmpeg_config->audio_stream.enc);
            free_stream(ffmpeg_config->oc, &ffmpeg_config->audio_stream);
            avformat_free_context(ffmpeg_config->oc);
            return -1;
        }

        ret = open_audio(ffmpeg_config->oc, audio_codec, &ffmpeg_config->audio_stream, NULL);
        if (ret < 0)
        {
            avformat_free_context(ffmpeg_config->oc);
        }
    }

作用: 在输出格式上下文中添加一个流(如视频流或音频流),并为其分配编码参数。

为什么需要: 推流通常包含视频和音频两种数据,每种数据需要独立的流来承载。创建流是为了定义这些数据的结构(如分辨率、帧率、采样率等),否则 FFmpeg 无法组织数据。

原理: 流(AVStream)是容器中的独立轨道,推流时服务器和客户端会根据流信息解码数据

4. 打开输出 URL (avio_open)

复制代码
   if (!(fmt->flags & AVFMT_NOFILE))
    {
        //第四步:打开输出文件
        /**
        * 作用: 
        * 建立与目标服务器(如 RTMP 服务器)的物理连接。
        * 为什么需要: 
        * 推流是将数据发送到远程服务器的过程,必须先打开一个通道(类似于文件句柄或网络 socket),
        * 否则数据无法传输。如果没有这一步,程序会因为找不到输出目标而报错。
        * 原理: 
        * avio_open 使用底层 I/O 抽象层(AVIOContext)与服务器通信,支持多种协议(如 RTMP、HLS)。
        * 对于 RTMP,它会通过 librtmp 初始化连接并完成握手。
         */
        ret = avio_open(&ffmpeg_config->oc->pb, ffmpeg_config->network_addr, AVIO_FLAG_WRITE);
        if (ret < 0)
        {
            free_stream(ffmpeg_config->oc, &ffmpeg_config->video_stream);
            free_stream(ffmpeg_config->oc, &ffmpeg_config->audio_stream);
            avformat_free_context(ffmpeg_config->oc);
            return -1;
        }
    }

**作用:**建立与目标服务器(如 RTMP 服务器)的物理连接。

为什么需要: 推流是将数据发送到远程服务器的过程,必须先打开一个通道(类似于文件句柄或网络 socket),否则数据无法传输。如果没有这一步,程序会因为找不到输出目标而报错**。**

原理: avio_open 使用底层 I/O 抽象层(AVIOContext)与服务器通信,支持多种协议(如 RTMP、HLS)。对于 RTMP,它会通过 librtmp 初始化连接并完成握手**。**


5. 写入流头部信息 (avformat_write_header)

复制代码
    // 第五步:写入文件头信息
    //这行代码会根据ffmpeg_config->oc中指定的输出格式(如FLV或TS),
    //生成并写入相应的头部信息到输出文件或流中。
    avformat_write_header(ffmpeg_config->oc, NULL);

作用: 将流的元数据(比如编码格式、分辨率、时长等)写入输出流的开头。

为什么需要: 接收端(比如直播服务器或播放器)需要这些信息来正确解析后续的数据。如果缺少头部信息,接收端可能无法识别流格式,导致播放失败。

原理: 头部信息是容器格式(如 FLV)的标准部分,包含流的描述性数据,类似于文件的"索引"。


6. 写入数据 (av_interleaved_write_frame)

复制代码
int write_ffmpeg_avpacket(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt)
{
    /*将输出数据包时间戳值从编解码器重新调整为流时基 */
    av_packet_rescale_ts(pkt, *time_base, st->time_base);
    pkt->stream_index = st->index;

    /**
     * 第六步:写入数据 
     * 作用: 
     * 将编码后的音视频数据(AVPacket)写入输出流,发送到服务器。
     * 为什么需要: 
     * 这是推流的核心步骤,所有的音视频内容都通过这一步传输到目标。
     * 如果没有这一步,之前的准备工作就毫无意义,因为数据没有实际发送。
     * 原理: 
     * FFmpeg 使用"交错写入"(interleaved)方式处理多路流(如视频和音频),确保时间戳对齐,符合实时传输的要求。
     * 数据会被封装为 FLV 标签(Tag)并通过网络发送。
     */
    return av_interleaved_write_frame(fmt_ctx, pkt);
}

作用: 将编码后的音视频数据(AVPacket)写入输出流,发送到服务器。

为什么需要: 这是推流的核心步骤,所有的音视频内容都通过这一步传输到目标。如果没有这一步,之前的准备工作就毫无意义,因为数据没有实际发送。

原理: FFmpeg 使用"交错写入"(interleaved)方式处理多路流(如视频和音频),确保时间戳对齐,符合实时传输的要求。数据会被封装为 FLV 标签(Tag)并通过网络发送。


7. 写入流尾部 (av_write_trailer)

复制代码
    //第7步: 写入流尾部 (av_write_trailer)
    /**
     * 
     * 作用: 标记流的结束,并写入必要的结尾信息
     */
    av_write_trailer(ffmpeg_config.oc); // 写入AVFormatContext的尾巴

作用: 标记流的结束,并写入必要的结尾信息。

为什么需要: 某些容器格式需要在结束时添加元数据(如索引表),以确保接收端能正确处理整个流。如果不写入尾部,流可能会出现不完整或无法播放的问题。

原理: 尾部信息是容器格式规范的一部分,对于 FLV 来说,它可能只是简单地关闭流,但对于其他格式(如 MP4),它可能包含关键的索引。


8. 清理资源 (avio_closep, avformat_free_context)

复制代码
   //第八步:清理资源 (avio_closep, avformat_free_context)
    /**
     * 
     * 作用: 关闭网络连接并释放内存
     */
    free_stream(ffmpeg_config.oc, &ffmpeg_config.video_stream); // 释放VIDEO_STREAM的资源
    free_stream(ffmpeg_config.oc, &ffmpeg_config.audio_stream); // 释放AUDIO_STREAM的资源
    avio_closep(&ffmpeg_config.oc->pb);                         // 释放AVIO资源
    avformat_free_context(ffmpeg_config.oc);                    // 释放AVFormatContext资源

作用: 关闭网络连接并释放内存。

为什么需要: 推流结束后,如果不清理资源,会导致内存泄漏或网络连接未正确关闭,影响程序稳定性。对于长时间运行的推流任务尤其重要。

**原理:**FFmpeg 的上下文对象和 I/O 句柄占用系统资源(如内存和 socket),需要显式释放以遵循良好的编程实践。


整体工作流程的意义,这些步骤共同构成了 FFmpeg 推流的完整工作流,反映了音视频处理和网络传输的本质:

  1. 准备阶段: 初始化环境、定义格式和目标(步骤 1-3)。

  2. 连接阶段: 建立与服务器的通信(步骤 4)。

  3. 传输阶段: 发送元数据和实际数据(步骤 5-6)。

  4. 结束阶段: 完成传输并清理(步骤 7-8)。

相关推荐
l***775226 分钟前
从MySQL5.7平滑升级到MySQL8.0的最佳实践分享
ffmpeg
ZouZou老师6 小时前
FFmpeg性能优化经典案例
性能优化·ffmpeg
aqi009 小时前
FFmpeg开发笔记(九十)采用FFmpeg套壳的音视频转码百宝箱FFBox
ffmpeg·音视频·直播·流媒体
齐齐大魔王11 小时前
FFmpeg
ffmpeg
你好音视频12 小时前
FFmpeg RTSP拉流流程深度解析
ffmpeg
IFTICing1 天前
【环境配置】ffmpeg下载、安装、配置(Windows环境)
windows·ffmpeg
haiy20111 天前
FFmpeg 编译
ffmpeg
aqi001 天前
FFmpeg开发笔记(八十九)基于FFmpeg的直播视频录制工具StreamCap
ffmpeg·音视频·直播·流媒体
八月的雨季 最後的冰吻1 天前
FFmepg--28- 滤镜处理 YUV 视频帧:实现上下镜像效果
ffmpeg·音视频
ganqiuye1 天前
向ffmpeg官方源码仓库提交patch
大数据·ffmpeg·video-codec