ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
encoder.c File Reference

FFmpeg HEVC encoder for ASCII art frames. More...

Go to the source code of this file.

Data Structures

struct  h265_encoder
 h265 encoder is intentionally a small, concrete record. Fields such as codec_ctx, codec, frame, packet give callers the identity and current value they need. It is normally borrowed from, or owned by, the surrounding subsystem—not independently managed. More...

Typedefs

typedef struct h265_encoder h265_encoder_t
 h265 encoder is intentionally a small, concrete record. Fields such as codec_ctx, codec, frame, packet give callers the identity and current value they need. It is normally borrowed from, or owned by, the surrounding subsystem—not independently managed.

Functions

bool video_encoder_is_available (video_codec_t codec)
h265_encoder_t * h265_encoder_create (uint16_t initial_width, uint16_t initial_height)
h265_encoder_t * video_encoder_create (video_codec_t codec_type, uint16_t initial_width, uint16_t initial_height)
void h265_encoder_destroy (h265_encoder_t *encoder)
asciichat_error_t h265_encode (h265_encoder_t *encoder, uint16_t width, uint16_t height, const uint8_t *ascii_data, uint8_t *output_buf, size_t *output_size)
void h265_encoder_request_keyframe (h265_encoder_t *encoder)
asciichat_error_t h265_encoder_flush (h265_encoder_t *encoder, uint8_t *output_buf, size_t *output_size)
void h265_encoder_get_stats (h265_encoder_t *encoder, uint64_t *total_frames, uint64_t *keyframes, uint32_t *avg_bitrate)

Detailed Description

FFmpeg HEVC encoder for ASCII art frames.

Definition in file encoder.c.

Typedef Documentation

◆ h265_encoder_t

typedef struct h265_encoder h265_encoder_t

h265 encoder is intentionally a small, concrete record. Fields such as codec_ctx, codec, frame, packet give callers the identity and current value they need. It is normally borrowed from, or owned by, the surrounding subsystem—not independently managed.

Function Documentation

◆ h265_encode()

asciichat_error_t h265_encode ( h265_encoder_t * encoder,
uint16_t width,
uint16_t height,
const uint8_t * ascii_data,
uint8_t * output_buf,
size_t * output_size )

Encode an RGB24 upload frame

The encoder automatically detects size changes and reinitializes when needed. The output packet includes frame dimensions so decoder can reconfigure.

Parameters
encoderEncoder handle
widthCurrent frame width in pixels
heightCurrent frame height in pixels
ascii_dataRGB24 pixel data (width × height × 3 bytes)
output_bufBuffer for encoded output
output_sizeInput: buffer size, Output: actual encoded size
Returns
Error code (ASCIICHAT_OK on success)

OUTPUT PACKET FORMAT: [flags: u8][width: u16][height: u16][x265_data...]

Definition at line 317 of file encoder.c.

