小白學習音視頻系列(三) 使用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推流結束,雖然走了點彎路,但是你還是會成功的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章