百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

基于FFmpeg的推流器(以推送RTMP为例)

off999 2025-02-13 13:53 117 浏览 0 评论

简介

RTMP推流器(Streamer)的在流媒体系统中的作用可以用下图表示。首先将视频数据以RTMP的形式发送到流媒体服务器端(Server,比如FMS,Red5,Wowza等),然后客户端(一般为Flash Player)通过访问流媒体服务器就可以收看实时流了。


运行本程序之前需要先运行RTMP流媒体服务器,并在流媒体服务器上建立相应的Application。有关流媒体服务器的操作不在本文的论述范围内,在此不再详述。本程序运行后,即可通过RTMP客户端(例如 Flash Player, FFplay等等)收看推送的直播流。详细教程资料关注+后台私信;资料;两个字可以免费视频领取+文档+各大厂面试题 资料内容包括:C/C++,Linux,golang,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,嵌入式 等。


需要要注意的地方

封装格式

RTMP采用的封装格式是FLV。因此在指定输出流媒体的时候需要指定其封装格式为“flv”。同理,其他流媒体协议也需要指定其封装格式。例如采用UDP推送流媒体的时候,可以指定其封装格式为“mpegts”。

延时

发送流媒体的数据的时候需要延时。不然的话,FFmpeg处理数据速度很快,瞬间就能把所有的数据发送出去,流媒体服务器是接受不了的。因此需要按照视频实际的帧率发送数据。本文记录的推流器在视频帧与帧之间采用了av_usleep()函数休眠的方式来延迟发送。这样就可以按照视频的帧率发送数据了,参考代码如下。

//…
int64_t start_time=av_gettime();
while (1) {
//…
	//Important:Delay
	if(pkt.stream_index==videoindex){
		AVRational time_base=ifmt_ctx->streams[videoindex]->time_base;
		AVRational time_base_q={1,AV_TIME_BASE};
		int64_t pts_time = av_rescale_q(pkt.dts, time_base, time_base_q);
		int64_t now_time = av_gettime() - start_time;
		if (pts_time > now_time)
			av_usleep(pts_time - now_time);
	}
//…
}
//…

PTS/DTS问题

没有封装格式的裸流(例如H.264裸流)是不包含PTS、DTS这些参数的。在发送这种数据的时候,需要自己计算并写入AVPacket的PTS,DTS,duration等参数。这里还没有深入研究,简单写了一点代码,如下所示。

//FIX:No PTS (Example: Raw H.264)
//Simple Write PTS
if(pkt.pts==AV_NOPTS_VALUE){
	//Write PTS
	AVRational time_base1=ifmt_ctx->streams[videoindex]->time_base;
	//Duration between 2 frames (us)
	int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(ifmt_ctx->streams[videoindex]->r_frame_rate);
	//Parameters
	pkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);
	pkt.dts=pkt.pts;
	pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);
}

程序流程图

程序的流程图如下图所示。可以看出和《最简单的基于FFMPEG的封装格式转换器(无编解码)》中的封装格式转换器比较类似。它们之间比较明显的区别在于:
1.Streamer输出为URL
2.Streamer包含了延时部分

代码

代码如下。

/**
 * 最简单的基于FFmpeg的推流器(推送RTMP)
 * Simplest FFmpeg Streamer (Send RTMP)
 * 
 * 雷霄骅 Lei Xiaohua
 * leixiaohua1020@126.com
 * 中国传媒大学/数字电视技术
 * Communication University of China / Digital TV Technology
 * http://blog.csdn.net/leixiaohua1020
 * 
 * 本例子实现了推送本地视频至流媒体服务器(以RTMP为例)。
 * 是使用FFmpeg进行流媒体推送最简单的教程。
 *
 * This example stream local media files to streaming media 
 * server (Use RTMP as example). 
 * It's the simplest FFmpeg streamer.
 * 
 */
 
#include 
 
