Android 音視頻深入 十五 FFmpeg 實現基於Rtmp協議的推流(附源碼下載)

來源:互聯網
上載者:User

標籤:音視頻   FFmpeg   Rtmp   推流   

源碼地址
https://github.com/979451341/Rtmp

1.配置RTMP伺服器

這個我不多說貼兩個部落格分別是在mac和windows環境上的,大家跟著弄
MAC搭建RTMP伺服器
https://www.jianshu.com/p/6fcec3b9d644
這個是在windows上的,RTMP伺服器搭建(crtmpserver和nginx)

https://www.jianshu.com/p/c71cc39f72ec
2.關於推流輸出的ip地址我好好說說

我這裡是手機開啟熱點,電腦串連手機,這個RTMP伺服器的推流地址有localhost,伺服器在電腦上,對於電腦這個localhost是127.0.0.1,但是對於外界比如手機,你不能用localhost,而是用這個電腦的在這個熱點也就是區域網路的ip地址,不是127.0.0.1這個只代表本裝置節點的ip地址,這個你需要去手機設定——》更多——》移動網際網路共用——》攜帶型WLAN熱點——》管理裝置列表,就可以看到電腦的區域網路ip地址了

3.說說代碼

註冊組件,第二個如果不加的話就不能擷取網路資訊,比如類似url

av_register_all();avformat_network_init();

擷取輸入視頻的資訊,和建立輸出url地址的環境

    av_dump_format(ictx, 0, inUrl, 0);    ret = avformat_alloc_output_context2(&octx, NULL, "flv", outUrl);    if (ret < 0) {        avError(ret);        throw ret;    }

將輸入視頻流放入剛才建立的輸出資料流裡

    for (i = 0; i < ictx->nb_streams; i++) {        //擷取輸入視頻流        AVStream *in_stream = ictx->streams[i];        //為輸出上下文添加音視頻流(初始化一個音視頻流容器)        AVStream *out_stream = avformat_new_stream(octx, in_stream->codec->codec);        if (!out_stream) {            printf("未能成功添加音視頻流\n");            ret = AVERROR_UNKNOWN;        }        if (octx->oformat->flags & AVFMT_GLOBALHEADER) {            out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;        }        ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);        if (ret < 0) {            printf("copy 轉碼器上下文失敗\n");        }        out_stream->codecpar->codec_tag = 0;

// out_stream->codec->codec_tag = 0;
}

開啟輸出url,並寫入頭部資料

    //開啟IO    ret = avio_open(&octx->pb, outUrl, AVIO_FLAG_WRITE);    if (ret < 0) {        avError(ret);        throw ret;    }    logd("avio_open success!");    //寫入頭部資訊    ret = avformat_write_header(octx, 0);    if (ret < 0) {        avError(ret);        throw ret;    }

然後開始迴圈解碼並推流資料

首先擷取一幀的資料

ret = av_read_frame(ictx, &pkt);

然後給這一幀的資料配置參數,如果原有配置沒有時間就配置時間,我在這裡再提兩個概念