318 {
319 ASCII_OTEL_SCOPE(H265_ENCODE);
320 ASCII_OTEL_ATTR_INT("video.frame.width", width);
321 ASCII_OTEL_ATTR_INT("video.frame.height", height);
322 if (encoder && encoder->codec)
323 ASCII_OTEL_ATTR_STRING("video.codec", encoder->codec->id == AV_CODEC_ID_H264 ? "h264" : "h265");
324 if (!encoder || !ascii_data || !output_buf || !output_size) {
325 return SET_ERRNO(ERROR_INTERNAL, "Invalid encoder arguments");
326 }
327
328 if (!width || !height || (width & 1) || (height & 1)) {
329 return SET_ERRNO(ERROR_MEDIA_INIT, "Video encoding requires non-zero even dimensions, got %ux%u", width, height);
330 }
331
332 if (*output_size < 5) {
333 return SET_ERRNO(ERROR_NETWORK_SIZE, "Output buffer too small (minimum 5 bytes)");
334 }
335
336 bool size_changed = width != encoder->current_width || height != encoder->current_height;
337 asciichat_error_t result = h265_encoder_reconfigure(encoder, width, height);
338 if (result != ASCIICHAT_OK) {
339 return result;
340 }
341
342 h265_encoder_ascii_to_yuv420(ascii_data, width, height, encoder->yuv_buf);
343
344 // Setup frame data pointers
345 encoder->frame->data[0] = encoder->yuv_buf;
346 encoder->frame->data[1] = encoder->yuv_buf + width * height;
347 encoder->frame->data[2] = encoder->yuv_buf + width * height + (width / 2) * (height / 2);
348 encoder->frame->linesize[0] = width;
349 encoder->frame->linesize[1] = width / 2;
350 encoder->frame->linesize[2] = width / 2;
351 encoder->frame->width = width;
352 encoder->frame->height = height;
353 encoder->frame->format = AV_PIX_FMT_YUV420P;
354
355 encoder->frame->pts = (int64_t)encoder->frames_submitted++;
356 encoder->frame->pict_type = AV_PICTURE_TYPE_NONE;
357
358 // Request keyframe if needed
359 if (encoder->request_keyframe) {
360 encoder->frame->pict_type = AV_PICTURE_TYPE_I;
361 encoder->request_keyframe = false;
362 }
363
364 // Encode frame
365 uint64_t stats_codec_start = time_get_ns();
366 int send_ret = avcodec_send_frame(encoder->codec_ctx, encoder->frame);
367 if (send_ret < 0) {
368 stats_duration_record(stats_runtime_scope(), STATS_DURATION_ENCODE, time_get_ns() - stats_codec_start);
369 log_error("avcodec_send_frame failed: %d", send_ret);
370 return SET_ERRNO(ERROR_MEDIA_DECODE, "Failed to send frame to encoder");
371 }
372
373 int recv_ret = avcodec_receive_packet(encoder->codec_ctx, encoder->packet);
374 stats_duration_record(stats_runtime_scope(), STATS_DURATION_ENCODE, time_get_ns() - stats_codec_start);
375 if (recv_ret == AVERROR(EAGAIN)) {
376 // EAGAIN is normal - encoder needs more frames before outputting a packet
377 log_dev("EAGAIN - encoder buffering frames (normal), waiting for next frame");
378 *output_size = 0; // No output this time
379 return ASCIICHAT_OK;
380 }
381 if (recv_ret < 0) {
382 log_error("avcodec_receive_packet failed: %d (EAGAIN=%d)", recv_ret, AVERROR(EAGAIN));
383 return SET_ERRNO(ERROR_MEDIA_DECODE, "Failed to receive encoded packet");
384 }
385 log_debug_every(US_PER_SEC_INT, "Got encoded packet: size=%d", (int)encoder->packet->size);
386
387 uint8_t flags = 0;
388 if (encoder->packet->flags & AV_PKT_FLAG_KEY) {
390 encoder->keyframes++;
391 }
392
393 if (size_changed) {
395 }
396
397 size_t header_size = 5;
398 size_t packet_size = encoder->packet->size;
399 size_t required_size = header_size + packet_size;
400
401 if (required_size > *output_size) {
402 return SET_ERRNO(ERROR_NETWORK_SIZE, "Output buffer too small: need %zu, have %zu", required_size, *output_size);
403 }
404
405 // Write header: [flags][width:u16][height:u16]
406 output_buf[0] = flags;
407 output_buf[1] = (width >> 8) & 0xFF;
408 output_buf[2] = width & 0xFF;
409 output_buf[3] = (height >> 8) & 0xFF;
410 output_buf[4] = height & 0xFF;
411
412 // Write encoded data
413 if (packet_size > 0) {
414 memcpy(output_buf + header_size, encoder->packet->data, packet_size);
415 }
416
417 *output_size = required_size;
418 ascii_otel_metric(ASCII_OTEL_METRIC_VIDEO_BYTES, (double)required_size);
419 if (encoder->packet->flags & AV_PKT_FLAG_KEY)
420 ascii_otel_metric(ASCII_OTEL_METRIC_VIDEO_KEYFRAMES, 1);
421 ASCII_OTEL_ATTR_INT("video.packet.size", (int64_t)required_size);
422 ASCII_OTEL_ATTR_INT("video.frame.keyframe", (encoder->packet->flags & AV_PKT_FLAG_KEY) != 0);
423 ascii_otel_metric(ASCII_OTEL_METRIC_VIDEO_ENCODE_FRAMES, 1);
424 ascii_otel_metric(ASCII_OTEL_METRIC_VIDEO_ENCODE_INPUT, (double)width * height * 3);
425 ascii_otel_metric(ASCII_OTEL_METRIC_VIDEO_ENCODE_BYTES_IN, (double)width * height * 3);
426 ascii_otel_metric(ASCII_OTEL_METRIC_VIDEO_ENCODE_BYTES_OUT, (double)required_size);
427 encoder->total_frames++;
429 ascii_otel_metric(ASCII_OTEL_METRIC_VIDEO_WIDTH, encoder->frame->width);
430 ascii_otel_metric(ASCII_OTEL_METRIC_VIDEO_HEIGHT, encoder->frame->height);
431 if (required_size)
432 ascii_otel_metric(ASCII_OTEL_METRIC_VIDEO_RATIO,
433 (double)encoder->frame->width * encoder->frame->height * 3 / (double)required_size);
434 log_dev("[H265_ENCODE_DONE] Encoding complete, output_size=%zu", required_size);
435
436 return ASCIICHAT_OK;
437}
#define SET_ERRNO(code,...)
#define H265_ENCODER_FLAG_KEYFRAME
Definition encoder.h:34
#define H265_ENCODER_FLAG_SIZE_CHANGE
Definition encoder.h:35
unsigned int height
Definition fft.c:23
unsigned int width
Definition fft.c:23
unsigned long long uint64_t
Definition common.h:63
unsigned char uint8_t
Definition common.h:60
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_MEDIA_DECODE
Definition error_codes.h:67
@ ERROR_NETWORK_SIZE
Definition error_codes.h:77
@ ERROR_MEDIA_INIT
Definition error_codes.h:65
@ ERROR_INTERNAL
Definition error_codes.h:87
#define log_dev(...)
Log a DEV message (most verbose, development only).
Definition log/log.h:581
#define log_error(...)
Log an ERROR message.
Definition log/log.h:634
#define US_PER_SEC_INT
Definition time.h:173
#define EAGAIN
void stats_counter_add(stats_scope_t *scope, stats_counter_id_t id, uint64_t amount)
void stats_duration_record(stats_scope_t *scope, stats_duration_id_t id, uint64_t elapsed_ns)
#define log_debug_every(interval_us, fmt,...)
Rate-limited DEBUG logging.
Definition log/log.h:749
void ascii_otel_metric(ascii_otel_metric_t metric, double value)
Definition otel.c:161
#define ASCII_OTEL_SCOPE(id)
Definition otel.h:72
#define ASCII_OTEL_ATTR_INT(key, value)
Definition otel.h:67
#define ASCII_OTEL_ATTR_STRING(key, value)
Definition otel.h:66
stats_scope_t * stats_runtime_scope(void)
Definition runtime.c:80
uint64_t keyframes
Definition encoder.c:82
const AVCodec * codec
Definition encoder.c:67
uint64_t total_frames
Definition encoder.c:81
uint16_t current_height
Definition encoder.c:72
AVFrame * frame
Definition encoder.c:68
uint16_t current_width
Definition encoder.c:71
uint64_t frames_submitted
Definition encoder.c:80
uint8_t * yuv_buf
Definition encoder.c:74
AVCodecContext * codec_ctx
Definition encoder.c:66
bool request_keyframe
Definition encoder.c:78
AVPacket * packet
Definition encoder.c:69

