本文使用Ffmpeg API实现推流。
一、说明AV_TIME_BASE
ffmpeg中的内部计时单位(时间基),ffmepg中的所有时间都是于它为一个单位,比如AVStream中的duration即以为着这个流的长度为duration个AV_TIME_BASE。AV_TIME_BASE定义为:
#define AV_TIME_BASE 1000000
函数声明
int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd)
直接看代码, 它的作用是计算 “a * b / c” 的值并分五种方式来取整.
但是在FFmpeg中,则是将以 “时钟基c” 表示的 数值a 转换成以 “时钟基b” 来表示。
看AVRounding结构体,就是这五种方式
enum AVRounding {AV_ROUND_ZERO = 0, ///
};
将以时钟基为c 的时间戳a 转换成以b为时钟基并且以rnd 这种方式进行运算的值
函数定义:
int64_t av_rescale_q_rnd(int64_t a, AVRational bq, AVRational cq,enum AVRounding rnd)
{int64_t b = bq.num * (int64_t)cq.den;int64_t c = cq.num * (int64_t)bq.den;return av_rescale_rnd(a, b, c, rnd);
}
函数定义
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)
{return av_rescale_q_rnd(a, bq, cq, AV_ROUND_NEAR_INF);
}
使用示例:
将以"1MHz时钟基" 表示的 “PTS/DTS值a” 转换成以 “90kHz时钟基” 表示。
//调用转换
int64_t av_rescale_q(pkt->pts=-10949117256, src_tb={num=1, den=1000000}, dst_tb{num=1, den=90000))
{return av_rescale_q_rnd(a, bq, cq, AV_ROUND_NEAR_INF);
}
int64_t av_rescale_q_rnd(int64_t a, AVRational bq, AVRational cq,enum AVRounding rnd)
{int64_t b = bq.num * (int64_t)cq.den;// = 1 * 90000 = 90000; int64_t c = cq.num * (int64_t)bq.den; // = 1 * 1000000 = 1000000 return av_rescale_rnd(a, b, c, 5);
}int64_t av_rescale_rnd(a=10949117256, b=90000, c=1000000, rnd=5)
{ if (rnd&#61;&#61;5) r &#61; c / 2; // r &#61;500000; if (b<&#61;INT_MAX && c<&#61;INT_MAX) { if (a<&#61;INT_MAX) return (a * b &#43; r)/c; else return a/c*b &#43; (a%c*b &#43; r)/c; // &#61; 10949117256 / 1000000 * 90000 &#43; // (10949117256 % 1000000 * 90000 &#43; 500000) / 1000000 // &#61; 985420553 } else { ... }
}
视频比较好理解&#xff0c;就是每帧递增&#xff0c;假如fps是25帧的&#xff0c;时间基为fps的倒数1/25&#xff0c;那么pts递增即可。
如下&#xff1a;
计算公式为&#xff1a;第n帧的pts&#61;n∗(&#xff08;1/timbase&#xff09;/fps);第n帧的pts &#61; n * (&#xff08;1 / timbase&#xff09;/ fps);第n帧的pts&#61;n∗(&#xff08;1/timbase&#xff09;/fps);
音频相对来说更难理解一些&#xff0c;因为音频的一个packet不止一帧&#xff0c;所以一秒到底有多少个packet就不知道&#xff0c;就别说如何计算pts了。
假设音频一秒有num_pkt个packet&#xff0c;那么这个num_pkt到底是多少&#xff1f;
这的从音频基础开始说起&#xff0c;我们知道音频有个采样率&#xff0c;就是一秒钟采用多少次&#xff0c;很多音频都是44100的采样率&#xff0c;也有8k的&#xff0c;那么这个采样率和num_pkt有什么关系呢&#xff1f;
我们发现在AVFrame中有一个比较重要的字段叫做nb_samples&#xff0c;这个字段名为采样数&#xff0c;此字段可以结合音频数据格式计算这个frame->data有多大&#xff0c;其实这个字段联合采样率还可以计算音频一秒有多少个packet。
计算公式如下&#xff1a;
numpkt&#61;采样率/nbsamples;num_pkt &#61; 采样率 / nb_samples;numpkt&#61;采样率/nbsamples;
这样我们就知道了音频每秒的包数目&#xff08;可以见到理解为帧&#xff09;&#xff0c;有了此数据计算pts就和视频一模一样了&#xff0c;
计算公式如下&#xff1a;
第n个包的pts &#61; n * (&#xff08;1 / timbase&#xff09;/ num_pkt);
很多音频时间基和采样率成倒数&#xff0c;那么根据公式我们的音频pts就可以很简单的以nb_samples递增了&#xff0c;如下&#xff1a;
第一个包&#xff1a;pts &#61; 0 * nb_samples;
第二个包&#xff1a;pts &#61; 1 * nb_samples;
第三个包&#xff1a;pts &#61; 2 * nb_samples;
.
.
.
第n个包&#xff1a;pts &#61; (n - 1) * nb_samples;
使用QT创建新项目&#xff0c;添加ffmpeg包引用。
#-------------------------------------------------
#
# Project created by QtCreator 2020-05-13T09:08:20
#
#-------------------------------------------------QT &#43;&#61; core guigreaterThan(QT_MAJOR_VERSION, 4): QT &#43;&#61; widgetsTARGET &#61; OutputSample
TEMPLATE &#61; appSOURCES &#43;&#61; main.cpp\mainwindow.cppHEADERS &#43;&#61; mainwindow.hFORMS &#43;&#61; mainwindow.ui
INCLUDEPATH &#43;&#61;"D:\\tools\\ffmpeg\\win32\\dev\\include"LIBS &#43;&#61; -LD:\tools\ffmpeg\win32\dev\lib -lavutil -lavformat -lavcodec -lavdevice -lavfilter -lpostproc -lswresample -lswscale
#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
#endifint main(int argc, char* argv[])
{AVOutputFormat *ofmt &#61; NULL;AVFormatContext *ifmt_ctx &#61; NULL, *ofmt_ctx &#61; NULL;AVPacket pkt;const char *in_filename, *out_filename;int ret, i;int videoindex&#61;-1;int frame_index&#61;0;int64_t start_time&#61;0;in_filename &#61; "D:/1.mp4";//输入URL&#xff08;Input file URL&#xff09;out_filename &#61; "rtmp://rtmp服务器地址:端口/flv/test_1_1";//输出 URL&#xff08;Output URL&#xff09;[RTMP]//out_filename &#61; "rtp://233.233.233.233:6666";//输出 URL&#xff08;Output URL&#xff09;[UDP]// 初始化ffmpegav_register_all();// 初始化网络库avformat_network_init();// 初始化输入if ((ret &#61; avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) <0) {printf( "Could not open input file.");goto end;}if ((ret &#61; avformat_find_stream_info(ifmt_ctx, 0)) <0) {printf( "Failed to retrieve input stream information");goto end;}for(i&#61;0; i
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 !&#61; AVERROR_EOF) {printf( "Error occurred.\n");return -1;}return 0;
}
使用VLC打开 rtmp://rtmp服务器地址:端口/flv/test_1_1 即可查看视频。
参考&#xff1a;https://blog.csdn.net/dancing_night/article/details/45972361
https://www.jianshu.com/p/5634712cfe1b