#define __STDC_CONSTANT_MACROS
 
#ifdef _WIN32
//Windows
extern "C"
{
#include "libavformat/avformat.h"
#include "libavutil/mathematics.h"
#include "libavutil/time.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include 
#include 
#include 
#ifdef __cplusplus
};
#endif
#endif
 
int main(int argc, char* argv[])
{
	AVOutputFormat *ofmt = NULL;
	//输入对应一个AVFormatContext,输出对应一个AVFormatContext
	//(Input AVFormatContext and Output AVFormatContext)
	AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
	AVPacket pkt;
	const char *in_filename, *out_filename;
	int ret, i;
	int videoindex=-1;
	int frame_index=0;
	int64_t start_time=0;
	//in_filename  = "cuc_ieschool.mov";
	//in_filename  = "cuc_ieschool.mkv";
	//in_filename  = "cuc_ieschool.ts";
	//in_filename  = "cuc_ieschool.mp4";
	//in_filename  = "cuc_ieschool.h264";
	in_filename  = "cuc_ieschool.flv";//输入URL(Input file URL)
	//in_filename  = "shanghai03_p.h264";
	
	out_filename = "rtmp://localhost/publishlive/livestream";//输出 URL(Output URL)[RTMP]
	//out_filename = "rtp://233.233.233.233:6666";//输出 URL(Output URL)[UDP]
 
	av_register_all();
	//Network
	avformat_network_init();
	//输入(Input)
	if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
		printf( "Could not open input file.");
		goto end;
	}
	if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
		printf( "Failed to retrieve input stream information");
		goto end;
	}
 
	for(i=0; inb_streams; i++) 
		if(ifmt_ctx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){
			videoindex=i;
			break;
		}
 
	av_dump_format(ifmt_ctx, 0, in_filename, 0);
 
	//输出(Output)
	
	avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", out_filename); //RTMP
	//avformat_alloc_output_context2(&ofmt_ctx, NULL, "mpegts", out_filename);//UDP
 
	if (!ofmt_ctx) {
		printf( "Could not create output context\n");
		ret = AVERROR_UNKNOWN;
		goto end;
	}
	ofmt = ofmt_ctx->oformat;
	for (i = 0; i < ifmt_ctx->nb_streams; i++) {
		//根据输入流创建输出流(Create output AVStream according to input AVStream)
		AVStream *in_stream = ifmt_ctx->streams[i];
		AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
		if (!out_stream) {
			printf( "Failed allocating output stream\n");
			ret = AVERROR_UNKNOWN;
			goto end;
		}
		//复制AVCodecContext的设置(Copy the settings of AVCodecContext)
		ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
		if (ret < 0) {
			printf( "Failed to copy context from input to output stream codec context\n");
			goto end;
		}
		out_stream->codec->codec_tag = 0;
		if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
			out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
	}
	//Dump Format------------------
	av_dump_format(ofmt_ctx, 0, out_filename, 1);
	//打开输出URL(Open output URL)
	if (!(ofmt->flags & AVFMT_NOFILE)) {
		ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
		if (ret < 0) {
			printf( "Could not open output URL '%s'", out_filename);
			goto end;
		}
	}
	//写文件头(Write file header)
	ret = avformat_write_header(ofmt_ctx, NULL);
	if (ret < 0) {
		printf( "Error occurred when opening output URL\n");
		goto end;
	}
 
	start_time=av_gettime();
	while (1) {
		AVStream *in_stream, *out_stream;
		//获取一个AVPacket(Get an AVPacket)
		ret = av_read_frame(ifmt_ctx, &pkt);
		if (ret < 0)
			break;
		//FIX:No PTS (Example: Raw H.264)
		//Simple Write PTS
		if(pkt.pts==AV_NOPTS_VALUE){
			//Write PTS
			AVRational time_base1=ifmt_ctx->streams[videoindex]->time_base;
			//Duration between 2 frames (us)
			int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(ifmt_ctx->streams[videoindex]->r_frame_rate);
			//Parameters
			pkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);
			pkt.dts=pkt.pts;
			pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);
		}
		//Important:Delay
		if(pkt.stream_index==videoindex){
			AVRational time_base=ifmt_ctx->streams[videoindex]->time_base;
			AVRational time_base_q={1,AV_TIME_BASE};
			int64_t pts_time = av_rescale_q(pkt.dts, time_base, time_base_q);
			int64_t now_time = av_gettime() - start_time;
			if (pts_time > now_time)
				av_usleep(pts_time - now_time);
 
		}
 
		in_stream  = ifmt_ctx->streams[pkt.stream_index];
		out_stream = ofmt_ctx->streams[pkt.stream_index];
		/* copy packet */
		//转换PTS/DTS(Convert PTS/DTS)
		pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
		pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
		pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
		pkt.pos = -1;
		//Print to Screen
		if(pkt.stream_index==videoindex){
			printf("Send %8d video frames to output URL\n",frame_index);
			frame_index++;
		}
		//ret = av_write_frame(ofmt_ctx, &pkt);
		ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
 
		if (ret < 0) {
			printf( "Error muxing packet\n");
			break;
		}
		
		av_free_packet(&pkt);
		
	}
	//写文件尾(Write file trailer)
	av_write_trailer(ofmt_ctx);
