FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/fftools/resources/resman.c
Date: 2025-06-01 09:29:47
Exec Total Coverage
Lines: 0 88 0.0%
Functions: 0 4 0.0%
Branches: 0 38 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2025 - softworkz
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /**
22 * @file
23 * output writers for filtergraph details
24 */
25
26 #include "config.h"
27
28 #include <string.h>
29
30 #if CONFIG_RESOURCE_COMPRESSION
31 #include <zlib.h>
32 #endif
33
34 #include "resman.h"
35 #include "fftools/ffmpeg_filter.h"
36 #include "libavutil/avassert.h"
37 #include "libavutil/pixdesc.h"
38 #include "libavutil/dict.h"
39 #include "libavutil/common.h"
40
41 extern const unsigned char ff_graph_html_data[];
42 extern const unsigned int ff_graph_html_len;
43
44 extern const unsigned char ff_graph_css_data[];
45 extern const unsigned ff_graph_css_len;
46
47 static const FFResourceDefinition resource_definitions[] = {
48 [FF_RESOURCE_GRAPH_CSS] = { FF_RESOURCE_GRAPH_CSS, "graph.css", &ff_graph_css_data[0], &ff_graph_css_len },
49 [FF_RESOURCE_GRAPH_HTML] = { FF_RESOURCE_GRAPH_HTML, "graph.html", &ff_graph_html_data[0], &ff_graph_html_len },
50 };
51
52
53 static const AVClass resman_class = {
54 .class_name = "ResourceManager",
55 };
56
57 typedef struct ResourceManagerContext {
58 const AVClass *class;
59 AVDictionary *resource_dic;
60 } ResourceManagerContext;
61
62 static AVMutex mutex = AV_MUTEX_INITIALIZER;
63
64 ResourceManagerContext *resman_ctx = NULL;
65
66
67 #if CONFIG_RESOURCE_COMPRESSION
68
69 static int decompress_gzip(ResourceManagerContext *ctx, uint8_t *in, unsigned in_len, char **out, size_t *out_len)
70 {
71 z_stream strm;
72 unsigned chunk = 65534;
73 int ret;
74 uint8_t *buf;
75
76 *out = NULL;
77 memset(&strm, 0, sizeof(strm));
78
79 // Allocate output buffer with extra byte for null termination
80 buf = (uint8_t *)av_mallocz(chunk + 1);
81 if (!buf) {
82 av_log(ctx, AV_LOG_ERROR, "Failed to allocate decompression buffer\n");
83 return AVERROR(ENOMEM);
84 }
85
86 // 15 + 16 tells zlib to detect GZIP or zlib automatically
87 ret = inflateInit2(&strm, 15 + 16);
88 if (ret != Z_OK) {
89 av_log(ctx, AV_LOG_ERROR, "Error during zlib initialization: %s\n", strm.msg);
90 av_free(buf);
91 return AVERROR(ENOSYS);
92 }
93
94 strm.avail_in = in_len;
95 strm.next_in = in;
96 strm.avail_out = chunk;
97 strm.next_out = buf;
98
99 ret = inflate(&strm, Z_FINISH);
100 if (ret != Z_OK && ret != Z_STREAM_END) {
101 av_log(ctx, AV_LOG_ERROR, "Inflate failed: %d, %s\n", ret, strm.msg);
102 inflateEnd(&strm);
103 av_free(buf);
104 return (ret == Z_STREAM_END) ? Z_OK : ((ret == Z_OK) ? Z_BUF_ERROR : ret);
105 }
106
107 if (strm.avail_out == 0) {
108 // TODO: Error or loop decoding?
109 av_log(ctx, AV_LOG_WARNING, "Decompression buffer may be too small\n");
110 }
111
112 *out_len = chunk - strm.avail_out;
113 buf[*out_len] = 0; // Ensure null termination
114
115 inflateEnd(&strm);
116 *out = (char *)buf;
117 return Z_OK;
118 }
119 #endif
120
121 static ResourceManagerContext *get_resman_context(void)
122 {
123 ResourceManagerContext *res = resman_ctx;
124
125 ff_mutex_lock(&mutex);
126
127 if (res)
128 goto end;
129
130 res = av_mallocz(sizeof(ResourceManagerContext));
131 if (!res) {
132 av_log(NULL, AV_LOG_ERROR, "Failed to allocate resource manager context\n");
133 goto end;
134 }
135
136 res->class = &resman_class;
137 resman_ctx = res;
138
139 end:
140 ff_mutex_unlock(&mutex);
141 return res;
142 }
143
144
145 void ff_resman_uninit(void)
146 {
147 ff_mutex_lock(&mutex);
148
149 if (resman_ctx) {
150 if (resman_ctx->resource_dic)
151 av_dict_free(&resman_ctx->resource_dic);
152 av_freep(&resman_ctx);
153 }
154
155 ff_mutex_unlock(&mutex);
156 }
157
158
159 char *ff_resman_get_string(FFResourceId resource_id)
160 {
161 ResourceManagerContext *ctx = get_resman_context();
162 FFResourceDefinition resource_definition = { 0 };
163 AVDictionaryEntry *dic_entry;
164 char *res = NULL;
165
166 if (!ctx)
167 return NULL;
168
169 for (unsigned i = 0; i < FF_ARRAY_ELEMS(resource_definitions); ++i) {
170 FFResourceDefinition def = resource_definitions[i];
171 if (def.resource_id == resource_id) {
172 resource_definition = def;
173 break;
174 }
175 }
176
177 if (!resource_definition.name) {
178 av_log(ctx, AV_LOG_ERROR, "Unable to find resource with ID %d\n", resource_id);
179 return NULL;
180 }
181
182 ff_mutex_lock(&mutex);
183
184 dic_entry = av_dict_get(ctx->resource_dic, resource_definition.name, NULL, 0);
185
186 if (!dic_entry) {
187 int dict_ret;
188
189 #if CONFIG_RESOURCE_COMPRESSION
190
191 char *out = NULL;
192 size_t out_len;
193
194 int ret = decompress_gzip(ctx, (uint8_t *)resource_definition.data, *resource_definition.data_len, &out, &out_len);
195
196 if (ret) {
197 av_log(NULL, AV_LOG_ERROR, "Unable to decompress the resource with ID %d\n", resource_id);
198 goto end;
199 }
200
201 dict_ret = av_dict_set(&ctx->resource_dic, resource_definition.name, out, 0);
202 if (dict_ret < 0) {
203 av_log(NULL, AV_LOG_ERROR, "Failed to store decompressed resource in dictionary: %d\n", dict_ret);
204 av_freep(&out);
205 goto end;
206 }
207
208 av_freep(&out);
209 #else
210
211 dict_ret = av_dict_set(&ctx->resource_dic, resource_definition.name, (const char *)resource_definition.data, 0);
212 if (dict_ret < 0) {
213 av_log(NULL, AV_LOG_ERROR, "Failed to store resource in dictionary: %d\n", dict_ret);
214 goto end;
215 }
216
217 #endif
218 dic_entry = av_dict_get(ctx->resource_dic, resource_definition.name, NULL, 0);
219
220 if (!dic_entry) {
221 av_log(NULL, AV_LOG_ERROR, "Failed to retrieve resource from dictionary after storing it\n");
222 goto end;
223 }
224 }
225
226 res = dic_entry->value;
227
228 end:
229 ff_mutex_unlock(&mutex);
230 return res;
231 }
232