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

HEVC/H.265 decoder for ASCII art frames using libde265. More...

Go to the source code of this file.

Macros

#define H265_DECODER_FLAG_KEYFRAME   0x01
#define H265_DECODER_FLAG_SIZE_CHANGE   0x02

Functions

h265_decoder_t * h265_decoder_create (void)
void h265_decoder_destroy (h265_decoder_t *decoder)
asciichat_error_t h265_decode (h265_decoder_t *decoder, const uint8_t *encoded_packet, size_t packet_size, uint16_t *output_width, uint16_t *output_height, uint8_t *output_buf, size_t *output_size)
void h265_decoder_get_stats (h265_decoder_t *decoder, uint64_t *total_frames, uint64_t *keyframes, uint16_t *last_width, uint16_t *last_height)

Detailed Description

HEVC/H.265 decoder for ASCII art frames using libde265.

Decodes x265-encoded ASCII art frames back into terminal grid data. Automatically reconfigures when frame size changes.

FRAME SIZE CHANGES: The decoder reads width/height from each packet and reconfigures as needed. No explicit API call required - handled transparently during decode.

PACKET FORMAT: [flags: u8][width: u16][height: u16][encoded_data: x265]

Definition in file video/h265/decoder.h.

Macro Definition Documentation

◆ H265_DECODER_FLAG_KEYFRAME

#define H265_DECODER_FLAG_KEYFRAME   0x01

Decoder flags (from packet)

Definition at line 28 of file video/h265/decoder.h.

Referenced by h265_decode().

◆ H265_DECODER_FLAG_SIZE_CHANGE

#define H265_DECODER_FLAG_SIZE_CHANGE   0x02

Definition at line 29 of file video/h265/decoder.h.

Function Documentation

◆ h265_decode()

asciichat_error_t h265_decode ( h265_decoder_t * decoder,
const uint8_t * encoded_packet,
size_t packet_size,
uint16_t * output_width,
uint16_t * output_height,
uint8_t * output_buf,
size_t * output_size )

Decode an x265-encoded ASCII frame

The decoder reads width/height from the packet and automatically reconfigures when frame dimensions change.

Parameters
decoderDecoder handle
encoded_packetEncoded packet (flags+width+height+x265_data)
packet_sizeSize of encoded packet
output_widthOutput: decoded frame width
output_heightOutput: decoded frame height
output_bufBuffer for decoded ASCII data (should be at least width×height)
output_sizeInput: buffer size, Output: actual decoded size
Returns
Error code (ASCIICHAT_OK on success)

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

Call this as: uint16_t width, height; uint8_t ascii_grid[256*64]; size_t decoded_size = sizeof(ascii_grid); h265_decode(decoder, packet, packet_size, &width, &height, ascii_grid, &decoded_size);

Definition at line 108 of file video/h265/decoder.c.

