Skip to content

Instantly share code, notes, and snippets.

@zoff99
Created December 13, 2024 17:59
Show Gist options
  • Save zoff99/0c8780be8f61059d34cddeb338fdfa8d to your computer and use it in GitHub Desktop.
Save zoff99/0c8780be8f61059d34cddeb338fdfa8d to your computer and use it in GitHub Desktop.
x265_change_bitrate_during_encoding
/*
*
* 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("vc_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();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment