ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
decoder.c
Go to the documentation of this file.
1
7#include <ascii-chat/common.h>
9#include <ascii-chat/log/io.h>
11
12#include <libavcodec/avcodec.h>
13#include <libavutil/frame.h>
14
15#include <string.h>
16#include <stdlib.h>
17
18typedef struct h265_decoder {
19 AVCodecContext *codec_ctx;
20 AVFrame *frame;
21 AVPacket *packet;
22 AVCodecParserContext *parser;
23
26
30
33 if (!dec) {
34 SET_ERRNO(ERROR_MEMORY, "Failed to allocate decoder structure");
35 return NULL;
36 }
37
38 // Find HEVC decoder
39 const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_HEVC);
40 if (!codec) {
41 SET_ERRNO(ERROR_MEDIA_INIT, "HEVC decoder not found");
42 SAFE_FREE(dec);
43 return NULL;
44 }
45
46 dec->codec_ctx = avcodec_alloc_context3(codec);
47 if (!dec->codec_ctx) {
48 SET_ERRNO(ERROR_MEMORY, "Failed to allocate codec context");
49 SAFE_FREE(dec);
50 return NULL;
51 }
52
53 // Open decoder (capture x265 library output during initialization)
54 int codec_open_result = 0;
55 LOG_IO("hevc", { codec_open_result = avcodec_open2(dec->codec_ctx, codec, NULL); });
56
57 if (codec_open_result < 0) {
58 SET_ERRNO(ERROR_MEDIA_INIT, "Failed to open HEVC decoder");
59 avcodec_free_context(&dec->codec_ctx);
60 SAFE_FREE(dec);
61 return NULL;
62 }
63
64 // Allocate frame and packet
65 dec->frame = av_frame_alloc();
66 dec->packet = av_packet_alloc();
67 dec->parser = av_parser_init(AV_CODEC_ID_HEVC);
68
69 if (!dec->frame || !dec->packet || !dec->parser) {
70 SET_ERRNO(ERROR_MEMORY, "Failed to allocate frame/packet/parser");
71 if (dec->frame)
72 av_frame_free(&dec->frame);
73 if (dec->packet)
74 av_packet_free(&dec->packet);
75 if (dec->parser)
76 av_parser_close(dec->parser);
77 avcodec_free_context(&dec->codec_ctx);
78 SAFE_FREE(dec);
79 return NULL;
80 }
81
82 log_info("FFmpeg HEVC decoder created");
83
84 // Register decoder for debugging
85 NAMED_REGISTER_H265_DECODER(dec, "h265_decoder", NULL);
86
87 return dec;
88}
89
91 if (!decoder)
92 return;
93
94 if (decoder->frame) {
95 av_frame_free(&decoder->frame);
96 }
97 if (decoder->packet) {
98 av_packet_free(&decoder->packet);
99 }
100 if (decoder->parser) {
101 av_parser_close(decoder->parser);
102 }
103 if (decoder->codec_ctx) {
104 avcodec_free_context(&decoder->codec_ctx);
105 }
106 SAFE_FREE(decoder);
107}
108
109asciichat_error_t h265_decode(h265_decoder_t *decoder, const uint8_t *encoded_packet, size_t packet_size,
110 uint16_t *output_width, uint16_t *output_height, uint8_t *output_buf,
111 size_t *output_size) {
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 av_packet_from_data(decoder->packet, (uint8_t *)hevc_data, hevc_size);
142
143 if (avcodec_send_packet(decoder->codec_ctx, decoder->packet) < 0) {
144 return SET_ERRNO(ERROR_MEDIA_DECODE, "Failed to send packet to decoder");
145 }
146
147 if (avcodec_receive_frame(decoder->codec_ctx, decoder->frame) < 0) {
148 return SET_ERRNO(ERROR_MEDIA_DECODE, "Failed to receive decoded frame");
149 }
150 } else {
151 // Empty packet - just signal a frame boundary
152 if (avcodec_send_packet(decoder->codec_ctx, NULL) < 0) {
153 return SET_ERRNO(ERROR_MEDIA_DECODE, "Failed to flush decoder");
154 }
155 }
156
157 // Extract Y plane (grayscale intensity)
158 uint8_t *y_plane = decoder->frame->data[0];
159 int y_stride = decoder->frame->linesize[0];
160 int img_width = decoder->frame->width;
161 int img_height = decoder->frame->height;
162
163 if (img_width != (int)width || img_height != (int)height) {
164 log_warn("Image dimensions mismatch: expected %ux%u, got %dx%d", width, height, img_width, img_height);
165 }
166
167 // Copy Y plane to output (ASCII intensity values)
168 for (int y = 0; y < img_height && y < (int)height; y++) {
169 memcpy(output_buf + y * width, y_plane + y * y_stride, width);
170 }
171
172 *output_width = width;
173 *output_height = height;
174 *output_size = width * height;
175
176 decoder->last_width = width;
177 decoder->last_height = height;
178 decoder->total_frames++;
179
180 if (flags & H265_DECODER_FLAG_KEYFRAME) {
181 decoder->keyframes++;
182 }
183
184 return ASCIICHAT_OK;
185}
186
187void h265_decoder_get_stats(h265_decoder_t *decoder, uint64_t *total_frames, uint64_t *keyframes, uint16_t *last_width,
188 uint16_t *last_height) {
189 if (!decoder)
190 return;
191
192 if (total_frames)
193 *total_frames = decoder->total_frames;
194 if (keyframes)
195 *keyframes = decoder->keyframes;
196 if (last_width)
197 *last_width = decoder->last_width;
198 if (last_height)
199 *last_height = decoder->last_height;
200}
⚙️ Common definitions, error codes, macros, and types shared throughout the application
Named object registry for debugging — log identifiable resource names.
void h265_decoder_destroy(h265_decoder_t *decoder)
Definition decoder.c:90
struct h265_decoder h265_decoder_t
h265_decoder_t * h265_decoder_create(void)
Definition decoder.c:31
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)
Definition decoder.c:109
void h265_decoder_get_stats(h265_decoder_t *decoder, uint64_t *total_frames, uint64_t *keyframes, uint16_t *last_width, uint16_t *last_height)
Definition decoder.c:187
HEVC/H.265 decoder for ASCII art frames using libde265.
#define H265_DECODER_FLAG_KEYFRAME
Definition decoder.h:28
unsigned short uint16_t
Definition common.h:57
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_CALLOC(count, size, cast)
Definition common.h:274
unsigned long long uint64_t
Definition common.h:59
unsigned char uint8_t
Definition common.h:56
#define NAMED_REGISTER_H265_DECODER(decoder, name, parent_ptr)
Register an H.265 decoder with automatic format specifier.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:81
@ ERROR_MEMORY
Definition error_codes.h:56
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_MEDIA_DECODE
Definition error_codes.h:72
@ ERROR_NETWORK_SIZE
Definition error_codes.h:82
@ ERROR_MEDIA_INIT
Definition error_codes.h:70
@ ERROR_INTERNAL
Definition error_codes.h:92
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
Capture stdout/stderr and redirect to logging system.
#define LOG_IO(prefix, block)
Capture output from a code block and log it.
Definition io.h:81
AVFrame * frame
Definition decoder.c:20
uint64_t keyframes
Definition decoder.c:28
uint16_t last_width
Definition decoder.c:24
uint64_t total_frames
Definition decoder.c:27
uint16_t last_height
Definition decoder.c:25
AVCodecContext * codec_ctx
Definition decoder.c:19
AVCodecParserContext * parser
Definition decoder.c:22
AVPacket * packet
Definition decoder.c:21
Cross-platform system functions interface for ascii-chat.