110 {
111 ASCII_OTEL_SCOPE(H265_DECODE);
112 if (!decoder || !encoded_packet || !output_width || !output_height || !output_buf || !output_size) {
113 return SET_ERRNO(ERROR_INTERNAL, "Invalid decoder arguments");
114 }
115
116 if (packet_size < 5) {
117 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Packet too small (minimum 5 bytes)");
118 }
119
120 // Parse header
121 uint8_t flags = encoded_packet[0];
122 uint16_t width = ((uint16_t)encoded_packet[1] << 8) | encoded_packet[2];
123 uint16_t height = ((uint16_t)encoded_packet[3] << 8) | encoded_packet[4];
124
125 if (width == 0 || height == 0) {
126 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid frame dimensions: %ux%u", width, height);
127 }
128
129 size_t required_output = width * height;
130 if (*output_size < required_output) {
131 *output_size = required_output;
132 return SET_ERRNO(ERROR_NETWORK_SIZE, "Output buffer too small: need %zu, have %zu", required_output, *output_size);
133 }
134
135 // Decode HEVC data
136 if (packet_size > 5) {
137 const uint8_t *hevc_data = encoded_packet + 5;
138 size_t hevc_size = packet_size - 5;
139
140 // Send packet to decoder
141 // FFmpeg owns packet storage and requires zero padding after the payload.
142 // The wire buffer belongs to the caller and may be stack allocated.
143 if (hevc_size > INT_MAX)
144 return SET_ERRNO(ERROR_NETWORK_SIZE, "Encoded video packet exceeds FFmpeg limits");
145 av_packet_unref(decoder->packet);
146 if (av_new_packet(decoder->packet, (int)hevc_size) < 0)
147 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate decoder packet");
148 memcpy(decoder->packet->data, hevc_data, hevc_size);
149
150 uint64_t stats_codec_start = time_get_ns();
151 int send_result = avcodec_send_packet(decoder->codec_ctx, decoder->packet);
152 av_packet_unref(decoder->packet);
153 if (send_result < 0) {
154 return SET_ERRNO(ERROR_MEDIA_DECODE, "Failed to send packet to decoder");
155 }
156
157 int stats_decode_result = avcodec_receive_frame(decoder->codec_ctx, decoder->frame);
158 stats_duration_record(stats_runtime_scope(), STATS_DURATION_DECODE, time_get_ns() - stats_codec_start);
159 if (stats_decode_result < 0) {
160 return SET_ERRNO(ERROR_MEDIA_DECODE, "Failed to receive decoded frame");
161 }
162 } else {
163 // Empty packet - just signal a frame boundary
164 if (avcodec_send_packet(decoder->codec_ctx, NULL) < 0) {
165 return SET_ERRNO(ERROR_MEDIA_DECODE, "Failed to flush decoder");
166 }
167 }
168
169 // Extract Y plane (grayscale intensity)
170 uint8_t *y_plane = decoder->frame->data[0];
171 int y_stride = decoder->frame->linesize[0];
172 int img_width = decoder->frame->width;
173 int img_height = decoder->frame->height;
174
175 if (img_width != (int)width || img_height != (int)height) {
176 log_warn("Image dimensions mismatch: expected %ux%u, got %dx%d", width, height, img_width, img_height);
177 }
178
179 // Copy Y plane to output (ASCII intensity values)
180 for (int y = 0; y < img_height && y < (int)height; y++) {
181 memcpy(output_buf + y * width, y_plane + y * y_stride, width);
182 }
183
184 *output_width = width;
185 *output_height = height;
186 *output_size = width * height;
187
188 decoder->last_width = width;
189 decoder->last_height = height;
190 decoder->total_frames++;
191 ASCII_OTEL_ATTR_STRING("video.codec", "h265");
192 ASCII_OTEL_ATTR_INT("video.frame.width", width);
193 ASCII_OTEL_ATTR_INT("video.frame.height", height);
194 ASCII_OTEL_ATTR_INT("video.frame.keyframe", (flags & H265_DECODER_FLAG_KEYFRAME) != 0);
195 ASCII_OTEL_ATTR_INT("video.packet.size", (int64_t)packet_size);
196 ascii_otel_metric(ASCII_OTEL_METRIC_VIDEO_DECODE_BYTES, (double)packet_size);
197 ascii_otel_metric(ASCII_OTEL_METRIC_VIDEO_DECODE_FRAMES, 1);
198 ascii_otel_metric(ASCII_OTEL_METRIC_VIDEO_DECODE_BYTES_OUT, (double)*output_size);
200
201 if (flags & H265_DECODER_FLAG_KEYFRAME) {
202 decoder->keyframes++;
203 }
204
205 return ASCIICHAT_OK;
206}
#define SET_ERRNO(code,...)
unsigned int height
Definition fft.c:23
unsigned int width
Definition fft.c:23
unsigned short uint16_t
Definition common.h:61
unsigned long long uint64_t
Definition common.h:63
unsigned char uint8_t
Definition common.h:60
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:76
@ ERROR_MEMORY
Definition error_codes.h:51
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_MEDIA_DECODE
Definition error_codes.h:67
@ ERROR_NETWORK_SIZE
Definition error_codes.h:77
@ ERROR_INTERNAL
Definition error_codes.h:87
#define log_warn(...)
Log a WARN message.
Definition log/log.h:621
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)
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
AVCodecContext * codec_ctx
#define H265_DECODER_FLAG_KEYFRAME

References ASCII_OTEL_ATTR_INT, ASCII_OTEL_ATTR_STRING, ascii_otel_metric(), ASCII_OTEL_SCOPE, ASCIICHAT_OK, h265_decoder::codec_ctx, ERROR_INTERNAL, ERROR_MEDIA_DECODE, ERROR_MEMORY, ERROR_NETWORK_PROTOCOL, ERROR_NETWORK_SIZE, h265_decoder::frame, H265_DECODER_FLAG_KEYFRAME, height, h265_decoder::keyframes, h265_decoder::last_height, h265_decoder::last_width, log_warn, h265_decoder::packet, SET_ERRNO, stats_counter_add(), STATS_COUNTER_FRAMES_DECODED, STATS_DURATION_DECODE, stats_duration_record(), stats_runtime_scope(), h265_decoder::total_frames, and width.

Referenced by h265_server_decode_and_convert().

◆ h265_decoder_create()

h265_decoder_t * h265_decoder_create ( void )

Create a new x265 decoder for ASCII frames

Returns
Decoder handle, or NULL on error

Definition at line 38 of file video/h265/decoder.c.

