libavcodec / libx264 не создают B-кадры

Я пишу приложение на C ++, которое использует libavcodec с libx264 для кодирования видео. Однако закодированных данных оказалось намного больше, чем я ожидал. Я проанализировал результаты и обнаружил, что при кодировании никогда не создавались B-кадры, только I- и P-кадры.

Я создал автономную утилиту на основе исходного кода и примеров ffmpeg для проверки настройки моего кодировщика. Он считывает файл H.264, перекодирует декодированные кадры и выводит результат в файл, используя формат ITU H.264 Annex B. Я также использовал ffmpeg для выполнения той же операции, чтобы я мог сравнить с известной хорошей реализацией. Моя утилита никогда не выводит B-кадры, в отличие от ffmpeg.

С тех пор я пытался выяснить, что делает ffmpeg, а мой код - нет. Сначала я попытался вручную указать настройки кодировщика, относящиеся к B-кадрам. Это не подействовало.

Затем я попытался запустить и ffmpeg, и мою утилиту под gdb и сравнить содержимое AVStream, AVCodecContext и X264Context перед тем, как открыть кодировщик и вручную установить любые поля, которые выглядели разными. Даже с идентичными настройками я по-прежнему создаю только I- и P-кадры.

Наконец, я подумал, что, возможно, проблема связана с моей обработкой временных меток. Я переработал свою тестовую утилиту, чтобы имитировать конвейер, используемый ffmpeg, и выводить отладочные данные с отметками времени, как это делает ffmpeg. Даже с моими отметками времени, идентичными ffmpeg, я все еще не получаю B-кадров.

На данный момент я не знаю, что еще попробовать. Когда я запускаю ffmpeg, я запускаю его с помощью командной строки ниже. Обратите внимание, что помимо предустановки «superfast», я в значительной степени использую значения по умолчанию.

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

Я уверен, что мне не хватает чего-то простого, но я не могу понять, что это такое. Будем очень благодарны любой помощи.


person Rob Schmidt    schedule 18.10.2013    source источник


Ответы (2)


После долгой отладки я наконец нашел решение. Проблема заключалась не в перечисленных выше настройках потока и кодировщика, а в том, как я передавал кадры на кодировщик.

Поскольку я просто пытался перекодировать данные, я просто делал снимки, выданные декодером, и передавал их кодировщику. Однако декодер устанавливает значение pict_type для создаваемых им структур AVFrame равным AV_PICTURE_TYPE_P. Кодировщик, в свою очередь, использует значение pict_type для определения типа генерируемых кадров. Следовательно, кодер всегда производит только P-кадры, поскольку это то, что всегда указывается во входных данных.

Сброс поля pict_type на AV_PICTURE_TYPE_NONE позволяет кодировщику создавать любой тип кадра, который он считает лучшим. Как только я обновил свой код для этого, моя тестовая утилита сгенерировала B-кадры и создала файл, идентичный тому, который выводит сам ffmpeg.

person Rob Schmidt    schedule 06.11.2013

у меня такая же проблема, но с x264. я обнаружил, что "superfast" изменит bframes = 0; в x264 вот так: x264_param_default_preset (& param, «быстро», «нулевой латентности»); измените его на x264_param_default_preset (& param, "slow", "zerolatency"); тогда я могу получить b-кадры.

кажется, что вы можете изменить свой параметр в этой функции. или вы можете проверить его определение av_dict_set (& opts, "preset", "superfast", 0);

person Shaun_Xia    schedule 24.10.2013
comment
Я думал, что пробовал разные предустановки кодирования с одинаковыми результатами, но я попробую еще раз, чтобы убедиться. Спасибо за предложение. - person Rob Schmidt; 24.10.2013
comment
Я попытался установить предустановку на очень низкую скорость, но все еще не получил B-кадров. Это изменение определенно имело эффект, поскольку для запуска моей тестовой программы потребовалось больше времени, а результаты отладки показали гораздо меньшие пакеты, но, к сожалению, не B-кадры. - person Rob Schmidt; 24.10.2013
comment
я понятия не имею о ffmpeg lib, добавьте codec_ctx- ›max_b_frames = 3; codec_ctx-› b_frame_strategy = 1; codec_ctx- ›refs = 6; и добавление этого флага FLAGS2 (CODEC_FLAG2_WPRED) должно иметь какое-то значение. но также вы должны убедиться, что никакая другая функция не изменит эти параметры. вы можете добавить точки останова и проверить эти параметры. - person Shaun_Xia; 25.10.2013
comment
Я попытался установить значения codec_ctx, как вы предложили, безуспешно. Я подключил отладчик и проверил значения до и после открытия кодировщика. Мне не удалось найти CODEC_FLAG2_WPRED, поэтому я не смог попробовать эту часть вашего предложения. Я также пробовал это как с медленными, так и со сверхбыстрыми настройками. Должно быть что-то еще фундаментальное, что я делаю неправильно. - person Rob Schmidt; 29.10.2013