【FFmpeg】调用ffmpeg库实现RTMP推流
参考:最简单的基于FFmpeg的推流器(以推送RTMP为例)
====== 示例工程 ======
【FFmpeg】调用FFmpeg库实现264软编
【FFmpeg】调用FFmpeg库实现264软解
1.FFmpeg编译
参考: Windows搭建RTMP服务器+OBS推流+VLC拉流
本文使用FFmpeg-7.0版本
2.RTMP服务器搭建
将本机配置成为服务器,实现本地的推流和拉流操作。RTMP服务器的搭建参考:RTMP服务器的搭建
RTMP是Adobe提出的一种应用层的协议,用于解决多媒体数据传输流的多路复用(Multiplexing)和分包(packetizing)的问题传输,传输媒体的格式为FLV,因此本文推流的格式是flv格式。flv文件可以使用ffmpeg命令行,从yuv文件转换而来。
3.调用FFmpeg库实现RTMP推流和拉流
3.1 基本框架
在实现时,对参考博客中的部分函数进行了修改
- 不再使用av_register_all()函数对编解码器进行初始化
- 增加av_log_set_level(AV_LOG_TRACE)增加日志输出信息
- 使用本机IP127.0.0.1
- 修改部分参数的调用,因为部分变量存储的位置发生了变化
- 修改部分函数的调用,因为使用的FFmpeg版本不同
- 将推流和拉流的部分合并,用一套代码实现
在编码过程当中,主要使用了如下的函数
| 函数名 | 作用 |
|---|---|
| av_log_set_level | 配置输出日志级别 (AV_LOG_TRACE最详细) |
| avformat_network_init | 初始化网络模块 |
| avformat_open_input | 打开输入文件,并且将文件信息赋值给AVFormatContext保存 |
| avformat_find_stream_info | 根据AVFormatContext查找流信息 |
| av_dump_format | 将AVFormatContext中的媒体文件的信息进行格式化输出 |
| avformat_alloc_output_context2 | 根据format_name(或filename或oformat)创建输出文件的AVFormatContext信息 |
| avformat_new_stream | 根据AVFormatContext和AVCodecContext创建新的流 |
| avcodec_parameters_copy | 拷贝AVCodecParameters |
| avio_open | 根据url进行AVIOContext的创建与初始化(这个url在推流时就是服务器地址) |
| avformat_write_header | 为流分配priv_data并且将流的头信息写入到输出媒体文件 |
| av_read_frame | 根据AVFormatContext所提供的的信息读取一帧,存入AVPacket |
| av_interleaved_write_frame | 以交错的方式将帧送入到媒体文件中 |
| av_packet_unref | 释放AVPacket |
| av_write_trailer | 将流的尾部写入到输出的媒体文件中,并且释放文件中的priv_data |
| avformat_close_input | 释放AVFormatContext |
从使用的函数来看,主要的操作流程和数据流走向大约为:
- 初始化网络模块,为RTMP传输进行准备(avformat_network_init)
- 打开输入文件,创建输入文件结构体并且读取输入文件信息(avformat_open_input),此时也会创建输入的流信息结构体
- 根据输入文件查找流信息,赋值给流信息结构体(avformat_find_stream_info)
- 打印输入文件信息(av_dump_format)
- 根据输出文件信息来创建输出文件结构体(avformat_alloc_output_context2)
- 创建输出流(avformat_new_stream)
- 将输入流的参数拷贝给输出给输出流(avcodec_parameters_copy)
- 打印输出文件信息(av_dump_format)
- 打开输出口,准备推流(avio_open)
- 写入流的头部信息(avformat_write_header)
- 读取一帧信息,存储到AVPacket中(av_read_frame)
- 处理时间戳;PTS是播放时间戳,告诉播放器播放这一帧的时间;DTS是解码时间戳,告诉播放器解码这一帧的时间;PTS通常是按照递增顺序排列的。这里雷博士认为延时很重要,如果不对前后帧推流的时间进行控制,帧会瞬时推送到服务器端,会出现服务器无法正常接收帧的情况
- 将帧推流(av_interleaved_write_frame)
- 写入流的尾部信息(av_write_trailer)
- 释放结构体信息(av_packet_unref、av_write_trailer和avformat_close_input)
在上述流程当中,利用avformat_open_input获取了头信息,知道了是否包含音频流和视频流,但是并不知道编解码格式,还需要使用avformat_find_stream来获取具体的格式。
3.2 实现代码
在调试时,发现如果AVPacket这里定义如果是指针的话,会出现av_read_frame第二帧读取失败的情况,这里有待进一步思考。
在代码中,利用bool is_sender来控制是发送还是接收,发送和接收都使用同一套代码,只是在时间戳部分有所区别,即发送端需要计算而接收端不需要使用。不过这里的in_url和out_url还是固定的,实际使用时得重新配置。
#pragma warning(disable : 4996)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "streamer.h"
#ifdef _WIN32
//Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/avutil.h"
#include "libavutil/opt.h"
#include "libavutil/time.h"
#include "libavutil/timestamp.h"
#include "libavutil/mathematics.h"
#include "libavutil/log.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/avutil.h"
#include "libavutil/opt.h"
#ifdef __cplusplus
};
#endif
#endif
int streamer_internal(const char* in_url, const char* out_url, bool is_sender)
{
// set log level
// av_log_set_level(AV_LOG_TRACE);
AVOutputFormat* av_out_fmt = NULL;
AVFormatContext* av_in_fmt_ctx = NULL;
AVFormatContext* av_out_fmt_ctx = NULL;
AVPacket av_pkt;
const char*<

最低0.47元/天 解锁文章
2928

被折叠的 条评论
为什么被折叠?