References ASCII_OTEL_ATTR_INT, ASCII_OTEL_ATTR_STRING, ascii_otel_metric(), ASCII_OTEL_SCOPE, ASCIICHAT_OK, h265_encoder::codec, h265_encoder::codec_ctx, h265_encoder::current_height, h265_encoder::current_width, EAGAIN, ERROR_INTERNAL, ERROR_MEDIA_DECODE, ERROR_MEDIA_INIT, ERROR_NETWORK_SIZE, h265_encoder::frame, h265_encoder::frames_submitted, H265_ENCODER_FLAG_KEYFRAME, H265_ENCODER_FLAG_SIZE_CHANGE, height, h265_encoder::keyframes, log_debug_every, log_dev, log_error, h265_encoder::packet, h265_encoder::request_keyframe, SET_ERRNO, stats_counter_add(), STATS_COUNTER_FRAMES_ENCODED, STATS_DURATION_ENCODE, stats_duration_record(), stats_runtime_scope(), h265_encoder::total_frames, US_PER_SEC_INT, width, and h265_encoder::yuv_buf.

Referenced by acip_send_image_frame_h265(), h265_client_capture_and_encode(), and threaded_send_image_frame_encoded().

◆ h265_encoder_create()

h265_encoder_t * h265_encoder_create ( uint16_t initial_width,
uint16_t initial_height )

Create a new x265 encoder for ASCII frames

Parameters
initial_widthInitial frame width (characters)
initial_heightInitial frame height (characters)
Returns
Encoder handle, or NULL on error

Definition at line 86 of file encoder.c.

86 {
87 ASCII_OTEL_SCOPE(H265_ENCODER_CREATE);
88 return video_encoder_create(VIDEO_CODEC_H265, initial_width, initial_height);
89}
@ VIDEO_CODEC_H265
H.265 (HEVC) video codec.
Definition codecs.h:30
h265_encoder_t * video_encoder_create(video_codec_t codec_type, uint16_t initial_width, uint16_t initial_height)
Definition encoder.c:91

References ASCII_OTEL_SCOPE, VIDEO_CODEC_H265, and video_encoder_create().

Referenced by h265_client_context_create().

◆ h265_encoder_destroy()

void h265_encoder_destroy ( h265_encoder_t * encoder)

Destroy an x265 encoder

Parameters
encoderEncoder to destroy (may be NULL)

Definition at line 179 of file encoder.c.

179 {
180 if (!encoder)
181 return;
182
183 if (encoder->frame) {
184 av_frame_free(&encoder->frame);
185 }
186 if (encoder->packet) {
187 av_packet_free(&encoder->packet);
188 }
189 if (encoder->codec_ctx) {
190 LOG_IO("video encoder", { avcodec_free_context(&encoder->codec_ctx); });
191 }
192 if (encoder->yuv_buf) {
193 SAFE_FREE(encoder->yuv_buf);
194 }
195 SAFE_FREE(encoder);
196}
#define SAFE_FREE(ptr)
Definition common.h:377
#define LOG_IO(prefix, block)
Definition io.h:104

References h265_encoder::codec_ctx, h265_encoder::frame, LOG_IO, h265_encoder::packet, SAFE_FREE, and h265_encoder::yuv_buf.

Referenced by h265_client_context_create(), h265_client_context_destroy(), and video_encoder_create().

◆ h265_encoder_flush()

asciichat_error_t h265_encoder_flush ( h265_encoder_t * encoder,
uint8_t * output_buf,
size_t * output_size )

Flush remaining buffered frames from encoder

Sends a NULL frame to the encoder to flush all buffered frames. This should be called before destroying the encoder or when you need to ensure all encoded data is retrieved.

Parameters
encoderEncoder handle
output_bufBuffer for encoded output
output_sizeInput: buffer size, Output: actual encoded size
Returns
Error code (ASCIICHAT_OK on success, ASCIICHAT_OK with size=0 when no more data)

USAGE: // After final h265_encode() call, flush remaining frames while (size > 0) { size = max_size; h265_encoder_flush(encoder, buf, &size); if (size > 0) { // Send the flushed frame... } }

Definition at line 445 of file encoder.c.