end:
	avformat_close_input(&ifmt_ctx);
	/* close output */
	if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
		avio_close(ofmt_ctx->pb);
	avformat_free_context(ofmt_ctx);
	if (ret < 0 && ret != AVERROR_EOF) {
		printf( "Error occurred.\n");
		return -1;
	}
	return 0;
}
 
 

结果

程序开始运行后。截图如下所示。


可以通过网页播放器播放推送的直播流。

例如下图所示,使用Flash Media Server 的Samples文件夹下的videoPlayer播放直播流的截图如下图所示。(直播地址:
rtmp://localhost/publishlive/livestream)


总结

C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂详细教程资料关注+后台私信;资料;两个字可以免费视频领取+文档+各大厂面试题 资料内容包括:C/C++,Linux,golang,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,嵌入式 等。

相关推荐

下载设置功能到手机桌面(设置下载安装到手机桌面)

华为手机怎么安装下载内容?1.打开华为应用市场后点击上方的“管理”。2.再点击进入“安装管理”。3.在下载好但没有桌面图标的APP那里点击“∨”处,再点“管理”就能查看到此应用的应用信息。4.选择其...

win7旗舰版恢复出厂设置视频教程

在Windows系统中恢复出厂设置的方法如下:通过Windows系统恢复:关闭电脑,然后按住电源按钮,直到电脑启动,进入Windows启动管理器。选择“疑难解答”或“高级选项”。选择“重置此电脑”,然...

win7分辨率不能调整怎么办(win7屏幕分辨率调不了怎么办 电脑分辨率调整)
win7分辨率不能调整怎么办(win7屏幕分辨率调不了怎么办 电脑分辨率调整)

1、首先检查显示器要设置的分辨率是否支持,方法是:在电脑空白处用鼠标右击选择“屏幕分辨率——高级设置”。在弹出来的属性窗口中,“适配器”选项卡下点击“列出所有模式”按钮,就可以看到里面所支持的分辨率,如果没有所设置的值,则说明不支持,所以就...

2026-01-07 20:03 off999

qq空间登录电脑版入口(qq空间登录电脑版入口官网)
qq空间登录电脑版入口(qq空间登录电脑版入口官网)

z.qq.com可以通过以下方式登录手机QQ空间:1、使用手机登录手机腾讯网3g.qq.com,点击“空间”,根据提示QQ号码和QQ密码就可以登录;2、通过手机直接输入手机QQ空间网址z.qq.com,根据提示操作即可登录;3、下载手机Q...

2026-01-07 19:51 off999

win10下载官网下载不了(win10下载官网为什么打不开)

可能有几个原因导致您无法在官网下载Windows10。首先,您需要确保您正在访问Microsoft官网,因为只有在官网上才能下载Windows10。如果您已经在官网上但仍然无法下载,可能是因为服务...

lenovo键盘锁住了怎么解锁(lenovo电脑键盘锁了怎么解锁)
lenovo键盘锁住了怎么解锁(lenovo电脑键盘锁了怎么解锁)

情况一:有时候只是键盘的键位被锁住了,我们可以尝试通过FN+F8解锁,这种方法适用于笔记本。如果是键盘上的数字键盘锁住了,可以按一下NumLock键位解锁,看到上面有个灯亮了就说明解锁了。情况二:1、首先鼠标右键点击此电脑图标,然后点击管理...

2026-01-07 18:51 off999

vivo手机怎么切换经典桌面(vivo手机从哪里切换经典桌面)
vivo手机怎么切换经典桌面(vivo手机从哪里切换经典桌面)

  1、从手机桌面中找到变形器的图标。   2、选择带有安卓系统的图标就可以从平行空间退出,并切换到经典桌面。   3、想要再次进入平行空间的话只...

2026-01-07 18:03 off999

磁盘写保护的解除方法(磁盘写保护如何处理)
  • 磁盘写保护的解除方法(磁盘写保护如何处理)
  • 磁盘写保护的解除方法(磁盘写保护如何处理)
  • 磁盘写保护的解除方法(磁盘写保护如何处理)
  • 磁盘写保护的解除方法(磁盘写保护如何处理)
硬盘安装步骤(硬盘的安装教程)

   随着电脑技术不断改进和发展。硬盘的技术也在不断更新,从过去的几百兆、几个G、几十G、几百G到如今的几个T硬盘容量赿来赿大,读写速度赿来趆快。但硬盘安装的步骤几乎...

pe启动工具(pe启动工具排行榜)

u盘pe是可以重复使用的,没有问题的,可以给无数的电脑安装系统。用u盘的做的pe只要不是中病毒,或者你格式化u盘,里面的pe系统一直都是可以用的哈。只是说现在的pe系统最新的更新的技术不一样,...

笔记本电脑按键失灵怎么解决

笔记本电脑按键失灵可能是由于键盘内部灰尘或者物质堵塞,也可能是由于键盘接触不良或者硬件故障导致。修复方法可以尝试清洗键盘或更换键盘,如果还是不行则需要检查硬件或者寻求专业维修人员的帮助。在日常使用中,...

苹果手机imei查询官网(苹果手机imei查询官网免费)

IMEI查询官网:首页:http://www.imeidb.comIMEIdb的客户包括了国内知名手机厂商、IMEIdb始创于2011年IMEIdb是国内唯一的提供面向普通用户和商业用户的IMEI一体...

qq如何修改登录密码(qq怎么修改登录密码)
  • qq如何修改登录密码(qq怎么修改登录密码)
  • qq如何修改登录密码(qq怎么修改登录密码)
  • qq如何修改登录密码(qq怎么修改登录密码)
  • qq如何修改登录密码(qq怎么修改登录密码)
斐讯路由器还能用吗(2021年斐讯路由器还能用吗)

斐讯路由器K2P以上型号的路由器还能用并且还不错。k2p接人个500M左右的网络还可以,K3、K3C更不用说了。斐讯虽然倒闭了,但网络上好多大师给这些路由器续命,适配了不少优秀的路由固件。可以呀,我有...

windows 10下载工具(win10下载软件的工具)

Windows10系统下载软件方法,如果你想使用你的Windows10系统下载软件,那么你可以去你的电脑的应用市场里面,首先下载一个ie的浏览器,这样把你的ie浏览器安装到电脑桌面上面,然后可以在...

取消回复欢迎 发表评论: