首页 网站首页 商业信息 推流 查看内容

FFmpeg入门

admin 2023-2-13 15:07 5505人围观 推流

音视频流媒体技术:FFmpeg入门 - 视频播放上一篇博客先容了怎样用ffmpeg去播放视频.

里面用于翻开视频流的avformat_open_input函数除了翻开当地视频之外,现实上也能翻开rtmp协议的远程视频,实现拉流:

./demo -p 当地视频途径

./demo -p rtmp://办事器ip/视频流途径

这篇文章我们来说下怎样实现推流,然后和之前的demo代码配合就能完成推流、拉流的全部进程,实现直播

rtmp办事器

全部直播的功用分红下面三个模块:



从上图我们可以看到rtmp是需要办事器做转发的,我们选用开源的srs.间接从github上把它的源码拉下来编译,然后间接启动即可:

git clone git@github.com:ossrs/srs.git
cd srs/trunk
./configure
make
./etc/init.d/srs start

假如是当地的电脑,这个时辰就能在局域网内间接用它的内网ip去拜候了.但假如是腾讯云、阿里云之类的云办事器还需要设置平安组开放下面几个端口的拜候权限:

listen 1935;
max_connections 1000;
#srs_log_tank file;
#srs_log_file ./objs/srs.log;
daemon on;
http_api {
enabled on;
listen 1985;
}
http_server {
enabled on;
listen 8080;
dir ./objs/nginx/html;
}
rtc_server {
enabled on;
listen 8000; # UDP port
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#config-candidate
candidate $CANDIDATE;
}
...

固然假如这几个端口已经被占用的话可以点窜设置文件conf/srs.conf去点窜
办事器到这里就预备好了,阅读器拜候下面网址对srs停止调试、设置:
http://办事器ip:8080/players/rtc_publisher.html
http://办事器ip:1985/console/ng_index.html

推流

预备输出流

我们挑选推送当地的视频到rtmp办事器,所以第一步照旧是翻开当地视频流:

bool VideoSender::Send(const string& srcUrl, const string& destUrl) {
...
// 翻开文件流读取文件头剖析出视频信息如轨道信息、时长等
// mFormatContext初始化为NULL,假如翻开成功,它会被设备成非NULL的值
// 这个方式现实可以翻开多种来历的数据,url可所以当地途径、rtmp地址等
// 在不需要的时辰经过avformat_close_input封闭文件流
if(avformat_open_input(&inputFormatContext, srcUrl.c_str(), NULL, NULL) < 0) {
cout << "open " << srcUrl << " failed" << endl;
break;
}

// 对于没有文件头的格式如MPEG大概H264裸流等,可以经过这个函数剖析前几帧获得视频的信息
if(avformat_find_stream_info(inputFormatContext, NULL) < 0) {
cout << "can't find stream info in " << srcUrl << endl;
break;
}

// 打印输入视频信息
av_dump_format(inputFormatContext, 0, srcUrl.c_str(), 0);
...
}

【相关进修材料保举,点击下方链接免费报名,先码住不迷路~】

音视频免费进修地址:FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开辟

【纯干货免费分享】C++音视频进修材料包、大厂口试题、技术视频和进修线路图,材料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以点击788280672加群免费支付哦~



当地视频翻开以后,我们建立输出视频流高低文,然后为输出流建立轨道,最初翻开输出视频流:

// 建立输出流高低文,outputFormatContext初始化为NULL,假如翻开成功,它会被设备成非NULL的值,在不需要的时辰利用avformat_free_context开释
// 输出流利用flv格式
if(avformat_alloc_output_context2(&outputFormatContext, NULL, "flv", destUrl.c_str()) < 0) {
cout << "can't alloc output context for " << destUrl << endl;
break;
}

// 拷贝编解码参数
if(!createOutputStreams(inputFormatContext, outputFormatContext)) {
break;
}

// 打印输出视频信息
av_dump_format(outputFormatContext, 0, destUrl.c_str(), 1);

// 翻开输出流,竣事的时辰利用avio_close封闭
if(avio_open(&outputFormatContext->pb, destUrl.c_str(), AVIO_FLAG_WRITE) < 0) {
cout << "can't open avio " << destUrl << endl;
break;
}

这里有个createOutputStreams用于按照当地视频文件的轨道信息,为输出流建立一样的轨道:

