38.FFmpeg+OpenCV直播推流(FFmpeg api实现以文件为输入源做推流)

今天我们来ffmpeg api编程实现推流并测试效果
项目源码

开发环境

Visual Studio 2015 + FFmpeg-3.2 + nginx服务器

开发过程
1.环境准备

首先在FFmpeg官网下载ffmpeg dev 和share sdk


ffmpeg下载类型.png

下载解压之后,打开文件夹目录,拷贝dev中的include和lib目录,shared中的bin目录,然后创建一个项目目录,将这三个目录拷贝进去,方便我们编写代码时配置相对路径。其中bin存放的都是dll动态库文件,include存放的是头文件,lib存放的是每一个dll对应的lib文件


QQ图片20181125115923.png

然后创建src目录用于我们的项目根目录,我们将会使用Visual Studio在这个目录下创建控制台项目来编写推流代码。

项目创建完成之后,需要对AS做一些配置:

a.右键项目名打开属性,在配置属性的常规选项中的输出目录配置一个目录用于存放我们编译后的文件,这里直接设置为上边创建的bin目录


QQ图片20181125120741.png

b.然后同样在配置属性下找到调试,设置工作目录为bin


QQ图片20181125120901.png

然后在编译时就会去这里找动态库文件

c.下一步在C/C++选项中找到常规,设置头文件目录


QQ图片20181125121143.png

d.在链接器下配置lib目录位置


QQ图片20181125121255.png

这样一来,VS的工作环境就搭建好了

2.代码编写

接下来正式开始写代码,我们这次的推流以本地文件为源进行推,推流的过程大概是这么几步

1.ffmpeg初始化

av_register_all注册所有封装器和解封装器以及协议,这个方法在前边文章已经提到过

avformat_network_init初始化网络相关的东西,推流必须用到这个

av_register_all();
avformat_network_init();
2.读取源文件

avformat_open_input打开一个流读取流的文件头信息,如果没有文件头则无法读取到正确的信息

avformat_find_stream_info 针对一些不包含头信息的源(MPEG)很有效,调用防止无法读取到文件的正确信息

avformat_open_input(&ictx, input, 0, &opts);
avformat_find_stream_info(ictx, 0);
3.推流的准备工作

源文件信息我们已经读取到了,接下来开始准备推流,推流之前我们需要构造出几个对象:
AVFormatContext:输出格式上下文
AVStream:输出流,有几条stream就构造几个

avcodec_parameters_copy的作用是将输入流也就是源文件的parameter信息拷贝到输出流的参数中

avformat_alloc_output_context2(&octx, 0, "flv", output)
avformat_new_stream(octx, ictx->streams[i]->codec->codec)
avcodec_parameters_copy(out->codecpar, ictx->streams[i]->codecpar)
4.开始推流

avio_open会初始化出一个AVIOContext对象,我们可以把他看作一个普通的io流对象,只不过区别在于它是向流媒体服务器写入数据的

avformat_write_header是初始化推流的头信息

av_read_frame是在一个循环中进行的,不断的读取源得到AVPacket,然后由av_interleaved_write_frame负责开始推

avio_open(&octx->pb, output, AVIO_FLAG_WRITE);
avformat_write_header(octx, 0);
av_read_frame(ictx, &pkt)
av_interleaved_write_frame(octx, &pkt);

推流的过程很简单,不过其中涉及到一点比较重要的东西就是推流中的速度控制,我们可以设置一个时间基准,通过对比packet的pts和时间基准的大小来决定推送的速度,从而实现正常效果的推送,关键代码如下

//推流每一帧数据
    AVPacket pkt;
    long long startTime = av_gettime();
    for (;;) {
        result = av_read_frame(ictx, &pkt);
        if (result != 0) {
            break;
        }
        //计算转换时间戳pts dts
        AVRational itime = ictx->streams[pkt.stream_index]->time_base;
        AVRational otime = octx->streams[pkt.stream_index]->time_base;
        pkt.pts = av_rescale_q_rnd(pkt.pts, itime, otime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
        pkt.dts = av_rescale_q_rnd(pkt.pts, itime, otime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
        pkt.duration = av_rescale_q_rnd(pkt.duration, itime, otime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
        pkt.pos = -1;
        
        //视频帧推送速度
        if (ictx->streams[pkt.stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            //获取视频的时间戳
            AVRational rational = ictx->streams[pkt.stream_index]->time_base;
            //从开始解码到现在过去的时间,可以作为推流进行的时间,用当前帧的pts做对比,进行同步
            long long now = av_gettime() - startTime;
            long long dts = 0;
            //单位微秒
            dts = pkt.dts*r2d(rational)*1000*1000;

            //说明推送太快,等一等
            if (dts > now) {
                av_usleep(dts - now);
                cout << "等待"<< dts - now<< endl;
            }
            else {
                cout << "无需等待" << dts - now << endl;
            }
        }

        result = av_interleaved_write_frame(octx, &pkt);
    
        if (result < 0) {
            return XError(result);
        }

        //av_packet_unref(&pkt);
    }
总结

本篇文章简单讲述了ffmpeg实现推流的过程,推流服务器使用的是nginx,所以在开始推之前要确保nginx服务器已经开始,并且正确配置服务器的ip地址,推流之后我们可以通过vlc播放器测试推流效果。本文代码比较简要,具体可查看源码项目源码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容

  • 明天周末了,第一次周末让我感到兴奋和激动。因为以前都是没有周末的(可怜兮兮),突然成为有周末,而且是双休不...
    灵轩_f7e9阅读 182评论 0 0
  • 今天家人聚会举杯共饮,一不小心喝多了,生平第一次喝多了,晕晕的吐了很多,但脑子清晰明白作业还没完成,要写分享...
    跑步遇到最好的自己阅读 193评论 0 0
  • 在三里屯的下午,阳光正好,均匀的散落在咖啡飘香的玻璃窗旁,与他一起去看了倪妮的新作。 二十八岁我还未成年,坐在影院...
    邊思文阅读 272评论 0 1
  • 今天的版纳,上午大雨倾盆,中午便雨过天晴。满眼的绿意肆意的生长,房檐下断断续续的滴着雨,拍打在绿叶上的声音错落有致...
    格小主阅读 315评论 0 1