DTS(解碼時間戳記)和PTS(顯示時間戳記)分別是解碼器進行解碼和顯示幀時相對於SCR(系統參考)的時間戳記。SCR可以理解為解碼器應該開始從磁碟讀取資料時的時間。

        if (pkt.pts == AV_NOPTS_VALUE) {            //AVRational time_base:時基。通過該值可以把PTS,DTS轉化為真正的時間。            AVRational time_base1 = ictx->streams[videoindex]->time_base;            int64_t calc_duration =                    (double) AV_TIME_BASE / av_q2d(ictx->streams[videoindex]->r_frame_rate);            //配置參數            pkt.pts = (double) (frame_index * calc_duration) /                      (double) (av_q2d(time_base1) * AV_TIME_BASE);            pkt.dts = pkt.pts;            pkt.duration =                    (double) calc_duration / (double) (av_q2d(time_base1) * AV_TIME_BASE);        }

調節播放時間,就是當初我們解碼視頻之前記錄了一個目前時間,然後在迴圈推流的時候又擷取一次目前時間,兩者的差值是我們視頻應該播放的時間,如果視頻播放太快就進程休眠 pkt.dts減去實際播放的時間的差值

        if (pkt.stream_index == videoindex) {            AVRational time_base = ictx->streams[videoindex]->time_base;            AVRational time_base_q = {1, AV_TIME_BASE};            //計算視頻播放時間            int64_t pts_time = av_rescale_q(pkt.dts, time_base, time_base_q);            //計算實際視頻的播放時間            int64_t now_time = av_gettime() - start_time;            AVRational avr = ictx->streams[videoindex]->time_base;            cout << avr.num << " " << avr.den << "  " << pkt.dts << "  " << pkt.pts << "   "                 << pts_time << endl;            if (pts_time > now_time) {                //睡眠一段時間(目的是讓當前視頻記錄的播放時間與實際時間同步)                av_usleep((unsigned int) (pts_time - now_time));            }        }

如果延時了,這一幀的配置所記錄的時間就應該改變

        //計算延時後,重新指定時間戳記        pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base,                                   (AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));        pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base,                                   (AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));        pkt.duration = (int) av_rescale_q(pkt.duration, in_stream->time_base,                                          out_stream->time_base);

回調這一幀的時間參數,這裡在MainActivity裡執行個體化了介面,顯示播放時間

    int res = FFmpegHandle.setCallback(new PushCallback() {        @Override        public void videoCallback(final long pts, final long dts, final long duration, final long index) {            runOnUiThread(new Runnable() {                @Override                public void run() {                    if(pts == -1){                        tvPushInfo.setText("播放結束");                        return ;                    }                    tvPushInfo.setText("播放時間:"+dts/1000+"秒");                }            });        }    });

然後段代碼調用了c語言的setCallback函數,擷取了介面的執行個體,和介面的videoCallback函數引用,這裡還調用了一次這個函數初始化時間顯示

//轉換為全域變數pushCallback = env->NewGlobalRef(pushCallback1);if (pushCallback == NULL) {    return -3;}cls = env->GetObjectClass(pushCallback);if (cls == NULL) {    return -1;}mid = env->GetMethodID(cls, "videoCallback", "(JJJJ)V");if (mid == NULL) {    return -2;}env->CallVoidMethod(pushCallback, mid, (jlong) 0, (jlong) 0, (jlong) 0, (jlong) 0);

這個時候我們回到迴圈推流一幀幀資料的時候調用videoCallback函數

env->CallVoidMethod(pushCallback, mid, (jlong) pts, (jlong) dts, (jlong) duration,                    (jlong) index);

然後就是向輸出url輸出資料,並釋放這一幀的資料

        ret = av_interleaved_write_frame(octx, &pkt);        av_packet_unref(&pkt);

釋放資源

//關閉輸出上下文,這個很關鍵。if (octx != NULL)    avio_close(octx->pb);//釋放輸出封裝上下文if (octx != NULL)    avformat_free_context(octx);//關閉輸入上下文if (ictx != NULL)    avformat_close_input(&ictx);octx = NULL;ictx = NULL;env->ReleaseStringUTFChars(path_, path);env->ReleaseStringUTFChars(outUrl_, outUrl);

最後回調時間顯示,說播放結束

callback(env, -1, -1, -1, -1);

4.關於接收推流資料

我這裡使用的是VLC,這個mac和windows都有版本,FILE——》OPEN NETWORK,輸入之前的輸出url就可以了。這裡要注意首先在app上開啟推流再使用VLC開啟url才可以

效果如下

參考文章

https://www.jianshu.com/p/dcac5da8f1da

這個博主對於推流真的熟練,大家如果對推流還想輸入瞭解可以看看他的部落格

Android 音視頻深入 十五 FFmpeg 實現基於Rtmp協議的推流(附源碼下載)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.