ffmpeg 推流器代码如下:
#include
using namespace std;
//引入头文件
extern "C"
{
#include "libavformat/avformat.h"
//引入时间
#include "libavutil/time.h"
#include
}
//引入库
#pragma comment(lib,"avformat.lib")
//工具库,包括获取错误信息等
#pragma comment(lib,"avutil.lib")
//编解码的库
#pragma comment(lib,"avcodec.lib")
int avError(int errNum);
int httRtmp();
static double r2d(AVRational r)
{
return r.num == 0 || r.den == 0 ? 0. : (double)r.num / (double)r.den;
}
int main() {
httRtmp();
getchar();
return 0;
}
int avError(int errNum) {
char buf[1024];
//获取错误信息
av_strerror(errNum, buf, sizeof(buf));
cout << " failed! " << buf << endl;
return -1;
}
//推送本地文件到rtmp服务器
int httRtmp() {
int videoindex = -1;
int audioindex = -1;
//所有代码执行之前要调用av_register_all和avformat_network_init
//初始化所有的封装和解封装 flv mp4 mp3 mov。不包含编码和解码
av_register_all();
//初始化网络库
avformat_network_init();
//使用的相对路径,执行文件在bin目录下。test.mp4放到bin目录下即可
const char *inUrl = "testvideo.mp4";
//const char *inUrl = "rtmp://103.229.149.171/myapp/GoProCut";
//const char *inUrl = "http://liveali.ifeng.com/live/CCTV.m3u8?time=1540543965804http://liveali.ifeng.com/live/CCTV.m3u8?time=1540543965804";
//输出的地址
const char *outUrl = "rtmp://3891.livepush.myqcloud.com/live/3891_user_b5c2d4be_4397?bizid=3891&txSecret=d90779bae4f294f647c98823eaa20dbf&txTime=5BEB8A45";
//////////////////////////////////////////////////////////////////
// 输入流处理部分
/////////////////////////////////////////////////////////////////
//打开文件,解封装 avformat_open_input
//AVFormatContext **ps 输入封装的上下文。包含所有的格式内容和所有的IO。如果是文件就是文件IO,网络就对应网络IO
//const char *url 路径
//AVInputFormt * fmt 封装器
//AVDictionary ** options 参数设置
AVFormatContext *ictx = NULL;
AVOutputFormat *ofmt = NULL;
//打开文件,解封文件头
int ret = avformat_open_input(&ictx, inUrl, NULL, NULL);
if (ret < 0) {
return avError(ret);
}
cout << "avformat_open_input success!" << endl;
//获取音频视频的信息 .h264 flv 没有头信息
ret = avformat_find_stream_info(ictx, 0);
if (ret != 0) {
return avError(ret);
}
//打印视频视频信息
//0打印所有 inUrl 打印时候显示,
av_dump_format(ictx, 0, inUrl, 0);
//////////////////////////////////////////////////////////////////
// 输出流处理部分
/////////////////////////////////////////////////////////////////
AVFormatContext * octx = NULL;
//如果是输入文件 flv可以不传,可以从文件中判断。如果是流则必须传
//创建输出上下文
ret = avformat_alloc_output_context2(&octx, NULL, "flv", outUrl);
if (ret < 0) {
return avError(ret);
}
cout << "avformat_alloc_output_context2 success!" << endl;
ofmt = octx->oformat;
cout << "nb_streams " << ictx->nb_streams << endl;
int i;
//for (i = 0; i < ictx->nb_streams; i++) {
// cout << "i " << i <<" "<< ictx->nb_streams<< endl;
// AVStream *in_stream = ictx->streams[i];
// AVCodec *codec = avcodec_find_decoder(in_stream->codecpar->codec_id);
// AVStream *out_stream = avformat_new_stream(octx, codec);
// if (!out_stream) {
// printf("Failed allocating output stream\n");
// ret = AVERROR_UNKNOWN;
// }
// AVCodecContext *pCodecCtx = avcodec_alloc_context3(codec);
// ret = avcodec_parameters_to_context(pCodecCtx, in_stream->codecpar);
// if (ret < 0) {
// printf("Failed to copy context input to output stream codec context\n");
// }
// pCodecCtx->codec_tag = 0;
// if (octx->oformat->flags & AVFMT_GLOBALHEADER) {
// pCodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;
// }
// ret = avcodec_parameters_from_context(out_stream->codecpar, pCodecCtx);
// if (ret < 0) {
// printf("Failed to copy context input to output stream codec context\n");
// }
//}
for (i = 0; i < ictx->nb_streams; i++) {
//获取输入视频流
AVStream *in_stream = ictx->streams[i];
//为输出上下文添加音视频流(初始化一个音视频流容器)
AVStream *out_stream = avformat_new_stream(octx, in_stream->codec->codec);
if (!out_stream) {
printf("未能成功添加音视频流\n");
ret = AVERROR_UNKNOWN;
}
//将输入编解码器上下文信息 copy 给输出编解码器上下文
//ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);
//ret = avcodec_parameters_from_context(out_stream->codecpar, in_stream->codec);
//ret = avcodec_parameters_to_context(out_stream->codec, in_stream->codecpar);
if (ret < 0) {
printf("copy 编解码器上下文失败\n");
}
out_stream->codecpar->codec_tag = 0;
out_stream->codec->codec_tag = 0;
if (octx->oformat->flags & AVFMT_GLOBALHEADER) {
out_stream->codec->flags = out_stream->codec->flags | 0;
}
}
//输入流数据的数量循环
for (i = 0; i < ictx->nb_streams; i++) {
if (ictx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
videoindex = i;
break;
}
}
for (int i = 0; i < ictx->nb_streams; i++)
{
if (ictx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
audioindex = i;
break;
}
}
av_dump_format(octx, 0, outUrl, 1);
//////////////////////////////////////////////////////////////////
// 准备推流
/////////////////////////////////////////////////////////////////
//打开IO
ret = avio_open(&octx->pb, outUrl, AVIO_FLAG_WRITE);
if (ret < 0) {
avError(ret);
}
//写入头部信息
ret = avformat_write_header(octx, 0);
if (ret < 0) {
avError(ret);
}
cout << "avformat_write_header Success!" << endl;
//推流每一帧数据
//int64_t pts [ pts*(num/den) 第几秒显示]
//int64_t dts 解码时间 [P帧(相对于上一帧的变化) I帧(关键帧,完整的数据) B帧(上一帧和下一帧的变化)] 有了B帧压缩率更高。
//uint8_t *data
//int size
//int stream_index
//int flag
AVPacket pkt;
//获取当前的时间戳 微妙
long long start_time = av_gettime();
long long frame_index = 0;
const AVBitStreamFilter *filter = av_bsf_get_by_name("aac_adtstoasc");
while (1) {
//输入输出视频流
AVStream *in_stream, *out_stream;
//获取解码前数据//读包
ret = av_read_frame(ictx, &pkt);
if (ret < 0) {
cout << "获取解码前数据shibai!" << endl;
break;
}
/*
PTS(Presentation Time Stamp)显示播放时间
DTS(Decoding Time Stamp)解码时间
*/
//没有显示时间(比如未解码的 H.264 )
if (pkt.pts == AV_NOPTS_VALUE) {
cout << "获取解码前数据AV_NOPTS_VALUE!" << endl;
//AVRational time_base:时基。通过该值可以把PTS,DTS转化为真正的时间。
AVRational time_base1 = ictx->streams[videoindex]->time_base;
//计算两帧之间的时间
/*
r_frame_rate 基流帧速率 (不是太懂)
av_q2d 转化为double类型
*/
int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(ictx->streams[videoindex]->r_frame_rate);
//配置参数
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);
}
//延时
if (pkt.stream_index == videoindex) {
cout << "延时!" << endl;
AVRational time_base = ictx->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;
AVRational avr = ictx->streams[videoindex]->time_base;
cout << avr.num << "den= " << avr.den << " dts= " << pkt.dts << " pts= " << pkt.pts << " pts_time " << pts_time << endl;
cout << "dts=" << pkt.dts << "now_time=" << now_time << "pts_time=" << pts_time << endl;
if (pts_time > now_time) {
cout << "睡眠一段时间="<< (pts_time - now_time) << endl;
//睡眠一段时间(目的是让当前视频记录的播放时间与实际时间同步)
av_usleep((unsigned int)(pts_time - now_time));
}
}
cout << "dd====" << endl;
in_stream = ictx->streams[pkt.stream_index];
out_stream = octx->streams[pkt.stream_index];
//计算延时后,重新指定时间戳
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 = (int)av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
//字节流的位置,-1 表示不知道字节流位置
pkt.pos = -1;
if (pkt.stream_index == videoindex ) {
printf("Send %8d video frames to output URL\n", frame_index);
frame_index++;
}
//向输出上下文发送(向地址推送)//发送包文件
//处理hls中包含adts heard 的问题 acc_adtstoasc
if (pkt.stream_index == 4) {
AVBSFContext *bsf_ctx;
if (!filter)
{
av_log(NULL, AV_LOG_ERROR, "Unkonw bitstream filter");
}
//2.过滤器分配内存
int ret = av_bsf_alloc(filter, &bsf_ctx);
//把pkt数据推送到filter中去
ret = av_bsf_send_packet(bsf_ctx, &pkt);
if (ret < 0) {
printf("chulishibai\n");
}
//获取处理后的数据,用同一个pkt
ret = av_bsf_receive_packet(bsf_ctx, &pkt);
if (ret < 0) {
printf("chulishibai\n");
}
av_bsf_free(&bsf_ctx);
}
ret = av_interleaved_write_frame(octx, &pkt);
if (ret < 0) {
printf("发送数据包出错\n");
break;
}
//释放
av_free_packet(&pkt);
}
return 0;
}