445 {
446 ASCII_OTEL_SCOPE(H265_ENCODER_FLUSH);
447 if (!encoder || !output_buf || !output_size) {
448 return SET_ERRNO(ERROR_INTERNAL, "Invalid encoder or output parameters for flush");
449 }
450
451 if (*output_size < 5) {
452 return SET_ERRNO(ERROR_NETWORK_SIZE, "Output buffer too small (minimum 5 bytes)");
453 }
454
455 if (!encoder->codec_opened) {
456 *output_size = 0;
457 return ASCIICHAT_OK;
458 }
459
460 // Send NULL frame to signal end of stream (flushes encoder)
461 log_debug("H265_FLUSH: Sending NULL frame to encoder to flush buffered data");
462 uint64_t stats_codec_start = time_get_ns();
463 int send_ret = avcodec_send_frame(encoder->codec_ctx, NULL);
464 if (send_ret < 0) {
465 stats_duration_record(stats_runtime_scope(), STATS_DURATION_ENCODE, time_get_ns() - stats_codec_start);
466 log_error("H265_FLUSH: avcodec_send_frame(NULL) failed: %d", send_ret);
467 return SET_ERRNO(ERROR_MEDIA_DECODE, "Failed to flush encoder");
468 }
469
470 // Try to receive a packet
471 log_debug("H265_FLUSH: Attempting to receive flushed packet...");
472 int recv_ret = avcodec_receive_packet(encoder->codec_ctx, encoder->packet);
473 stats_duration_record(stats_runtime_scope(), STATS_DURATION_ENCODE, time_get_ns() - stats_codec_start);
474 if (recv_ret == AVERROR(EAGAIN)) {
475 log_dev("H265_FLUSH: EAGAIN - no more frames available");
476 *output_size = 0;
477 return ASCIICHAT_OK;
478 }
479 if (recv_ret == AVERROR_EOF) {
480 log_debug("H265_FLUSH: EOF - encoder is flushed");
481 *output_size = 0;
482 return ASCIICHAT_OK;
483 }
484 if (recv_ret < 0) {
485 log_error("H265_FLUSH: avcodec_receive_packet failed: %d", recv_ret);
486 return SET_ERRNO(ERROR_MEDIA_DECODE, "Failed to receive flushed packet");
487 }
488
489 log_debug("H265_FLUSH: Got flushed packet: size=%d", (int)encoder->packet->size);
490
491 // Check if packet size exceeds output buffer
492 size_t header_size = 5;
493 size_t packet_size = encoder->packet->size;
494 size_t required_size = header_size + packet_size;
495
496 if (required_size > *output_size) {
497 return SET_ERRNO(ERROR_NETWORK_SIZE, "Output buffer too small: need %zu, have %zu", required_size, *output_size);
498 }
499
500 // Write header: [flags][width:u16][height:u16]
501 uint8_t flags = 0;
502 if (encoder->packet->flags & AV_PKT_FLAG_KEY) {
504 encoder->keyframes++;
505 }
506
507 output_buf[0] = flags;
508 output_buf[1] = (encoder->current_width >> 8) & 0xFF;
509 output_buf[2] = encoder->current_width & 0xFF;
510 output_buf[3] = (encoder->current_height >> 8) & 0xFF;
511 output_buf[4] = encoder->current_height & 0xFF;
512
513 // Write encoded data
514 if (packet_size > 0) {
515 memcpy(output_buf + header_size, encoder->packet->data, packet_size);
516 }
517
518 *output_size = required_size;
519 ASCII_OTEL_ATTR_INT("video.packet.size", (int64_t)required_size);
520 if (encoder->codec)
521 ASCII_OTEL_ATTR_STRING("video.codec", encoder->codec->id == AV_CODEC_ID_H264 ? "h264" : "h265");
522 ascii_otel_metric(ASCII_OTEL_METRIC_VIDEO_ENCODE_FRAMES, 1);
523 ascii_otel_metric(ASCII_OTEL_METRIC_VIDEO_ENCODE_BYTES_OUT, (double)required_size);
524 ascii_otel_metric(ASCII_OTEL_METRIC_VIDEO_BYTES, (double)required_size);
525 if (encoder->packet->flags & AV_PKT_FLAG_KEY)
526 ascii_otel_metric(ASCII_OTEL_METRIC_VIDEO_KEYFRAMES, 1);
527 encoder->total_frames++;
529
530 return ASCIICHAT_OK;
531}
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:595
bool codec_opened
Definition encoder.c:77

References ASCII_OTEL_ATTR_INT, ASCII_OTEL_ATTR_STRING, ascii_otel_metric(), ASCII_OTEL_SCOPE, ASCIICHAT_OK, h265_encoder::codec, h265_encoder::codec_ctx, h265_encoder::codec_opened, h265_encoder::current_height, h265_encoder::current_width, EAGAIN, ERROR_INTERNAL, ERROR_MEDIA_DECODE, ERROR_NETWORK_SIZE, H265_ENCODER_FLAG_KEYFRAME, h265_encoder::keyframes, log_debug, log_dev, log_error, h265_encoder::packet, SET_ERRNO, stats_counter_add(), STATS_COUNTER_FRAMES_ENCODED, STATS_DURATION_ENCODE, stats_duration_record(), stats_runtime_scope(), and h265_encoder::total_frames.

