FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/fftools/resources/resman.c
Date: 2025-06-17 11:18:48
Exec Total Coverage
Lines: 0 66 0.0%
Functions: 0 3 0.0%
Branches: 0 26 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 "libavutil/avassert.h"
36 #include "libavutil/pixdesc.h"
37 #include "libavutil/dict.h"
38 #include "libavutil/common.h"
39
40 extern const unsigned char ff_graph_html_data[];
41 extern const unsigned int ff_graph_html_len;
42
43 extern const unsigned char ff_graph_css_data[];
44 extern const unsigned ff_graph_css_len;
45
46 static const FFResourceDefinition resource_definitions[] = {
47 [FF_RESOURCE_GRAPH_CSS] = { FF_RESOURCE_GRAPH_CSS, "graph.css", &ff_graph_css_data[0], &ff_graph_css_len },
48 [FF_RESOURCE_GRAPH_HTML] = { FF_RESOURCE_GRAPH_HTML, "graph.html", &ff_graph_html_data[0], &ff_graph_html_len },
49 };
50
51
52 static const AVClass resman_class = {
53 .class_name = "ResourceManager",
54 };
55
56 typedef struct ResourceManagerContext {
57 const AVClass *class;
58 AVDictionary *resource_dic;
59 } ResourceManagerContext;
60
61 static AVMutex mutex = AV_MUTEX_INITIALIZER;
62
63 static ResourceManagerContext resman_ctx = { .class = &resman_class };
64
65
66 #if CONFIG_RESOURCE_COMPRESSION
67
68 static int decompress_gzip(ResourceManagerContext *ctx, uint8_t *in, unsigned in_len, char **out, size_t *out_len)
69 {
70 z_stream strm;
71 unsigned chunk = 65534;
72 int ret;
73 uint8_t *buf;
74
75 *out = NULL;
76 memset(&strm, 0, sizeof(strm));
77
78 // Allocate output buffer with extra byte for null termination
79 buf = (uint8_t *)av_mallocz(chunk + 1);
80 if (!buf) {
81 av_log(ctx, AV_LOG_ERROR, "Failed to allocate decompression buffer\n");
82 return AVERROR(ENOMEM);
83 }
84
85 // 15 + 16 tells zlib to detect GZIP or zlib automatically
86 ret = inflateInit2(&strm, 15 + 16);
87 if (ret != Z_OK) {
88 av_log(ctx, AV_LOG_ERROR, "Error during zlib initialization: %s\n", strm.msg);
89 av_free(buf);
90 return AVERROR(ENOSYS);
91 }
92
93 strm.avail_in = in_len;
94 strm.next_in = in;
95 strm.avail_out = chunk;
96 strm.next_out = buf;
97
98 ret = inflate(&strm, Z_FINISH);
99 if (ret != Z_OK && ret != Z_STREAM_END) {
100 av_log(ctx, AV_LOG_ERROR, "Inflate failed: %d, %s\n", ret, strm.msg);
101 inflateEnd(&strm);
102 av_free(buf);
103 return (ret == Z_STREAM_END) ? Z_OK : ((ret == Z_OK) ? Z_BUF_ERROR : ret);
104 }
105
106 if (strm.avail_out == 0) {
107 // TODO: Error or loop decoding?
108 av_log(ctx, AV_LOG_WARNING, "Decompression buffer may be too small\n");
109 }
110
111 *out_len = chunk - strm.avail_out;
112 buf[*out_len] = 0; // Ensure null termination
113
114 inflateEnd(&strm);
115 *out = (char *)buf;
116 return Z_OK;
117 }
118 #endif
119
120 void ff_resman_uninit(void)
121 {
122 ff_mutex_lock(&mutex);
123
124 av_dict_free(&resman_ctx.resource_dic);
125
126 ff_mutex_unlock(&mutex);
127 }
128
129
130 char *ff_resman_get_string(FFResourceId resource_id)
131 {
132 ResourceManagerContext *ctx = &resman_ctx;
133 FFResourceDefinition resource_definition = { 0 };
134 AVDictionaryEntry *dic_entry;
135 char *res = NULL;
136
137 for (unsigned i = 0; i < FF_ARRAY_ELEMS(resource_definitions); ++i) {
138 FFResourceDefinition def = resource_definitions[i];
139 if (def.resource_id == resource_id) {
140 resource_definition = def;
141 break;
142 }
143 }
144
145 av_assert1(resource_definition.name);
146
147 ff_mutex_lock(&mutex);
148
149 dic_entry = av_dict_get(ctx->resource_dic, resource_definition.name, NULL, 0);
150
151 if (!dic_entry) {
152 int dict_ret;
153
154 #if CONFIG_RESOURCE_COMPRESSION
155
156 char *out = NULL;
157 size_t out_len;
158
159 int ret = decompress_gzip(ctx, (uint8_t *)resource_definition.data, *resource_definition.data_len, &out, &out_len);
160
161 if (ret) {
162 av_log(ctx, AV_LOG_ERROR, "Unable to decompress the resource with ID %d\n", resource_id);
163 goto end;
164 }
165
166 dict_ret = av_dict_set(&ctx->resource_dic, resource_definition.name, out, 0);
167 if (dict_ret < 0) {
168 av_log(ctx, AV_LOG_ERROR, "Failed to store decompressed resource in dictionary: %d\n", dict_ret);
169 av_freep(&out);
170 goto end;
171 }
172
173 av_freep(&out);
174 #else
175
176 dict_ret = av_dict_set(&ctx->resource_dic, resource_definition.name, (const char *)resource_definition.data, 0);
177 if (dict_ret < 0) {
178 av_log(ctx, AV_LOG_ERROR, "Failed to store resource in dictionary: %d\n", dict_ret);
179 goto end;
180 }
181
182 #endif
183 dic_entry = av_dict_get(ctx->resource_dic, resource_definition.name, NULL, 0);
184
185 if (!dic_entry) {
186 av_log(ctx, AV_LOG_ERROR, "Failed to retrieve resource from dictionary after storing it\n");
187 goto end;
188 }
189 }
190
191 res = dic_entry->value;
192
193 end:
194 ff_mutex_unlock(&mutex);
195 return res;
196 }
197