Android 音視頻深入 十六 FFmpeg 推流手機攝像頭,實現直播 (附源碼下載)

来源:https://www.cnblogs.com/jianpanwuzhe/archive/2018/02/28/8482873.html
-Advertisement-
Play Games

源碼地址https://github.com/979451341/RtmpCamera/tree/master配置RMTP伺服器,雖然之前說了,這裡就直接粘貼過來吧 1.配置RTMP伺服器 這個我不多說貼兩個博客分別是在mac和windows環境上的,大家跟著弄MAC搭建RTMP伺服器https:/ ...


源碼地址
https://github.com/979451341/RtmpCamera/tree/master

配置RMTP伺服器,雖然之前說了,這裡就直接粘貼過來吧

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.代碼

 

我們這裡要用到SurfaceView和Camera這對老組合,多而不說,就是Camera的配置有的需要註意

                    Camera.Parameters parameters = camera.getParameters();
                    //對拍照參數進行設置
                    for (Camera.Size size : parameters.getSupportedPictureSizes()) {
                        LogUtils.d(size.width + "  " + size.height);
                    }

 

註意這段列印出來的寬高,後來設置Camera拍攝的圖片大小配置必須是裡面的一組,否則無法獲取Camera的回調數據,這個很關鍵

parameters.setPictureSize(screenWidth, screenHeight); // 設置照片的大小

 

還有cpp文件里的寬高也要這樣,否則程式會崩潰,其實這裡的寬高我們可以通過比例縮放來處理,就可以任意使用寬高,但是我這裡沒有寫。。。。。。。。。

 

int width = 320;
int height = 240;

 

Camera預覽回調

camera.setPreviewCallback(new StreamIt()); // 設置回調的類

 

我們在這個回調里傳送需要進行推流的數據,這裡通過isPlaying標識符控制了,需要我們點擊start按鈕才會開始推流,並且這裡傳輸數據的代碼是通過開啟一個單線程來完成,保證上次操作完成了才會執行下一次

    public class StreamIt implements Camera.PreviewCallback {
        @Override
        public void onPreviewFrame(final byte[] data, Camera camera) {
            if(isPlaying){
                long endTime = System.currentTimeMillis();
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        encodeTime = System.currentTimeMillis();
                        FFmpegHandle.getInstance().onFrameCallback(data);
                        LogUtils.w("編碼第:" + (encodeCount++) + "幀,耗時:" + (System.currentTimeMillis() - encodeTime));
                    }
                });
                LogUtils.d("採集第:" + (++count) + "幀,距上一幀間隔時間:"
                        + (endTime - previewTime) + "  " + Thread.currentThread().getName());
                previewTime = endTime;
            }

        }
    }

之前還執行了initVideo函數,初始化了FFmpeg並傳輸了推流地址

計算編碼出的yuv數據的大小

    yuv_width = width;
    yuv_height = height;
    y_length = width * height;
    uv_length = width * height / 4;

 

初始化組件和輸出編碼環境

    av_register_all();

    //output initialize
    avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", out_path);
    //output encoder initialize
    pCodec = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (!pCodec) {
        loge("Can not find encoder!\n");
        return -1;
    }

 

配置編碼環境

    pCodecCtx = avcodec_alloc_context3(pCodec);
    //編碼器的ID號,這裡為264編碼器,可以根據video_st里的codecID 參數賦值
    pCodecCtx->codec_id = pCodec->id;
    //像素的格式,也就是說採用什麼樣的色彩空間來表明一個像素點
    pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
    //編碼器編碼的數據類型
    pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
    //編碼目標的視頻幀大小,以像素為單位
    pCodecCtx->width = width;
    pCodecCtx->height = height;
    pCodecCtx->framerate = (AVRational) {fps, 1};
    //幀率的基本單位,我們用分數來表示,
    pCodecCtx->time_base = (AVRational) {1, fps};
    //目標的碼率,即採樣的碼率;顯然,採樣碼率越大,視頻大小越大
    pCodecCtx->bit_rate = 400000;
    //固定允許的碼率誤差,數值越大,視頻越小
//    pCodecCtx->bit_rate_tolerance = 4000000;
    pCodecCtx->gop_size = 50;
    /* Some formats want stream headers to be separate. */
    if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
        pCodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;

    //H264 codec param
//    pCodecCtx->me_range = 16;
    //pCodecCtx->max_qdiff = 4;
    pCodecCtx->qcompress = 0.6;
    //最大和最小量化繫數
    pCodecCtx->qmin = 10;
    pCodecCtx->qmax = 51;
    //Optional Param
    //兩個非B幀之間允許出現多少個B幀數
    //設置0表示不使用B幀
    //b 幀越多,圖片越小
    pCodecCtx->max_b_frames = 0;

    if (pCodecCtx->codec_id == AV_CODEC_ID_H264) {
//        av_dict_set(¶m, "preset", "slow", 0);
        /**
         * 這個非常重要,如果不設置延時非常的大
         * ultrafast,superfast, veryfast, faster, fast, medium
         * slow, slower, veryslow, placebo. 這是x264編碼速度的選項
       */
        av_dict_set(¶m, "preset", "superfast", 0);
        av_dict_set(¶m, "tune", "zerolatency", 0);
    }

 

打開編碼器

    if (avcodec_open2(pCodecCtx, pCodec, ¶m) < 0) {
        loge("Failed to open encoder!\n");
        return -1;
    }

 


創建並配置一個視頻流

    video_st = avformat_new_stream(ofmt_ctx, pCodec);
    if (video_st == NULL) {
        return -1;
    }
    video_st->time_base.num = 1;
    video_st->time_base.den = fps;
//    video_st->codec = pCodecCtx;
    video_st->codecpar->codec_tag = 0;
    avcodec_parameters_from_context(video_st->codecpar, pCodecCtx);

 

查看輸出url是否有效,並根據輸出格式寫入文件頭

    if (avio_open(&ofmt_ctx->pb, out_path, AVIO_FLAG_READ_WRITE) < 0) {
        loge("Failed to open output file!\n");
        return -1;
    }

    //Write File Header
    avformat_write_header(ofmt_ctx, NULL);

 

接下來就是處理Camera傳送過來的數據

轉換數據格式

    jbyte *in = env->GetByteArrayElements(buffer_, NULL);

 

根據編碼器獲取緩存圖片大小,並創建緩存圖片空間

    int picture_size = av_image_get_buffer_size(pCodecCtx->pix_fmt, pCodecCtx->width,
                                                pCodecCtx->height, 1);
    uint8_t *buffers = (uint8_t *) av_malloc(picture_size);

 

將之前創建的緩存圖片空間賦予AVFrame

    pFrameYUV = av_frame_alloc();
    //將buffers的地址賦給AVFrame中的圖像數據,根據像素格式判斷有幾個數據指針
    av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, buffers, pCodecCtx->pix_fmt,
                         pCodecCtx->width, pCodecCtx->height, 1);

 

轉換AVFrame格式,卓攝像頭數據為NV21格式,此處將其轉換為YUV420P格式

    memcpy(pFrameYUV->data[0], in, y_length); //Y
    pFrameYUV->pts = count;
    for (int i = 0; i < uv_length; i++) {
        //將v數據存到第三個平面
        *(pFrameYUV->data[2] + i) = *(in + y_length + i * 2);
        //將U數據存到第二個平面
        *(pFrameYUV->data[1] + i) = *(in + y_length + i * 2 + 1);
    }

    pFrameYUV->format = AV_PIX_FMT_YUV420P;
    pFrameYUV->width = yuv_width;
    pFrameYUV->height = yuv_height;

 

編碼AVFrame數據

avcodec_send_frame(pCodecCtx, pFrameYUV);

獲取編碼後得到的數據

