2013-10-18 39 views
2

我正在用C編寫一個應用程序,它使用libavcodec和libx264來編碼視頻。但是,編碼數據最終比我預期的要大得多。我分析了結果,發現我的編碼從不產生B幀,只產生I幀和P幀。libavcodec/libx264不生成B幀

我創建了一個基於ffmpeg源代碼和示例來測試我的編碼器設置的獨立實用程序。它讀入H.264文件,對解碼的幀進行重新編碼,並使用ITU H.264附錄B格式將結果輸出到文件。我也使用ffmpeg來執行相同的操作,所以我可以比較一個已知的良好實現。我的公用程序從不輸出B幀,而ffmpeg卻輸出。

我自從試圖找出ffmpeg做了什麼,我的代碼沒有。我首先嚐試手動指定與B幀相關的編碼器設置。這沒有效果。

然後我嘗試在gdb下運行ffmpeg和我的實用程序,並在打開編碼器之前比較AVStream,AVCodecContext和X264Context的內容,並手動設置任何顯示不同的字段。即使使用相同的設置,我仍然只生成I幀和P幀。

最後,我想也許問題出在我的時間戳處理上。我重寫了我的測試實用程序以模仿ffmpeg使用的管道並輸出時間戳調試輸出,如ffmpeg。即使我的時間戳與ffmpeg相同,我仍然沒有得到B幀。

在這一點上,我不知道還有什麼要嘗試。當我運行ffmpeg時,我使用下面的命令行來運行它。請注意,除了「超快」預設,我幾乎都使用默認值。

ffmpeg -v debug -i ~/annexb.264 -codec:v libx264 -preset superfast -g 30 -f h264 ./out.264 

下面列出了配置編碼器的代碼。它也指定了「超快」預設。

static AVStream *add_video_stream(AVFormatContext *output_ctx, AVCodec **output_codec, enum AVCodecID codec_id) 
{ 
    *output_codec = avcodec_find_encoder(codec_id); 
    if (*output_codec == NULL) { 
     printf("Could not find encoder for '%s' (%d)\n", avcodec_get_name(codec_id), codec_id); 
     return NULL; 
    } 

    AVStream *output_stream = avformat_new_stream(output_ctx, *output_codec); 
    if (output_stream == NULL) { 
     printf("Could not create video stream.\n"); 
     return NULL; 
    } 
    output_stream->id = output_ctx->nb_streams - 1; 
    AVCodecContext *codec_ctx = output_stream->codec; 

    avcodec_get_context_defaults3(codec_ctx, *output_codec); 

    codec_ctx->width = 1280; 
    codec_ctx->height = 720; 

    codec_ctx->time_base.den = 15000; 
    codec_ctx->time_base.num = 1001; 

/* codec_ctx->gop_size = 30;*/ 
    codec_ctx->pix_fmt = AV_PIX_FMT_YUVJ420P; 

    // try to force B-frame output 
/* codec_ctx->max_b_frames = 3;*/ 
/* codec_ctx->b_frame_strategy = 2;*/ 

    output_stream->sample_aspect_ratio.num = 1; 
    output_stream->sample_aspect_ratio.den = 1; 

    codec_ctx->sample_aspect_ratio.num = 1; 
    codec_ctx->sample_aspect_ratio.den = 1; 

    codec_ctx->chroma_sample_location = AVCHROMA_LOC_LEFT; 

    codec_ctx->bits_per_raw_sample = 8; 

    if ((output_ctx->oformat->flags & AVFMT_GLOBALHEADER) != 0) { 
     codec_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; 
    } 

    return output_stream; 
} 


int main(int argc, char **argv) 
{ 
    // ... open input file 

    avformat_alloc_output_context2(&output_ctx, NULL, "h264", output_path); 
    if (output_ctx == NULL) { 
     fprintf(stderr, "Unable to allocate output context.\n"); 
     return 1; 
    } 

    AVCodec *output_codec = NULL; 
    output_stream = add_video_stream(output_ctx, &output_codec, output_ctx->oformat->video_codec); 
    if (output_stream == NULL) { 
     fprintf(stderr, "Error adding video stream to output context.\n"); 
     return 1; 
    } 
    encode_ctx = output_stream->codec; 

    // seems to have no effect 
#if 0 
    if (decode_ctx->extradata_size != 0) { 
     size_t extradata_size = decode_ctx->extradata_size; 
     printf("extradata_size: %zu\n", extradata_size); 
     encode_ctx->extradata = av_mallocz(extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); 
     memcpy(encode_ctx->extradata, decode_ctx->extradata, extradata_size); 
     encode_ctx->extradata_size = extradata_size; 
    } 
#endif // 0 

    AVDictionary *opts = NULL; 
    av_dict_set(&opts, "preset", "superfast", 0); 
    // av_dict_set(&opts, "threads", "auto", 0); // seems to have no effect 

    ret = avcodec_open2(encode_ctx, output_codec, &opts); 
    if (ret < 0) { 
     fprintf(stderr, "Unable to open output video cocec: %s\n", av_err2str(ret)); 
     return 1; 
    } 

    // ... decoding/encoding loop, clean up, etc. 

    return 0; 
} 

我的測試工具產生以下調試輸出,可以在其中看到有沒有產生B幀:

[libx264 @ 0x1b8c9c0] using mv_range_thread = 56 
[libx264 @ 0x1b8c9c0] using SAR=1/1 
[libx264 @ 0x1b8c9c0] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX 
[libx264 @ 0x1b8c9c0] profile High, level 3.1 
Output #0, h264, to './out.264': 
    Stream #0:0, 0, 1/90000: Video: h264, yuvj420p, 1280x720 [SAR 1:1 DAR 16:9], 1001/15000, q=-1--1, 90k tbn, 14.99 tbc 

<SNIP> 

[libx264 @ 0x1b8c9c0] frame= 0 QP=17.22 NAL=3 Slice:I Poc:0 I:3600 P:0 SKIP:0 size=122837 bytes 
[libx264 @ 0x1b8c9c0] frame= 1 QP=18.03 NAL=2 Slice:P Poc:2 I:411 P:1825 SKIP:1364 size=25863 bytes 
[libx264 @ 0x1b8c9c0] frame= 2 QP=17.03 NAL=2 Slice:P Poc:4 I:369 P:2159 SKIP:1072 size=37880 bytes 
[libx264 @ 0x1b8c9c0] frame= 3 QP=16.90 NAL=2 Slice:P Poc:6 I:498 P:2330 SKIP:772 size=50509 bytes 
[libx264 @ 0x1b8c9c0] frame= 4 QP=16.68 NAL=2 Slice:P Poc:8 I:504 P:2233 SKIP:863 size=50791 bytes 
[libx264 @ 0x1b8c9c0] frame= 5 QP=16.52 NAL=2 Slice:P Poc:10 I:513 P:2286 SKIP:801 size=51820 bytes 
[libx264 @ 0x1b8c9c0] frame= 6 QP=16.49 NAL=2 Slice:P Poc:12 I:461 P:2293 SKIP:846 size=51311 bytes 
[libx264 @ 0x1b8c9c0] frame= 7 QP=16.65 NAL=2 Slice:P Poc:14 I:476 P:2287 SKIP:837 size=51196 bytes 
[libx264 @ 0x1b8c9c0] frame= 8 QP=16.66 NAL=2 Slice:P Poc:16 I:508 P:2240 SKIP:852 size=51577 bytes 
[libx264 @ 0x1b8c9c0] frame= 9 QP=16.55 NAL=2 Slice:P Poc:18 I:477 P:2278 SKIP:845 size=51531 bytes 
[libx264 @ 0x1b8c9c0] frame= 10 QP=16.67 NAL=2 Slice:P Poc:20 I:517 P:2233 SKIP:850 size=51946 bytes 

<SNIP> 

