Created
December 13, 2024 17:59
-
-
Save zoff99/0c8780be8f61059d34cddeb338fdfa8d to your computer and use it in GitHub Desktop.
x265_change_bitrate_during_encoding
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* | |
* 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