avcodec_receive_packet(pCodecCtx, &enc_pkt);

釋放AVFrame

av_frame_free(&pFrameYUV);

對編碼後的數據進行配置,設置播放時間等

    enc_pkt.stream_index = video_st->index;
    AVRational time_base = ofmt_ctx->streams[0]->time_base;//{ 1, 1000 };
    enc_pkt.pts = count * (video_st->time_base.den) / ((video_st->time_base.num) * fps);
    enc_pkt.dts = enc_pkt.pts;
    enc_pkt.duration = (video_st->time_base.den) / ((video_st->time_base.num) * fps);
    __android_log_print(ANDROID_LOG_WARN, "eric",
                        "index:%d,pts:%lld,dts:%lld,duration:%lld,time_base:%d,%d",
                        count,
                        (long long) enc_pkt.pts,
                        (long long) enc_pkt.dts,
                        (long long) enc_pkt.duration,
                        time_base.num, time_base.den);
    enc_pkt.pos = -1;

 

進行推流

av_interleaved_write_frame(ofmt_ctx, &enc_pkt);

釋放Camera傳輸過來的數據

env->ReleaseByteArrayElements(buffer_, in, 0);

最後釋放所有資源

    if (video_st)
        avcodec_close(video_st->codec);
    if (ofmt_ctx) {
        avio_close(ofmt_ctx->pb);
        avformat_free_context(ofmt_ctx);
        ofmt_ctx = NULL;
    }

 

4.VLC的使用

在進行推流時,輸入推流地址,觀看推流數據,效果如下

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Redis數據類型: Redis支持五種數據類型:string(字元串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。 1、String(字元串) string是redis最基本的類型,你可以理解成與Memcached一模一樣的類型,一個key對應 ...
  • 1.sql語句邏輯執行順序 (7) SELECT (8) DISTINCT <select_list> (1) FROM <left_table> (3) <join_type> JOIN <right_table> (2) ON <join_condition> (4) WHERE <where_ ...
  • (一)、Spark讀取HBase中的數據 hbase中的數據 (二)、Spark寫HBase 1.第一種方式: 2.第二種方式: ...
  • 有時會碰到同事誤刪或誤更新了某些數據,現在把恢複數據的方法之一:備份日誌尾部,簡單記錄一下。 1. 首先檢查你要還原的資料庫的恢復模式是否為完整,如果不是改為完整恢復模式。 其次,確保該資料庫至少做過一次完整備份,因為所有其他類型的備份都是基於完整備份的,如果不確定的話可以通過下麵這個語句來查看數據 ...
  • 一、Redis安裝 Linux安裝 下載tar包,移至Linux目錄下 解壓:tar -zxvf redis-4.0.1.tar.gz 安裝gcc:yum install gcc-c++(編譯失敗需安裝gcc編譯器) 編譯:make 安裝redis:make PREFIX=/usr/local/re ...
  • Mysql存儲引擎 選擇合適的存儲引擎Innodb myisam myisam: 寫入數據非常快,適合使用場合dedecms/phpcms/discuz/微博系統等寫入、讀取操作多的系統。 innodb: 適合業務邏輯比較強的系統,修改操作較多的,例如ecshop、crm、辦公系統、商城系統。mys ...
  • 概述:com.mysql.jdbc.Driver是mysql-connector-java 5中的,而com.mysql.cj.jdbc.Driver是mysql-connector-java 6中的。1、JDBC連接Mysql5需用com.mysql.jdbc.Driver,例如:driverCl ...
  • Android中主要是依靠分析系統源碼類來做到的,首先我們得找到被Hook的對象,我稱之為Hook點;什麼樣的對象比較好Hook呢?自然是容易找到的對象。什麼樣的對象容易找到?靜態變數和單例;在一個進程之內,靜態變數和單例變數是相對不容易發生變化的,因此非常容易定位,而普通的對象則要麼無法標誌,要麼 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...