Bootstrap

小白学习音视频系列(三) 使用FFmpeg推流

小白学习音视频系列(三)

使用FFmpeg代码推流

image

关注微信号:cpp手艺人,获取更多文章


这一节主要是通过FFmpeg推流,主要就是代码部分,我做了详细的注解

这里有几个注意点还是需要注意下的

1.首先要记得开启nginx rtmp服务
2.服务端口记得要开启
3.ip地址需要换成你自己
4.当你推流成功之后,点击bin目录下的play.bat,同时注意下把play.bat里面的ip地址换成你自己的,就会启动ffplay.exe拉流

#include "common.h"

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

#include <iostream>

using std::cout;
using std::endl;

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

int main(int argc, char *argv[])
{
    char *in_url = "test.flv";
    // 初始化所有封装和解封装 flv mp4 mov mp3
    av_register_all();

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

    ///
    // 输入流打开文件,解封装
    // 输入封装上下文
    AVFormatContext *ictx = NULL;

    // 打开文件,解封文件头
    int ret_code = avformat_open_input(&ictx, in_url, 0, 0);
    if (ret_code < 0) { return av_error(ret_code); }

    // 获取音视频流信息,h264 flv
    ret_code = avformat_find_stream_info(ictx, 0);
    if (ret_code < 0) { return av_error(ret_code); }

    cout << "---------------------------------" << endl;
    av_dump_format(ictx, 0, in_url, 0);
    cout << "---------------------------------" << endl;

    ///
    // 输出流
    char *out_url = "rtmp://192.168.26.31/live";
    AVFormatContext *octx = NULL;
    ret_code = avformat_alloc_output_context2(&octx, 0, "flv", out_url);
    if (NULL == octx) { return av_error(ret_code); }

    for (int i = 0; i < ictx->nb_streams; ++i) {
        // 创建输出流
        AVStream *out = avformat_new_stream(octx, ictx->streams[i]->codec->codec);
        if (NULL == out) { return av_error(0); }

        ret_code = avcodec_parameters_copy(out->codecpar, ictx->streams[i]->codecpar);
        out->codec->codec_tag = 0;
    }

    cout << "---------------------------------" << endl;
    av_dump_format(octx, 0, out_url, 1);
    cout << "---------------------------------" << endl;


    
    //RTMP推流
    ret_code = avio_open(&octx->pb, out_url, AVIO_FLAG_WRITE);
    if (NULL == octx->pb) { return av_error(ret_code); }

    // 写入头信息
    ret_code = avformat_write_header(octx, 0);
    if (ret_code < 0) { return av_error(ret_code); }
    cout << "avformat_write_header" << endl;

    AVPacket pkt;
    int64_t last_pts = 0;
    long long startTime = av_gettime();
    for (;;) {
        ret_code = av_read_frame(ictx, &pkt);

        if (0 != ret_code) { break; }
        
        // 跳过异常的pts
        if (last_pts > pkt.pts) { continue; }

        cout << pkt.pts << " " << std::flush;

        // 计算转换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;
        last_pts = pkt.pts;

        // 视频帧推送速度
        if (ictx->streams[pkt.stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            AVRational tb = ictx->streams[pkt.stream_index]->time_base;
            // 过去的时间
            long long now = av_gettime()-startTime;
            long long dts = 0;
            dts = pkt.dts * (1000 * 1000 * r2d(tb));

            if (dts > now) { av_usleep(dts-now); }
        }

        ret_code = av_interleaved_write_frame(octx, &pkt);
        // 注意出错不用直接返回,否则推流会直接结束
        if (ret_code < 0) { /*return av_error(ret_code);*/ }
    }

    getchar();

    return 0;
}

image
github地址:https://github.com/MingYueRuYa/FFmpeg-RTMP

好,至此,ffmpeg推流结束,虽然走了点弯路,但是你还是会成功的。

;