[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