◆ h265_encoder_get_stats()

void h265_encoder_get_stats ( h265_encoder_t * encoder,
uint64_t * total_frames,
uint64_t * keyframes,
uint32_t * avg_bitrate )

Get encoder statistics

Parameters
encoderEncoder handle
total_framesOutput: total frames encoded
keyframesOutput: total keyframes encoded
avg_bitrateOutput: average bitrate in bits per second

Definition at line 533 of file encoder.c.

534 {
535 if (!encoder)
536 return;
537
538 if (total_frames)
539 *total_frames = encoder->total_frames;
540 if (keyframes)
541 *keyframes = encoder->keyframes;
542 if (avg_bitrate)
543 *avg_bitrate = encoder->avg_bitrate;
544}
uint32_t avg_bitrate
Definition encoder.c:83

References h265_encoder::avg_bitrate, h265_encoder::keyframes, and h265_encoder::total_frames.

Referenced by h265_client_get_stats().

◆ h265_encoder_request_keyframe()

void h265_encoder_request_keyframe ( h265_encoder_t * encoder)

Request a keyframe on the next encode

Forces the encoder to produce an I-frame (keyframe) on the next call to h265_encoder_encode(). Useful for error recovery or stream synchronization.

Parameters
encoderEncoder handle

Definition at line 439 of file encoder.c.

439 {
440 if (encoder) {
441 encoder->request_keyframe = true;
442 }
443}

References h265_encoder::request_keyframe.

Referenced by acip_send_image_frame_h265(), h265_client_request_keyframe(), and threaded_send_image_frame_encoded().

◆ video_encoder_create()

h265_encoder_t * video_encoder_create ( video_codec_t codec,
uint16_t initial_width,
uint16_t initial_height )

Create an HEVC or H.264 RGB24 upload encoder with the shared frame envelope. The legacy h265 encode/destroy functions also operate on H.264 instances.

Definition at line 91 of file encoder.c.