[libx264 @ 0x1b8c9c0] frame I:7  Avg QP:13.71 size:152207 
[libx264 @ 0x1b8c9c0] frame P:190 Avg QP:16.66 size: 50949 
[libx264 @ 0x1b8c9c0] mb I I16..4: 27.1% 30.8% 42.1% 
[libx264 @ 0x1b8c9c0] mb P I16..4: 6.8% 6.0% 0.8% P16..4: 61.8% 0.0% 0.0% 0.0% 0.0% skip:24.7% 
[libx264 @ 0x1b8c9c0] 8x8 transform intra:41.2% inter:86.9% 
[libx264 @ 0x1b8c9c0] coded y,uvDC,uvAC intra: 92.2% 28.3% 5.4% inter: 50.3% 1.9% 0.0% 
[libx264 @ 0x1b8c9c0] i16 v,h,dc,p: 7% 7% 77% 8% 
[libx264 @ 0x1b8c9c0] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 7% 15% 49% 6% 4% 3% 5% 3% 8% 
[libx264 @ 0x1b8c9c0] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 19% 25% 24% 6% 7% 4% 6% 3% 6% 
[libx264 @ 0x1b8c9c0] i8c dc,h,v,p: 72% 14% 10% 4% 
[libx264 @ 0x1b8c9c0] Weighted P-Frames: Y:0.0% UV:0.0% 
[libx264 @ 0x1b8c9c0] kb/s:6539.11 

FFMPEG,在另一方面,產生以下輸出幾乎是相同但包括B幀:

[libx264 @ 0x20b9c40] using mv_range_thread = 56 
[libx264 @ 0x20b9c40] using SAR=1/1 
[libx264 @ 0x20b9c40] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX 
[libx264 @ 0x20b9c40] profile High, level 3.1 
[h264 @ 0x20b8160] detected 4 logical cores 
Output #0, h264, to './out.264': 
    Metadata: 
    encoder   : Lavf54.63.104 
    Stream #0:0, 0, 1/90000: Video: h264, yuvj420p, 1280x720 [SAR 1:1 DAR 16:9], 1001/15000, q=-1--1, 90k tbn, 14.99 tbc 
Stream mapping: 
    Stream #0:0 -> #0:0 (h264 -> libx264) 

<SNIP> 

[libx264 @ 0x20b9c40] frame= 0 QP=17.22 NAL=3 Slice:I Poc:0 I:3600 P:0 SKIP:0 size=122835 bytes 
[libx264 @ 0x20b9c40] frame= 1 QP=18.75 NAL=2 Slice:P Poc:8 I:984 P:2045 SKIP:571 size=54208 bytes 
[libx264 @ 0x20b9c40] frame= 2 QP=19.40 NAL=2 Slice:B Poc:4 I:447 P:1581 SKIP:1572 size=24930 bytes 
[libx264 @ 0x20b9c40] frame= 3 QP=19.78 NAL=0 Slice:B Poc:2 I:199 P:1002 SKIP:2399 size=10717 bytes 
[libx264 @ 0x20b9c40] frame= 4 QP=20.19 NAL=0 Slice:B Poc:6 I:204 P:1155 SKIP:2241 size=15937 bytes 
[libx264 @ 0x20b9c40] frame= 5 QP=18.11 NAL=2 Slice:P Poc:16 I:990 P:2221 SKIP:389 size=64240 bytes 
[libx264 @ 0x20b9c40] frame= 6 QP=19.35 NAL=2 Slice:B Poc:12 I:439 P:1784 SKIP:1377 size=34048 bytes 
[libx264 @ 0x20b9c40] frame= 7 QP=19.88 NAL=0 Slice:B Poc:10 I:275 P:1035 SKIP:2290 size=16911 bytes 
[libx264 @ 0x20b9c40] frame= 8 QP=19.91 NAL=0 Slice:B Poc:14 I:257 P:1270 SKIP:2073 size=19172 bytes 
[libx264 @ 0x20b9c40] frame= 9 QP=17.90 NAL=2 Slice:P Poc:24 I:962 P:2204 SKIP:434 size=67439 bytes 
[libx264 @ 0x20b9c40] frame= 10 QP=18.84 NAL=2 Slice:B Poc:20 I:474 P:1911 SKIP:1215 size=37742 bytes 

<SNIP> 

[libx264 @ 0x20b9c40] frame I:7  Avg QP:15.95 size:130124 
[libx264 @ 0x20b9c40] frame P:52 Avg QP:17.78 size: 64787 
[libx264 @ 0x20b9c40] frame B:138 Avg QP:19.32 size: 26231 
[libx264 @ 0x20b9c40] consecutive B-frames: 6.6% 0.0% 0.0% 93.4% 
[libx264 @ 0x20b9c40] mb I I16..4: 30.2% 35.2% 34.6% 
[libx264 @ 0x20b9c40] mb P I16..4: 13.9% 11.4% 0.3% P16..4: 60.4% 0.0% 0.0% 0.0% 0.0% skip:13.9% 
[libx264 @ 0x20b9c40] mb B I16..4: 5.7% 3.3% 0.0% B16..8: 15.8% 0.0% 0.0% direct:25.7% skip:49.5% L0:43.2% L1:37.3% BI:19.5% 
[libx264 @ 0x20b9c40] 8x8 transform intra:39.4% inter:77.2% 
[libx264 @ 0x20b9c40] coded y,uvDC,uvAC intra: 90.7% 26.6% 3.0% inter: 34.0% 4.1% 0.0% 
[libx264 @ 0x20b9c40] i16 v,h,dc,p: 7% 7% 77% 9% 
[libx264 @ 0x20b9c40] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 7% 16% 51% 5% 4% 3% 5% 3% 7% 
[libx264 @ 0x20b9c40] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 22% 27% 20% 6% 6% 3% 6% 3% 6% 
[libx264 @ 0x20b9c40] i8c dc,h,v,p: 71% 15% 11% 3% 
[libx264 @ 0x20b9c40] Weighted P-Frames: Y:0.0% UV:0.0% 
[libx264 @ 0x20b9c40] kb/s:4807.16 

我敢肯定,我失去了一些東西簡單,但我不能爲我的生活看到它是什麼。任何援助將不勝感激。

回答

3

經過多次調試,我終於找到了解決方案。問題不在於上面列出的流和編碼器設置,而在於我如何將幀傳遞給編碼器。

因爲我只是試圖對數據進行轉碼,所以我只是簡單地拍下由解碼器發出的圖片,並將它們傳遞給編碼器。但是,解碼器將其產生的AVFrame structurespict_type值設置爲AV_PICTURE_TYPE_P。編碼器反過來使用pict_type值來確定要生成的幀的類型。因此,編碼器只能生成P幀,因爲輸入中始終指定了該幀。

pict_type字段重置爲AV_PICTURE_TYPE_NONE允許編碼器生成它認爲最好的任何類型的幀。一旦我更新了我的代碼來完成此操作,我的測試實用程序生成了B幀並生成了與ffmpeg本身的輸出相同的文件。

0

我有同樣的問題,但與x264。 我發現「超快」會改變bframes = 0; in x264 like this: x264_param_default_preset(& param,「fast」,「zerolatency」); 更改爲 x264_param_default_preset(& param,「slow」,「zerolatency」); 然後我可以得到B幀。

看來你可以在這個函數中改變你的參數。 或者你可以檢查它的定義 av_dict_set(& opts,「preset」,「superfast」,0);

+0

我以爲我曾嘗試過不同的編碼預設,結果相同,但我會再試一次來確定。感謝您的建議。 –

+0

我試着將預設設置爲「veryslow」,但仍然沒有B幀。這個改變肯定會產生影響,因爲運行我的測試程序需要更長的時間,而且調試輸出顯示了更小的數據包,但不幸的是沒有B幀。 –

+0

我不知道ffmpeg lib,添加codec_ctx-> max_b_frames = 3; codec_ctx-> b_frame_strategy = 1; codec_ctx->參= 6;並添加此標誌FLAGS2(CODEC_FLAG2_WPRED)應該有所作爲。但你也應該確保沒有其他功能會改變這些參數。你可以添加斷點並檢查這些參數。 –