static bool createOutputStreams(AVFormatContext* inputFormatContext, AVFormatContext* outputFormatContext) {
// 遍历输入流的一切轨道,拷贝编解码参数到输出流
for(int i = 0 ; i < inputFormatContext->nb_streams ; i++) {
// 为输出流建立轨道
AVStream* stream = avformat_new_stream(outputFormatContext, NULL);
if(NULL == stream) {
cout << "can't create stream, index " << i << endl;
return false;
}

// 编解码参数在AVCodecParameters中保存,从输入流拷贝到输出流
if(avcodec_parameters_copy(stream->codecpar, inputFormatContext->streams[i]->codecpar) < 0) {
cout << "can't copy codec paramters, stream index " << i << endl;
return false;
}

// codec_tag代表了音视频数据采用的码流格式,分歧的封装格式如flv、mp4等的支持情况是纷歧样的
// 上面的avcodec_parameters_copy将输出流的codec_tag从输入拷贝过来酿成了一样的
// 由于我们输出流在avformat_alloc_output_context2的时辰写死了flv格式
// 假如输入流不是flv而是mp4等格式的话便能够会出现mp4里某种codec_tag在flv不支持致使推流失利的情况
// 这里我们可以用av_codec_get_id从输出流的oformat的支持的codec_tag列内里面查找codec_id
// 假如和codecpar的codec_id纷歧致的话代表不支持
if(av_codec_get_id(outputFormatContext->oformat->codec_tag, stream->codecpar->codec_tag) != stream->codecpar->codec_id) {
// 这里将codec_tag设备为0,FFmpeg会按照编码codec_id从封装格式的codec_tag列表中找到一个codec_tag
stream->codecpar->codec_tag = 0;
}
}
return true;
}

codec_id和codec_tag
这里可以看到对于编码器有codec_id和codec_tag两个字段去描写,codec_id代表的是数据的编码范例.而codec_tag用于更具体的描写编解码的格式信息,它对应的是FourCC(Four-Character Codes)数据。
例如codec_id都是AV_CODEC_ID_RAWVIDEO的裸数据,但它能够是YUV的裸数据也能够是RGB的裸数据:

// libavformat/isom.c
{ AV_CODEC_ID_RAWVIDEO, MKTAG('r', 'a', 'w', ' ') }, /* uncompressed RGB */
{ AV_CODEC_ID_RAWVIDEO, MKTAG('y', 'u', 'v', '2') }, /* uncompressed YUV422 */
{ AV_CODEC_ID_RAWVIDEO, MKTAG('2', 'v', 'u', 'y') }, /* uncompressed 8-bit 4:2:2 */
{ AV_CODEC_ID_RAWVIDEO, MKTAG('y', 'u', 'v', 's') }, /* same as 2VUY but byte-swAPPed */

又例如codec_id都是AV_CODEC_ID_H264,但现实上也有很多细分范例:

// libavformat/isom.c
{ AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '1') }, /* AVC-1/H.264 */
{ AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '2') },
{ AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '3') },
{ AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '4') },
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '5', 'p') }, /* AVC-Intra 50M 720p24/30/60 */
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '5', 'q') }, /* AVC-Intra 50M 720p25/50 */
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '5', '2') }, /* AVC-Intra 50M 1080p25/50 */
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '5', '3') }, /* AVC-Intra 50M 1080p24/30/60 */
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '5', '5') }, /* AVC-Intra 50M 1080i50 */
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '5', '6') }, /* AVC-Intra 50M 1080i60 */
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '1', 'p') }, /* AVC-Intra 100M 720p24/30/60 */
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '1', 'q') }, /* AVC-Intra 100M 720p25/50 */
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '1', '2') }, /* AVC-Intra 100M 1080p25/50 */

可以看出来codec_tag是经过4个字母去暗示的,我们来看看MKTAG的界说:

#define MKTAG(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((unsigned)(d) << 24))

终极它获得的是一个整数,例如MKTAG('a', 'v', 'c', '1')获得的值是0x31637661

  • 0x31 =1
  • 0x63 = c
  • 0x76 = v
  • 0x61 = a

我们可以用av_fourcc2str这个函数将终极的整数转换回字符串
回过甚来看看这个判定:

if(av_codec_get_id(outputFormatContext->oformat->codec_tag, stream->codecpar->codec_tag) != stream->codecpar->codec_id)

大部分情况下假如codec_tag在输出流不支持的情况下av_codec_get_id拿到的是AV_CODEC_ID_NONE,所以大部分情况可以等价于:

if(av_codec_get_id(outputFormatContext->oformat->codec_tag, stream->codecpar->codec_tag) != AV_CODEC_ID_NONE)

不外也存在都是MKTAG('l', 'p', 'c', 'm'),但codec_id能够是AV_CODEC_ID_PCM_S16BE大概AV_CODEC_ID_PCM_S16LE的情况:

{ AV_CODEC_ID_PCM_S16BE, MKTAG('l', 'p', 'c', 'm') },
{ AV_CODEC_ID_PCM_S16LE, MKTAG('l', 'p', 'c', 'm') },

{ AV_CODEC_ID_PCM_S16LE, MKTAG('l', 'p', 'c', 'm') },
所以最好还是和原本的codec_id做比力会靠谱点。
写入视频数据
接着就是视频数据的写入了,首要有三个步调,写入文件头、读取当地视频包并写入输出视频流、写入文件结尾:

// 设备flvflags为no_duration_filesize用于处理下面的报错
// [flv @ 0x14f808e00] Failed to update header with correct duration.
// [flv @ 0x14f808e00] Failed to update header with correct filesize
AVDictionary * opts = NULL;
av_dict_set(&opts, "flvflags", "no_duration_filesize", 0);
if(avformat_write_header(outputFormatContext, opts ? &opts : NULL) < 0) {
cout << "write header to " << destUrl << " failed" << endl;
break;
}

// 建立建立AVPacket接收数据包
// 不管是紧缩的音频流还是紧缩的视频流,都是由一个个数据包组成的
// 解码的进程现实就是从文件流中读取一个个数据包传给解码器去解码
// 对于视频,它凡是应包括一个紧缩帧
// 对于音频,它能够是一段紧缩音频、包括多个紧缩帧
// 在不需要的时辰可以经过av_packet_free开释
packet = av_packet_alloc();
if(NULL == packet) {
cout << "can't alloc packet" << endl;
break;
}

...

// 从文件流里面读取出数据包,这里的数据包是编解码层的紧缩数据
while(av_read_frame(inputFormatContext, packet) >= 0) {
// 我们以视频轨道为基准去同步时候
// 假如时候还没有到就增加提早,避免向办事器推流速度过快
...

// 往输出流写入数据
av_interleaved_write_frame(outputFormatContext, packet);

// 写入成以后紧缩数据包的数据就不需要了,将它开释
av_packet_unref(packet);
}

// 写入视频尾部信息
av_write_trailer(outputFormatContext);

帧同步

由于av_read_frame这里读取出来的是未解码的紧缩数据速度很快,假如不做控制一会儿就发送完成了,会形成数据聚积在办事器上。这里我们疏忽收集传输耗时,仍然经过视频包的pts做一定的同步:

while(av_read_frame(inputFormatContext, packet) >= 0) {
// 我们以视频轨道为基准去同步时候
// 假如时候还没有到就增加提早,避免向办事器推流速度过快
if(videoStreamIndex == packet->stream_index) {
if(AV_NOPTS_VALUE == packet->pts) {
// 有些视频流不带pts数据,按30fps将间隔同一成32ms
av_usleep(32000);
} else {
// 带pts数据的视频流,我们计较出每一帧应当在什么时辰播放
int64_t nowTime = av_gettime() - startTime;
int64_t pts = packet->pts * 1000 * 1000 * timeBaseFloat;
if(pts > nowTime) {
av_usleep(pts - nowTime);
}
}
}
// 往输出流写入数据
av_interleaved_write_frame(outputFormatContext, packet);

// 写入成以后紧缩数据包的数据就不需要了,将它开释
av_packet_unref(packet);
}

资本开释

等视频流读写完成以后就是最初的资本开释扫尾工作了:

if(NULL != packet) {
av_packet_free(&packet);
}

if(NULL != outputFormatContext) {
if(NULL != outputFormatContext->pb) {
avio_close(outputFormatContext->pb);
}
avformat_free_context(outputFormatContext);
}

if(NULL != inputFormatContext) {
avformat_close_input(&inputFormatContext);
}

其他
源码和上篇博客的是同一个仓库,编译以后可以经过-s参数推流到办事器:
./demo -s video.flv rtmp://办事器ip/live/livestream

推流的同时就能利用-p参数去拉流停止实时播放:
./demo -p rtmp://办事器ip/live/livestream

这个demo只是简单的将当地视频文件推到办事器,现实上我们可以对他做些点窜就能实现将摄像头的视频流推到办事器了。

作者:嘉伟咯
原文链接:https://ffmpeg.0voice.com/forum.php?mod=viewthread&tid=2019

高端人脉微信群

高端人脉微信群

人脉=钱脉,我们相信天下没有聚不拢的人脉,扫码进群找到你所需的人脉,对接你所需的资源。

商业合作微信

商业合作微信

本站创始人微信,13年互联网营销经验,擅长引流裂变、商业模式、私域流量,高端人脉资源丰富。

精彩点评

相关推荐

详解RTSP推流实战(1)

详解RTSP推流实战(1)

0.引言本篇文章主要讲解RTSP推流实战,整体推流流程与RTMP推流流程类似。如果对于RTSP

视频号推流直播是什么?怎么玩?(附最全操作流程!)

视频号推流直播是什么?怎么玩?(附最全操作流程!)

随着视频号直播的不断完善,美颜、抽奖、推流直播等功能纷纷上线,【附近的直播和人】

抖音直播间推流机制是什么?怎么做能让直播间一直有流量 ... ...

抖音直播间推流机制是什么?怎么做能让直播间一直有流量 ... ...

很多人直播间没有人,就是因为搞不懂抖音直播间推流机制。不明白推流机制就找不到正确

python利用ffmpeg进行rtmp推流直播

python利用ffmpeg进行rtmp推流直播

思路:opencv读取视频 — 将视频分割为帧 — 将每一帧进行需求加工后 — 将此帧写入pi

OBS推流如何实现多平台推流

OBS推流如何实现多平台推流

500强直播策划,策划过多起直播。经常会遇到需要全平台推流的情况,但是OBS的原生软件

这篇微头条是3个月前写的,最近几天被再次推流,它有何特别之处

这篇微头条是3个月前写的,最近几天被再次推流,它有何特别之处

#头条文章养成计划#根据我400多天创作的亲身体验,文章推荐的时间比较长,而微头条的

头条的推流机制,原来是这样的

头条的推流机制,原来是这样的

#头条创作挑战赛#头条推流的机制是怎样的?在头条写作了31天,小编一直被这个问题困扰

在电脑上使用OBS在各大平台 直播 推流的方法

在电脑上使用OBS在各大平台 直播 推流的方法

Open Broadcaster Software(简称 OBS)是一款好用的第三方开源程序直播流媒体内容制

视频和视频帧:ffmpeg的RTMP推流

视频和视频帧:ffmpeg的RTMP推流

写在前面本文将介绍以下内容:什么是推流?将介绍推流常见的协议RTMP,HLS等。怎么用f

西瓜媒体直播教程:30秒学会推流直播

西瓜媒体直播教程:30秒学会推流直播

头条直播换到西瓜后台啦! 推流还是一样方便快捷! 功能更多更

视频号如何推流直播?推流直播详细教程

视频号如何推流直播?推流直播详细教程

​最近有很多同学问我视频号推流直播怎么做,这种三两句话回答不清楚,今天特意写了详

推流与拉流简概

推流与拉流简概

推流:将直播内容推送至服务器的过程拉流:为服务器已有直播内容,用指定地址进行拉取

一篇文章突然停止推流,我只做了2件事,结果收益200元

一篇文章突然停止推流,我只做了2件事,结果收益200元

我是依伊,一个全职写作的创作人,点击右上角关注,为你分享【新媒体写作变现】和【个

OBS直播多平台同时推流解决方法,简单粗暴

OBS直播多平台同时推流解决方法,简单粗暴

用OBS作为电脑直播推流,是很多人使用的一款开源软件。我们使用OBS时,它默认的是只能

3分钟带你了解抖音推流机制!

3分钟带你了解抖音推流机制!

你是否也遇到过辛辛苦苦拍了视频,但结果却还不如跳舞的小姐姐?为什么别人随便拍的视

千万级直播运营必须掌握的OBS推流直播技能

千万级直播运营必须掌握的OBS推流直播技能

专业直播操盘手必须掌握的OBS推流直播技能私域直播母東東,业绩增长分分钟Hello,各位

如何获取抖音直播的推流地址?

如何获取抖音直播的推流地址?

想在直播间中直播游戏,那就需要用到2个东西,分别是推流码和obs软件。那抖音直播推流

视频号问题系列(一):视频号如何开通推流直播

视频号问题系列(一):视频号如何开通推流直播

缘起: 最近工作比较忙、文章也没怎么更新,不过最近 一段时间过来问我视频号问题的朋

玩转直播,直播推流软件你选对了吗?

玩转直播,直播推流软件你选对了吗?

随着近几年互联网技术高速发展,人们对社交形式多样化的需求不断增加,从一开始的文字

直播推流和拉流方法-VLC 播放器专题

直播推流和拉流方法-VLC 播放器专题

直播目前处于一个风口期,很多直播开始跨平台跨地域直播,如何实现异地直播,跨平台直

商业洽谈 文章投递 寻求报道
电话咨询: 15924191378
关注微信