2015-04-21 83 views
0

我試圖使用Mac OS X 10.9.5上的libx264實時將來自MacBook Pro內置FaceTime高清攝像頭的輸入圖像實時編碼爲H.264視頻流。如何將來自攝像機的輸入圖像編碼爲H.264流?

下面是我所採取的步驟:在15fps的使用AVFoundation API(AVCaptureDevice類等)

  • 使用libswscale將圖像轉換成看見320×180 YUV420P格式

    1. 獲取1280×720從相機32BGRA圖像。
    2. 使用libx264將圖像編碼爲H.264視頻流(基線配置文件)。

    我每次從相機獲取圖像時都會應用上述步驟,相信編碼器會跟蹤編碼狀態並在可用時生成NAL單元。因爲我希望在向編碼器提供輸入圖像的同時獲得編碼幀,所以我決定每隔30幀(2秒)刷新一次編碼器(調用x264_encoder_delayed_frames())以調用x264_encoder_delayed_frames()。但是,當我重新啓動編碼時,編碼器會在一段時間後停止(x264_encoder_encode()永不返回)。我嘗試在刷新之前更改幀數,但情況沒有改變。

    下面是相關的代碼(我省略了圖像捕捉代碼,因爲它看起來沒有問題。)

    你能指出什麼我可能是做錯了?

    x264_t *encoder; 
    x264_param_t param; 
    
    // Will be called only first time. 
    int initEncoder() { 
        int ret; 
    
        if ((ret = x264_param_default_preset(&param, "medium", NULL)) < 0) { 
        return ret; 
        } 
    
        param.i_csp = X264_CSP_I420; 
        param.i_width = 320; 
        param.i_height = 180; 
        param.b_vfr_input = 0; 
        param.b_repeat_headers = 1; 
        param.b_annexb = 1; 
    
        if ((ret = x264_param_apply_profile(&param, "baseline")) < 0) { 
        return ret; 
        } 
    
        encoder = x264_encoder_open(&param); 
        if (!encoder) { 
        return AVERROR_UNKNOWN; 
        } 
    
        return 0; 
    } 
    
    // Will be called from encodeFrame() defined below. 
    int convertImage(const enum AVPixelFormat srcFmt, const int srcW, const int srcH, const uint8_t *srcData, const enum AVPixelFormat dstFmt, const int dstW, const int dstH, x264_image_t *dstData) { 
        struct SwsContext *sws_ctx; 
        int ret; 
        int src_linesize[4]; 
        uint8_t *src_data[4]; 
    
        sws_ctx = sws_getContext(srcW, srcH, srcFmt, 
             dstW, dstH, dstFmt, 
             SWS_BILINEAR, NULL, NULL, NULL); 
    
        if (!sws_ctx) { 
        return AVERROR_UNKNOWN; 
        } 
    
        if ((ret = av_image_fill_linesizes(src_linesize, srcFmt, srcW)) < 0) { 
        sws_freeContext(sws_ctx); 
        return ret; 
        } 
    
        if ((ret = av_image_fill_pointers(src_data, srcFmt, srcH, (uint8_t *) srcData, src_linesize)) < 0) { 
        sws_freeContext(sws_ctx); 
        return ret; 
        } 
    
        sws_scale(sws_ctx, (const uint8_t * const*)src_data, src_linesize, 0, srcH, dstData->plane, dstData->i_stride); 
        sws_freeContext(sws_ctx); 
        return 0; 
    } 
    
    // Will be called for each frame. 
    int encodeFrame(const uint8_t *data, const int width, const int height) { 
        int ret; 
        x264_picture_t pic; 
        x264_picture_t pic_out; 
        x264_nal_t *nal; 
        int i_nal; 
    
        if ((ret = x264_picture_alloc(&pic, param.i_csp, param.i_width, param.i_height)) < 0) { 
        return ret; 
        } 
    
        if ((ret = convertImage(AV_PIX_FMT_RGB32, width, height, data, AV_PIX_FMT_YUV420P, 320, 180, &pic.img)) < 0) { 
        x264_picture_clean(&pic); 
        return ret; 
        } 
    
        if ((ret = x264_encoder_encode(encoder, &nal, &i_nal, &pic, &pic_out)) < 0) { 
        x264_picture_clean(&pic); 
        return ret; 
        } 
    
        if(ret) { 
        for (int i = 0; i < i_nal; i++) { 
         printNAL(nal + i); 
        } 
        } 
    
        x264_picture_clean(&pic); 
        return 0; 
    } 
    
    // Will be called every 30 frames. 
    int flushEncoder() { 
        int ret; 
        x264_nal_t *nal; 
        int i_nal; 
        x264_picture_t pic_out; 
    
        /* Flush delayed frames */ 
        while (x264_encoder_delayed_frames(encoder)) { 
        if ((ret = x264_encoder_encode(encoder, &nal, &i_nal, NULL, &pic_out)) < 0) { 
         return ret; 
        } 
    
        if (ret) { 
         for (int j = 0; j < i_nal; j++) { 
         printNAL(nal + j); 
         } 
        } 
        } 
    } 
    
  • +0

    在flushEncoder()的末尾插入兩行後,上面的代碼就開始工作了。我添加了x264_encoder_close(編碼器);和initEncoder(); – kuu

    回答

    1

    當不存在更多輸入幀時,即在編碼結束時,您不應該在每幀之後刷新延遲幀,而只能刷新一次。

    +0

    感謝您指出API的濫用情況。我已經改變了代碼,並將沖洗延遲幀部分放在一個單獨的函數中(flushEncoder)。並且我讓函數每25幀調用一次。現在,我可以在前25幀中獲得28個NAL單元,其中包括SPS,PPS,SEI和25個切片(1個IDR + 24個其他切片)。但是,一旦我開始對接下來的25個幀進行編碼,編碼器將停止並且x264_encoder_encode ()永遠不會返回。你能再看看更新的代碼嗎? – kuu

    +0

    啊,每當我刷新編碼器時,我改變了重置編碼器的代碼(通過調用x264_encoder_close()和x264_encoder_open()),問題就消失了。我已經知道,一旦我調用x264_encoder_delayed_frames(),編碼器就沒用了。謝謝。 – kuu

    +1

    不是在調用x264_encoder_delayed_frames()之後,而是在用NULL幀調用x264_encoder_encode()之後刷新幀,因爲它停止了lookahead/slicetype判斷線程(因爲它表示沒有更多輸入幀),並且在此之後它變得「無用」因爲它們永遠不會被編碼,並且只會增加delayed_frames數量(這就是爲什麼你會無限循環的原因),所以變成將真實幀發送到encoder_encode()的意義更小。如前所述,您應該在x264_encoder_close()前最後一次刷新幀。 – nobody555