美文网首页
FFMPEG之最简单的RTMP推流

FFMPEG之最简单的RTMP推流

作者: d8893ea8ba05 | 来源:发表于2019-10-21 19:15 被阅读0次

    VC环境集成FFMPEG

    • 使用Visual Studio创建一个win32项目,并在解决方案文件的目录下,创建FMPEG文件夹
    • 从官网下载,将dev版本中的include文件夹和bin文件夹拷贝到上面创建的FMPEG文件夹内
    • 在项目--属性--配置属性--C/C++--常规--附加包含目录中添加“../FFMPEG/include”
    • 在项目--属性--配置属性--链接器--常规--附加目录库中添加“../FFMPEG/lib”
    • 在项目--属性--配置属性--链接器--输入--附加依赖项中添加lib的文件名:avcodec.lib;avdevice.lib;avfilter.lib;avformat.lib;avutil.lib;postproc.lib;swresample.lib;swscale.lib;
    • 将share版本中bin目录下的dll拷贝到解决方案的Debug目录下
    • 将一个flv文件拷贝到Debug目录下

    FFMPEG相关概念

    • 为什么会有DTS解码时间戳和PTS显示时间戳
      通常一个视频的视频帧是这样的顺序来播放的,IBBP,但是由于算法的原因,可能在展示B帧的时候,需要用到P帧的数据,因此实际上的存储顺序可能是IPBB,所以对于帧而言,他的解码顺序与播放顺序并不一致。如果我们仅仅使用简单的帧数乘以频率的方式来同步视频,播放端在改变了播放速度或者播放时间的时候,就无法正确同步了
    • time_base的作用
      在视频内没有绝对时间,只有相对时间,如00:00:05是指相对于起始点来讲的,在视频内部使用时间戳来表示,时间戳是按照时间刻度来计算的,也就是time_base,如果time_base是1/60,就表示一个时间单位是1/60秒。如果视频在编码的时候采用了1/1000的time_base,某个帧的pts是465000,在视频解码时,换成了1/9000的time_base,此时时间刻度不一致了,就需要通过计算才能得到解码时的pts
    • AVRational
    typedef struct AVRational{
        int num; ///< Numerator
        int den; ///< Denominator
    } AVRational;
    

    说白了就是标识一个分数,num是分子,den是分母,用av_q2d来表示这个分数的double结果

    一个RTMP推流的代码

    #include <iostream>
    
    extern "C"
    {
    #include "libavformat/avformat.h"
    #include "libavutil/mathematics.h"
    #include "libavutil/time.h"
    };
    
    int avError(int errNum);
    
    int main(int argc, char* argv[]){
        //输入文件
        const char *fileAddress = "cuc_ieschool.flv";
        //推流地址
        const char *rtmpAddress = "rtmp://video-center.alivecdn.com/record/1513?vhost=im-broadcast17.sdp.101.com&auth_key=1843197296-0-0-f6bac834f49cce39c3fd5f3b7ec0bcb1";
    
        //注册所有库
        av_register_all();
        //初始化网络库
        avformat_network_init();
    
    
        ////////////////////////////////////////////////////////////////
        //                       输入流处理部分                      ///
        ////////////////////////////////////////////////////////////////
        AVFormatContext *ictx = NULL;
        //打开文件
        int ret = avformat_open_input(&ictx, fileAddress, 0, NULL);
        if (ret < 0)
        {
            return avError(ret);
        }
        std::cout << "avformat_open_input succeeded" << std::endl;
        //获取流信息
        ret = avformat_find_stream_info(ictx, 0);
        if (ret != 0)
        {
            return avError(ret);
        }
        //打印视频信息
        av_dump_format(ictx, 0, fileAddress, 0);
    
    
        ////////////////////////////////////////////////////////////////
        //                       输出流处理部分                      ///
        ////////////////////////////////////////////////////////////////
        AVFormatContext *octx = NULL;
        //创建输出上下文
        ret = avformat_alloc_output_context2(&octx, NULL, "flv", rtmpAddress); 
        if (ret < 0) {
            return avError(ret);
        }
        std::cout << "avformat_alloc_output_context2 succeeded" << std::endl;
        //配置输出流
        for (int i = 0; i < ictx->nb_streams; i++) {
            //创建一个新的流
            AVStream *outStream = avformat_new_stream(octx, ictx->streams[i]->codec->codec);
            if (!outStream) {
                return avError(0);
            }
            //复制配置信息
            ret = avcodec_parameters_copy(outStream->codecpar, ictx->streams[i]->codecpar);
            if (ret < 0) {
                return avError(ret);
            }
            outStream->codec->codec_tag = 0;
        }
        //打印输出流的信息
        av_dump_format(octx, 0, rtmpAddress, 1);
    
    
        ////////////////////////////////////////////////////////////////
        //                         准备推流                          ///
        ////////////////////////////////////////////////////////////////
        //打开io
        ret = avio_open(&octx->pb, rtmpAddress, AVIO_FLAG_WRITE);
        if (ret < 0) {
            avError(ret);
        }
        //写入头部信息
        ret = avformat_write_header(octx, NULL);
        if ( ret < 0) {
            avError(ret);
        }
        std::cout << "avformat_write_header succeeded" << std::endl;
        //推流每一帧数据
        AVPacket avPacket;
        long long startTime = av_gettime();
        while (true)
        {
            ret = av_read_frame(ictx, &avPacket);
            if (ret < 0 )
            {
                break;
            }
            std::cout << avPacket.pts << " " << std::flush;
            //计算转换时间戳
            //获取时间基数
            AVRational itime = ictx->streams[avPacket.stream_index]->time_base;
            AVRational otime = octx->streams[avPacket.stream_index]->time_base;
            avPacket.pts = av_rescale_q_rnd(avPacket.pts, itime, otime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
            avPacket.dts = av_rescale_q_rnd(avPacket.dts, itime, otime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
            //到这一帧经历了多长时间
            avPacket.duration = av_rescale_q_rnd(avPacket.duration, itime, otime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
            avPacket.pos = -1;
            //视频帧推送速度
            if (ictx->streams[avPacket.stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
            {
                AVRational tb = ictx->streams[avPacket.stream_index]->time_base;
                //已经过去的时间
                long long now = av_gettime() - startTime;
                long long dts = 0;
                dts = avPacket.dts * (1000 * 1000 * av_q2d(tb));
                if (dts > now)
                {
                    av_usleep(dts - now);
                }
            }
            ret = av_interleaved_write_frame(octx, &avPacket);
            if (ret < 0)
            {
                break;
            }
        }
        
    
        std::cin.get();
        return 0;
    }
    
    int avError(int errNum) {
        char buf[1024];
        //获取错误信息
        av_strerror(errNum, buf, sizeof(buf));
        std::cout << " failed! " << buf << std::endl;
        return -1;
    }
    

    相关文章

      网友评论

          本文标题:FFMPEG之最简单的RTMP推流

          本文链接:https://www.haomeiwen.com/subject/vqolmctx.html