38 {
39 ASCII_OTEL_SCOPE(H265_DECODER_CREATE);
41 if (!dec) {
42 SET_ERRNO(ERROR_MEMORY, "Failed to allocate decoder structure");
43 return NULL;
44 }
45
46 defer_named(cleanup, h265_decoder_destroy(dec));
47
48 // Find HEVC decoder
49 const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_HEVC);
50 if (!codec) {
51 SET_ERRNO(ERROR_MEDIA_INIT, "HEVC decoder not found");
52 return NULL;
53 }
54
55 dec->codec_ctx = avcodec_alloc_context3(codec);
56 if (!dec->codec_ctx) {
57 SET_ERRNO(ERROR_MEMORY, "Failed to allocate codec context");
58 return NULL;
59 }
60
61 // Open decoder (capture x265 library output during initialization)
62 int codec_open_result = 0;
63 LOG_IO("hevc", { codec_open_result = avcodec_open2(dec->codec_ctx, codec, NULL); });
64
65 if (codec_open_result < 0) {
66 SET_ERRNO(ERROR_MEDIA_INIT, "Failed to open HEVC decoder");
67 return NULL;
68 }
69
70 // Allocate frame and packet
71 dec->frame = av_frame_alloc();
72 dec->packet = av_packet_alloc();
73 dec->parser = av_parser_init(AV_CODEC_ID_HEVC);
74
75 if (!dec->frame || !dec->packet || !dec->parser) {
76 SET_ERRNO(ERROR_MEMORY, "Failed to allocate frame/packet/parser");
77 return NULL;
78 }
79
80 log_info("FFmpeg HEVC decoder created");
81
82 // Register decoder for debugging
83 NAMED_REGISTER_H265_DECODER(dec, "h265_decoder", NULL);
84
85 defer_cancel(cleanup);
86 return dec;
87}
#define SAFE_CALLOC(count, size, cast)
Definition common.h:275
#define NAMED_REGISTER_H265_DECODER(decoder, name, parent_ptr)
Register an H.265 decoder with automatic format specifier.
@ ERROR_MEDIA_INIT
Definition error_codes.h:65
#define log_info(...)
Log an INFO message.
Definition log/log.h:608
#define LOG_IO(prefix, block)
Definition io.h:104
AVCodecParserContext * parser
void h265_decoder_destroy(h265_decoder_t *decoder)
struct h265_decoder h265_decoder_t

References ASCII_OTEL_SCOPE, h265_decoder::codec_ctx, ERROR_MEDIA_INIT, ERROR_MEMORY, h265_decoder::frame, h265_decoder_destroy(), log_info, LOG_IO, NAMED_REGISTER_H265_DECODER, h265_decoder::packet, h265_decoder::parser, SAFE_CALLOC, and SET_ERRNO.

Referenced by h265_server_get_client_decoder().

◆ h265_decoder_destroy()

void h265_decoder_destroy ( h265_decoder_t * decoder)

Destroy an x265 decoder

Parameters
decoderDecoder to destroy (may be NULL)

Definition at line 89 of file video/h265/decoder.c.

89 {
90 if (!decoder)
91 return;
92
93 if (decoder->frame) {
94 av_frame_free(&decoder->frame);
95 }
96 if (decoder->packet) {
97 av_packet_free(&decoder->packet);
98 }
99 if (decoder->parser) {
100 av_parser_close(decoder->parser);
101 }
102 if (decoder->codec_ctx) {
103 avcodec_free_context(&decoder->codec_ctx);
104 }
105 SAFE_FREE(decoder);
106}
#define SAFE_FREE(ptr)
Definition common.h:377

References h265_decoder::codec_ctx, h265_decoder::frame, h265_decoder::packet, h265_decoder::parser, and SAFE_FREE.

Referenced by h265_decoder_create(), h265_server_context_destroy(), and h265_server_remove_client().

◆ h265_decoder_get_stats()

void h265_decoder_get_stats ( h265_decoder_t * decoder,
uint64_t * total_frames,
uint64_t * keyframes,
uint16_t * last_width,
uint16_t * last_height )

Get decoder statistics

Parameters
decoderDecoder handle
total_framesOutput: total frames decoded
keyframesOutput: total keyframes decoded
last_widthOutput: width of last decoded frame
last_heightOutput: height of last decoded frame

Definition at line 208 of file video/h265/decoder.c.

209 {
210 if (!decoder)
211 return;
212
213 if (total_frames)
214 *total_frames = decoder->total_frames;
215 if (keyframes)
216 *keyframes = decoder->keyframes;
217 if (last_width)
218 *last_width = decoder->last_width;
219 if (last_height)
220 *last_height = decoder->last_height;
221}

References h265_decoder::keyframes, h265_decoder::last_height, h265_decoder::last_width, and h265_decoder::total_frames.

Referenced by h265_server_client_get_stats().