91 {
92 ASCII_OTEL_SCOPE(VIDEO_ENCODER_CREATE);
93 ASCII_OTEL_ATTR_STRING("video.codec", codec_type == VIDEO_CODEC_H264 ? "h264" : "h265");
94 ASCII_OTEL_ATTR_INT("video.frame.width", initial_width);
95 ASCII_OTEL_ATTR_INT("video.frame.height", initial_height);
96 if (codec_type != VIDEO_CODEC_H265 && codec_type != VIDEO_CODEC_H264) {
97 SET_ERRNO(ERROR_INVALID_PARAM, "Expected HEVC or H.264 codec");
98 return NULL;
99 }
100 if (initial_width == 0 || initial_height == 0) {
101 SET_ERRNO(ERROR_MEDIA_INIT, "Frame dimensions must be non-zero");
102 return NULL;
103 }
104
106 if (!enc) {
107 SET_ERRNO(ERROR_MEMORY, "Failed to allocate encoder structure");
108 return NULL;
109 }
110
111 defer_named(cleanup, h265_encoder_destroy(enc));
112
113 const AVCodec *codec = video_encoder_find_codec(codec_type);
114 if (!codec) {
115 SET_ERRNO(ERROR_MEDIA_INIT, "%s encoder not found", codec_type == VIDEO_CODEC_H264 ? "H.264" : "HEVC");
116 return NULL;
117 }
118 enc->codec = codec;
119
120 enc->codec_ctx = avcodec_alloc_context3(codec);
121 if (!enc->codec_ctx) {
122 SET_ERRNO(ERROR_MEMORY, "Failed to allocate codec context");
123 return NULL;
124 }
125
126 // Configure codec context
127 enc->codec_ctx->width = initial_width;
128 enc->codec_ctx->height = initial_height;
129 enc->codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
130 enc->codec_ctx->time_base = (AVRational){1, 30};
131 enc->codec_ctx->framerate = (AVRational){30, 1};
132
133 // Set ultrafast preset for low-latency encoding
134 if (av_opt_set(enc->codec_ctx->priv_data, "preset", "ultrafast", 0) < 0) {
135 log_warn("Failed to set preset to ultrafast, using default");
136 }
137 if (av_opt_set(enc->codec_ctx->priv_data, "tune", "zerolatency", 0) < 0) {
138 log_warn("Failed to set tune to zerolatency, using default");
139 }
140
141 enc->codec_ctx->thread_count = h265_thread_count();
142
143 // Defer avcodec_open2 to first encode call. The initial resolution (800x600) is
144 // usually wrong — the actual frame resolution is only known when the first capture
145 // arrives. Opening the codec here would create an x265 thread pool (~1s) that gets
146 // immediately discarded on reconfigure, wasting startup time.
147 enc->codec_opened = false;
148
149 // Allocate frame and packet
150 enc->frame = av_frame_alloc();
151 enc->packet = av_packet_alloc();
152
153 if (!enc->frame || !enc->packet) {
154 SET_ERRNO(ERROR_MEMORY, "Failed to allocate frame/packet");
155 return NULL;
156 }
157
158 enc->current_width = initial_width;
159 enc->current_height = initial_height;
160
161 // Allocate YUV420P buffer (Y plane + U/V planes)
162 size_t stride = initial_width;
163 enc->yuv_buf_size = stride * initial_height * 3 / 2;
164 enc->yuv_buf = SAFE_MALLOC(enc->yuv_buf_size, uint8_t *);
165 if (!enc->yuv_buf) {
166 SET_ERRNO(ERROR_MEMORY, "Failed to allocate YUV buffer");
167 return NULL;
168 }
169
170 log_info("FFmpeg video encoder created: %ux%u (codec: %s)", initial_width, initial_height, codec->name);
171
172 // Register encoder for debugging
173 NAMED_REGISTER_H265_ENCODER(enc, "h265_encoder", NULL);
174
175 defer_cancel(cleanup);
176 return enc;
177}
@ VIDEO_CODEC_H264
H.264 (AVC) video codec.
Definition codecs.h:32
void h265_encoder_destroy(h265_encoder_t *encoder)
Definition encoder.c:179
struct h265_encoder h265_encoder_t
h265 encoder is intentionally a small, concrete record. Fields such as codec_ctx, codec,...
#define SAFE_MALLOC(size, cast)
Definition common.h:265
#define SAFE_CALLOC(count, size, cast)
Definition common.h:275
#define NAMED_REGISTER_H265_ENCODER(encoder, name, parent_ptr)
Register an H.265 encoder with automatic format specifier.
@ ERROR_MEMORY
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
#define log_warn(...)
Log a WARN message.
Definition log/log.h:621
#define log_info(...)
Log an INFO message.
Definition log/log.h:608
size_t yuv_buf_size
Definition encoder.c:75

References ASCII_OTEL_ATTR_INT, ASCII_OTEL_ATTR_STRING, ASCII_OTEL_SCOPE, h265_encoder::codec, h265_encoder::codec_ctx, h265_encoder::codec_opened, h265_encoder::current_height, h265_encoder::current_width, ERROR_INVALID_PARAM, ERROR_MEDIA_INIT, ERROR_MEMORY, h265_encoder::frame, h265_encoder_destroy(), log_info, log_warn, NAMED_REGISTER_H265_ENCODER, h265_encoder::packet, SAFE_CALLOC, SAFE_MALLOC, SET_ERRNO, VIDEO_CODEC_H264, VIDEO_CODEC_H265, h265_encoder::yuv_buf, and h265_encoder::yuv_buf_size.

Referenced by h265_encoder_create(), and threaded_send_image_frame_encoded().

◆ video_encoder_is_available()

bool video_encoder_is_available ( video_codec_t codec)

Return whether a usable software encoder is registered for codec.

This probe is side-effect free: it does not allocate, open a codec context, or set errno. Automatic capture uses it to avoid probing unavailable codecs and reporting expected fallback failures to the user.

Definition at line 54 of file encoder.c.

54 {
55 if (codec != VIDEO_CODEC_H265 && codec != VIDEO_CODEC_H264)
56 return false;
57 return video_encoder_find_codec(codec) != NULL;
58}

References VIDEO_CODEC_H264, and VIDEO_CODEC_H265.