[x265] trying to change encoder bitrate during encoding, i can not get it to work

Zoff zoff at zoff.cc
Fri Dec 13 18:06:55 UTC 2024


Hi!

i did not find a mailinglist for users of x265 encoder. so i am posting here in hopes that someone can help me.
i am trying to use x265 for streaming video and want to change encoder bitrate during encoding, according to network bandwidth.
it works fine with x264 but when trying to achive this with x265 it fails no matter what i try.
it always stays on the bitrate that i set during encoder setup.

i hope i implemented it according to docs:
https://x265.readthedocs.io/en/master/api.html
https://x265.readthedocs.io/en/master/cli.html#quality-rate-control-and-rate-distortion-options

here is my minimal example:


############################################################


/*
  *
  * gcc -O3 -g test.c -o test $(pkg-config --libs --cflags x265) -fsanitize=address -fno-omit-frame-pointer -static-libasan
  *
  */

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include <x265.h>

int h265_enc_width = 1920;
int h265_enc_height = 1080;
int bit_rate = 2000;
uint64_t timestamp = 0;
int encoded_frame_bytes = 0;
int rnd_every_x_frames = 100;
int rnd_every_x_frames_cur = 0;

uint8_t *y = NULL;
uint8_t *u = NULL;
uint8_t *v = NULL;

x265_encoder *h265_encoder = NULL;
x265_picture *h265_in_pic = NULL;
x265_picture *h265_out_pic = NULL;
x265_param *param = NULL;

void init_encoder()
{
     param = x265_param_alloc();
     if (x265_param_default_preset(param, "ultrafast", "zerolatency") != 0) {
         printf("init_encoder_h265: H265 encoder:x265_param_default_preset error\n");
     }

     param->sourceWidth = h265_enc_width;
     param->sourceHeight = h265_enc_height;
     param->fpsNum = 20;
     param->fpsDenom = 1;
     param->internalCsp = X265_CSP_I420;
     param->bframes = 0;
     param->bRepeatHeaders = 1;
     param->bAnnexB = 1;
     param->keyframeMax = 60; // every n-th frame is an I-frame
     param->bIntraRefresh = 1;

     x265_param_parse(param, "repeat-headers", "1");
     x265_param_parse(param, "annexb", "1");
     x265_param_parse(param, "input-csp", "i420");
     x265_param_parse(param, "log-level", "debug");
     x265_param_parse(param, "frame-rc", "1");

     // https://x265.readthedocs.io/en/master/cli.html#quality-rate-control-and-rate-distortion-options
     // Specify the target bitrate in kbps. Default is 0 (CRF)

     param->rc.bitrate = bit_rate;
     param->rc.vbvBufferSize = bit_rate;
     param->rc.vbvMaxBitrate = bit_rate;

     param->rc.rateControlMode = X265_RC_ABR;
     param->rc.bStrictCbr = 1;

     h265_in_pic = x265_picture_alloc();
     x265_picture_init(param, h265_in_pic);

     h265_out_pic = x265_picture_alloc();
     x265_picture_init(param, h265_out_pic);

     // Allocate memory for YUV frame
     h265_in_pic->colorSpace = X265_CSP_I420;
     h265_in_pic->stride[0] = h265_enc_width;
     h265_in_pic->stride[1] = h265_enc_width / 2;
     h265_in_pic->stride[2] = h265_enc_width / 2;
     h265_in_pic->planes[0] = (uint8_t *)calloc(1, (h265_enc_width * h265_enc_height));
     h265_in_pic->planes[1] = (uint8_t *)calloc(1, (h265_enc_width * h265_enc_height) / 4);
     h265_in_pic->planes[2] = (uint8_t *)calloc(1, (h265_enc_width * h265_enc_height) / 4);

     h265_encoder = x265_encoder_open(param);
     printf("H265 encoder:h265_encoder=%p\n", (void *)h265_encoder);

     x265_param_free(param);
}

void kill_encoder()
{
     free(h265_in_pic->planes[0]);
     free(h265_in_pic->planes[1]);
     free(h265_in_pic->planes[2]);
     x265_picture_free(h265_in_pic);
     x265_picture_free(h265_out_pic);
     x265_encoder_close(h265_encoder);
}

void reconfigure_encoder()
{
     printf("reconfigure_encoder_h265:1:bit_rate = %d\n", (int)bit_rate);
     x265_param *param = x265_param_alloc();
     x265_encoder_parameters(h265_encoder, param);

     param->rc.bitrate = bit_rate;
     param->rc.vbvBufferSize = bit_rate;
     param->rc.vbvMaxBitrate = bit_rate;

     int res = x265_encoder_reconfig(h265_encoder, param);
     x265_param_free(param);
     setvbuf(stdout, NULL, _IOLBF, 0);
     setvbuf(stderr, NULL, _IOLBF, 0);
     printf("x265 [*R**] x265_encoder_reconfig:res=%d bitrate=%d\n", (int)res, bit_rate);
}

int encode_frame(uint16_t width, uint16_t height,
                            const uint8_t *y,
                            const uint8_t *u,
                            const uint8_t *v,
                            uint64_t pts)
{
     uint32_t i_nal;
     int x265_num_nals;
     uint64_t pts_out;
     int i_frame_size;
     int res;
     x265_nal* h265_nals = NULL;

     h265_in_pic->pts = (int64_t)pts;
     // printf("X265:in_ts:%lu\n", pts);

     memcpy(h265_in_pic->planes[0], y, (width * height));
     memcpy(h265_in_pic->planes[1], u, (width / 2) * (height / 2));
     memcpy(h265_in_pic->planes[2], v, (width / 2) * (height / 2));
     res = x265_encoder_encode(h265_encoder, &h265_nals, &i_nal, h265_in_pic, h265_out_pic);

     pts_out = (uint64_t)h265_out_pic->pts;
     // printf("X265:res:%d\n", res);
     // printf("X265:out_ts:%lu\n", pts);
     // printf("X265:out_ts:dts:%d\n", (int)h265_out_pic->dts);
     // printf("X265:out_ts:pts:%d\n", (int)h265_out_pic->pts);

     if (h265_nals == NULL) {
         printf("X265:RET 001\n");
         return -1;
     }

     if (i_nal < 1) {
         printf("X265:RET 002\n");
         return -2;
     }

     x265_num_nals = (int)i_nal;
     i_frame_size = h265_nals[0].sizeBytes;

     if (i_frame_size < 0) {
         // some error
         printf("X265:ERR 010\n");
     } else if (i_frame_size == 0) {
         // zero size output
         printf("X265:ERR 020\n");
     }

     if (h265_nals[0].payload == NULL) {
         printf("X265:ERR 099\n");
         return -3;
     }

     return i_frame_size;
}

uint32_t n_r(const uint32_t upper_bound)
{
     return rand() % upper_bound;
}

void rvbuf(uint8_t *buf, size_t size)
{
     for (int i=0; i < size; i++)
     {
         // random value 1..255
         *buf = (uint8_t)((n_r(255)) + 1);
         buf++;
     }
}

void rnd_yuv()
{
     size_t y_size = h265_enc_width * h265_enc_height;
     size_t u_size = (h265_enc_height/2) * (h265_enc_width/2);
     size_t v_size = (h265_enc_height/2) * (h265_enc_width/2);
     if (!y) {y = calloc(1, y_size);}
     if (!u) {u = calloc(1, u_size);}
     if (!v) {v = calloc(1, v_size);}
     rnd_every_x_frames_cur++;
     if (rnd_every_x_frames_cur > rnd_every_x_frames)
     {
         rnd_every_x_frames_cur = 0;
         rvbuf(y, y_size);
         rvbuf(u, u_size);
         rvbuf(v, v_size);
     }
}

int main(int argc, char *argv[])
{
     init_encoder();

     for (int i=0;i<200;i++) {
         timestamp++;
         rnd_yuv();
         encoded_frame_bytes = encode_frame(h265_enc_width, h265_enc_height, y, u, v, timestamp);
         printf("x265 [eee1]: bytes=%d\n", encoded_frame_bytes);
     }

     bit_rate = 800;
     reconfigure_encoder();

     for (int i=0;i<200;i++) {
         timestamp++;
         rnd_yuv();
         encoded_frame_bytes = encode_frame(h265_enc_width, h265_enc_height, y, u, v, timestamp);
         printf("x265 [eee2]: bytes=%d\n", encoded_frame_bytes);
     }

     bit_rate = 600;
     reconfigure_encoder();

     for (int i=0;i<200;i++) {
         timestamp++;
         rnd_yuv();
         encoded_frame_bytes = encode_frame(h265_enc_width, h265_enc_height, y, u, v, timestamp);
         printf("x265 [eee2]: bytes=%d\n", encoded_frame_bytes);
     }


     kill_encoder();
}



############################################################



compile with:
gcc -O3 -g test.c -o test $(pkg-config --libs --cflags x265) -fsanitize=address -fno-omit-frame-pointer -static-libasan
then run:
./test

what i get is:

x265 [info]: frame I:      1, Avg QP:20.63  kb/s: 112.32
x265 [info]: frame P:    599, Avg QP:41.15  kb/s: 1996.36
x265 [info]: consecutive B-frames: 100.0%

encoded 600 frames in 16.54s (36.28 fps), 1993.22 kb/s, Avg QP:41.11


the encoder bitrate stays at about 2000 kbit/s
but i change the rate with reconfigure_encoder() during encoding.

any ideas, what am i doing wrong?


cheers,
Zoff.


More information about the x265-devel mailing list