FFmpeg菜鸡互啄#第7篇#文件/rtsp推流到rtmp

关键步骤

    avformat_open_input//打开输入文件/rtsp
    avformat_find_stream_info////获取音视频流信息
    avformat_alloc_output_context2//创建输出上下文
    avformat_new_stream//创建输出流
    avcodec_copy_context//复制配置输出流
    avio_open//打开io
    avformat_write_header//写入头信息
    av_interleaved_write_frame//推流帧
    av_write_trailer(octx);//写文件尾

大致过程:打开(/配置)输入、创建输出(上下文/流)、配置输出流、打开输出IO、循环推流。推流本地文件的时候要计算一下pts、dts,并作延时推送。

Code

#include <stdio.h>

extern "C"
{
#include "libavformat\avformat.h"
#include "libavutil\time.h"
}

#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "avcodec.lib")

int Error(int res)
{
    char buf[1024] = { 0 };
    av_strerror(res, buf, sizeof(buf));
    printf("error : %s.\n", buf);
    return res;
}

int main(int argc, char* argv[])
{
    char* inUrl = "rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov";//可以是本地文件
    char* outUrl = "rtmp://123.207.71.137/live/test";

    //初始化所有封装器
    av_register_all();

    //初始化网络库
    avformat_network_init();

    int res = 0;
    //打开文件,解封装文件头
    //输入封装上下文
    AVFormatContext* ictx = NULL;
    //设置rtsp协议延时最大值
    AVDictionary *opts = NULL;
    av_dict_set(&opts, "max_delay", "500", 0);
    if ((res = avformat_open_input(&ictx, inUrl, NULL, &opts)) != 0)
        return Error(res);

    //获取音视频流信息
    if ((res = avformat_find_stream_info(ictx, NULL)) < 0)
        return Error(res);
    av_dump_format(ictx, 0, inUrl, 0);

    //创建输出上下文
    AVFormatContext* octx = NULL;
    if ((res = avformat_alloc_output_context2(&octx, NULL, "flv", outUrl) < 0))
        return Error(res);

    //配置输出流
    //遍历输入的AVStream
    for (int i = 0; i < ictx->nb_streams; ++i)
    {
        //创建输出流
        AVStream* out = avformat_new_stream(octx, ictx->streams[i]->codec->codec);
        if (out == NULL)
        {
            printf("new stream error.\n");
            return -1;
        }
        //复制配置信息
        if ((res = avcodec_copy_context(out->codec, ictx->streams[i]->codec)) != 0)
            return Error(res);
        //out->codec->codec_tag = 0;//标记不需要重新编解码
    }
    av_dump_format(octx, 0, outUrl, 1);

    //rtmp推流
    //打开io
    //@param s Used to return the pointer to the created AVIOContext.In case of failure the pointed to value is set to NULL.
    res = avio_open(&octx->pb, outUrl, AVIO_FLAG_WRITE);
    if (octx->pb == NULL)
        return Error(res);

    //写入头信息
    //avformat_write_header可能会改变流的timebase
    if ((res = avformat_write_header(octx, NULL)) < 0)
        return Error(res);

    long long  begintime = av_gettime();
    long long  realdts = 0;
    long long  caldts = 0;
    AVPacket pkt;
    while (true)
    {
        if ((res = av_read_frame(ictx, &pkt)) != 0)
            break;
        if(pkt.size <= 0)//读取rtsp时pkt.size可能会等于0
            continue;
        //转换pts、dts、duration
        pkt.pts = pkt.pts * av_q2d(ictx->streams[pkt.stream_index]->time_base) / av_q2d(octx->streams[pkt.stream_index]->time_base);
        pkt.dts = pkt.dts * av_q2d(ictx->streams[pkt.stream_index]->time_base) / av_q2d(octx->streams[pkt.stream_index]->time_base);
        pkt.duration = pkt.duration * av_q2d(ictx->streams[pkt.stream_index]->time_base) / av_q2d(octx->streams[pkt.stream_index]->time_base);
        pkt.pos = -1;//byte position in stream, -1 if unknown

        //文件推流计算延时
        //av_usleep(30 * 1000);
        /*realdts = av_gettime() - begintime;
        caldts = 1000 * 1000 * pkt.pts * av_q2d(octx->streams[pkt.stream_index]->time_base);
        if (caldts > realdts)
            av_usleep(caldts - realdts);*/

        if ((res = av_interleaved_write_frame(octx, &pkt)) < 0)//推流,推完之后pkt的pts,dts竟然都被重置了!而且前面几帧还因为dts没有增长而返回-22错误
            Error(res);

        av_free_packet(&pkt);//回收pkt内部分配的内存
    }
    av_write_trailer(octx);//写文件尾

    system("pause");
    return 0;
}

Github

https://github.com/gongluck/FFmpegTest.git

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏运维技术迷

从Mysql备份中恢复单个表

因为云平台的备份是把库中所有的表都打包成一个 .sql文件,然而这一个.sql文件大约有20G,现阶段的方法是把.sql文件source到数据库数据处理机器上,...

37911
来自专栏数据和云

高频错误:ORA-01555深入剖析

黄玮(Fuyuncat) 资深Oracle DBA,个人网站www.HelloDBA.com,致力于数据库底层技术的研究,其作品获得广大同行的高度评价. ORA...

2958
来自专栏杨建荣的学习笔记

MySQL高可用方案MGR+consul组合测试

今天来简单理一下MGR和consul的组合方案,前期的准备和步骤还是比较多的,晚上完成了基础的调试,来来回回切换了好多次,还算有点意思。

2693
来自专栏杨建荣的学习笔记

关于视图和存储过程的权限问题探究 (r9笔记第87天)

今天在处理一个工单的时候发现了一个奇怪的现象,开发同学需要创建一个存储过程,目前的架构类似这样的形式 ? 数据库中存在一个属主用户,表,存储过程等对象...

35310
来自专栏数据分析

[SQLServer大对象]——FileTable初体验

在我接触FileTable之前,存储文件都是存储文件的链接和扩展名到数据,其实并没有实际的把文件存储到数据库。 FileTable不同于一般的表,他可以存储非结...

3666
来自专栏逸鹏说道

zabbix最新SQL注入漏洞+EXP

最近zabbix又出大事了,高危的SQL注入漏洞,影响V3.0.4以下所有版本,请小伙伴及时修复。 漏洞概述: zabbix是一个开源的企业级性能监控解决方案。...

3418
来自专栏张善友的专栏

基于MongoDB GridFS的图片存储

它是mongodb的一个子模块,使用GridFS可以基于mongodb来持久存储文件.并且支持分布式应用(文件分布存储和读取).GridFS是mongodb中用...

42310
来自专栏高爽的专栏

增量接口的设计及实现

引言 在应用开发过程中,我们总会碰到这样的场景:某系统需要同步我们系统的数据去做一些业务逻辑,当数据量较小的时候,可以全量的提供,但当数据量很大时,全量提供就显...

3380
来自专栏数据和云

Oracle 12.2新特性掌上手册 - 第一卷 Availability

注:文章内容来自官方文档翻译。若需要了解更多,请查阅官方文档。 1、Multi-Instance Redo Apply (多实例redo应用) 在Oracle ...

3836
来自专栏数据和云

Oracle 12.2新特性掌上手册 - 第六卷 ADG的性能与诊断

编辑手记:在Oracle 12.2中,ADG有许多惊人的改进,通过ADG standby数据库的性能数据收集和诊断、快照standby数据库的应用,以及实时的数...

3717

扫码关注云+社区