【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 基本框架

在实现时,对参考博客中的部分函数进行了修改

  1. 不再使用av_register_all()函数对编解码器进行初始化
  2. 增加av_log_set_level(AV_LOG_TRACE)增加日志输出信息
  3. 使用本机IP127.0.0.1
  4. 修改部分参数的调用,因为部分变量存储的位置发生了变化
  5. 修改部分函数的调用,因为使用的FFmpeg版本不同
  6. 将推流和拉流的部分合并,用一套代码实现

在编码过程当中,主要使用了如下的函数

函数名 作用
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

从使用的函数来看,主要的操作流程和数据流走向大约为:

  1. 初始化网络模块,为RTMP传输进行准备(avformat_network_init)
  2. 打开输入文件,创建输入文件结构体并且读取输入文件信息(avformat_open_input),此时也会创建输入的流信息结构体
  3. 根据输入文件查找流信息,赋值给流信息结构体(avformat_find_stream_info)
  4. 打印输入文件信息(av_dump_format)
  5. 根据输出文件信息来创建输出文件结构体(avformat_alloc_output_context2)
  6. 创建输出流(avformat_new_stream)
  7. 将输入流的参数拷贝给输出给输出流(avcodec_parameters_copy)
  8. 打印输出文件信息(av_dump_format)
  9. 打开输出口,准备推流(avio_open)
  10. 写入流的头部信息(avformat_write_header)
  11. 读取一帧信息,存储到AVPacket中(av_read_frame)
  12. 处理时间戳;PTS是播放时间戳,告诉播放器播放这一帧的时间;DTS是解码时间戳,告诉播放器解码这一帧的时间;PTS通常是按照递增顺序排列的。这里雷博士认为延时很重要,如果不对前后帧推流的时间进行控制,帧会瞬时推送到服务器端,会出现服务器无法正常接收帧的情况
  13. 将帧推流(av_interleaved_write_frame)
  14. 写入流的尾部信息(av_write_trailer)
  15. 释放结构体信息(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*<
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值