Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Deniz Ugur, Romain Bouqueau, Sohaib Larbi
5 : * Copyright (c) Motion Spell
6 : * All rights reserved
7 : *
8 : * This file is part of the GPAC/GStreamer wrapper
9 : *
10 : * This GPAC/GStreamer wrapper is free software; you can redistribute it
11 : * and/or modify it under the terms of the GNU Affero General Public License
12 : * as published by the Free Software Foundation; either version 3, or (at
13 : * your option) any later version.
14 : *
15 : * This GPAC/GStreamer wrapper is distributed in the hope that it will be
16 : * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : * GNU Affero General Public License for more details.
19 : *
20 : * You should have received a copy of the GNU Affero General Public
21 : * License along with this library; see the file LICENSE. If not, write to
22 : * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 : *
24 : */
25 :
26 : #include "lib/session.h"
27 : #include "lib/memio.h"
28 : #include <gpac/list.h>
29 :
30 : #define SEP_LINK 5
31 : #define SEP_FRAG 2
32 :
33 : GF_Err
34 0 : process_link_directive(char* link,
35 : GF_Filter* filter,
36 : GF_List* loaded_filters,
37 : char* ext_link)
38 : {
39 0 : char* link_prev_filter_ext = NULL;
40 : GF_Filter* link_from;
41 0 : Bool reverse_order = GF_FALSE;
42 0 : s32 link_filter_idx = -1;
43 :
44 0 : if (!filter) {
45 0 : u32 idx = 0, count = gf_list_count(loaded_filters);
46 0 : if (!ext_link || !count)
47 0 : return GF_BAD_PARAM;
48 0 : ext_link[0] = 0;
49 0 : if (link[1] == GF_FS_DEFAULT_SEPS[SEP_LINK])
50 0 : idx = (u32)strtoul(link + 2, NULL, 10);
51 : else {
52 0 : idx = (u32)strtoul(link + 1, NULL, 10);
53 0 : if (count - 1 < idx)
54 0 : return GF_BAD_PARAM;
55 0 : idx = count - 1 - idx;
56 : }
57 0 : ext_link[0] = GF_FS_DEFAULT_SEPS[SEP_LINK];
58 0 : filter = gf_list_get(loaded_filters, idx);
59 0 : link = ext_link;
60 : }
61 :
62 0 : char* ext = strchr(link, GF_FS_DEFAULT_SEPS[SEP_FRAG]);
63 0 : if (ext) {
64 0 : ext[0] = 0;
65 0 : link_prev_filter_ext = ext + 1;
66 : }
67 0 : if (strlen(link) > 1) {
68 0 : if (link[1] == GF_FS_DEFAULT_SEPS[SEP_LINK]) {
69 0 : reverse_order = GF_TRUE;
70 0 : link++;
71 : }
72 0 : link_filter_idx = 0;
73 0 : if (strlen(link) > 1) {
74 0 : link_filter_idx = (s32)strtol(link + 1, NULL, 10);
75 0 : if (link_filter_idx < 0)
76 0 : return GF_BAD_PARAM;
77 : }
78 : } else
79 0 : link_filter_idx = 0;
80 0 : if (ext)
81 0 : ext[0] = GF_FS_DEFAULT_SEPS[SEP_FRAG];
82 :
83 0 : if (reverse_order)
84 0 : link_from = gf_list_get(loaded_filters, (u32)link_filter_idx);
85 : else
86 0 : link_from = gf_list_get(
87 0 : loaded_filters, gf_list_count(loaded_filters) - 1 - (u32)link_filter_idx);
88 :
89 0 : if (!link_from)
90 0 : return GF_BAD_PARAM;
91 0 : gf_filter_set_source(filter, link_from, link_prev_filter_ext);
92 0 : return GF_OK;
93 : }
94 :
95 : gboolean
96 16 : gpac_session_init(GPAC_SessionContext* ctx,
97 : GstElement* element,
98 : GstGpacParams* params)
99 : {
100 16 : ctx->session = gf_fs_new_defaults(GF_FS_FLAG_NON_BLOCKING);
101 16 : ctx->element = element;
102 16 : ctx->params = params;
103 16 : return ctx->session != NULL;
104 : }
105 :
106 : gboolean
107 16 : gpac_session_close(GPAC_SessionContext* ctx, gboolean print_stats)
108 : {
109 16 : if (ctx->session) {
110 16 : if (ctx->had_data_flow) {
111 : // Run the filter chain until the end
112 14 : gpac_session_run(ctx, TRUE);
113 : }
114 :
115 : // Stop the session
116 16 : gf_fs_stop(ctx->session);
117 :
118 : // Print session stats
119 16 : if (print_stats) {
120 0 : gf_log_set_tools_levels("app@info", 1);
121 0 : gf_fs_print_connections(ctx->session);
122 0 : gf_fs_print_stats(ctx->session);
123 0 : gf_log_set_tools_levels("app@warning", 1);
124 : }
125 :
126 16 : gpac_memio_free(ctx);
127 :
128 : // Reset the session context
129 16 : gf_fs_del(ctx->session);
130 16 : ctx->session = NULL;
131 16 : ctx->memin = NULL;
132 : }
133 16 : return TRUE;
134 : }
135 :
136 : void
137 16 : gpac_session_abort(GPAC_SessionContext* ctx)
138 : {
139 16 : if (ctx->session)
140 16 : gf_fs_abort(ctx->session, GF_FS_FLUSH_FAST);
141 16 : }
142 :
143 : GF_Err
144 2525 : gpac_session_run(GPAC_SessionContext* ctx, gboolean flush)
145 : {
146 2525 : if (!ctx->session)
147 0 : return GF_BAD_PARAM;
148 2525 : gf_filter_post_process_task(ctx->memin);
149 :
150 2525 : GF_Err e = GF_OK;
151 2525 : guint32 steps = 100;
152 : do {
153 252553 : e = gf_fs_run(ctx->session);
154 252553 : } while (!gf_fs_is_last_task(ctx->session) &&
155 252298 : (flush || (e == GF_OK && steps--)));
156 :
157 : // Check errors
158 2525 : e = gf_fs_get_last_connect_error(ctx->session);
159 2525 : if (e != GF_OK) {
160 0 : GST_ELEMENT_ERROR(ctx->element,
161 : LIBRARY,
162 : FAILED,
163 : ("Failed to connect filters: %s", gf_error_to_string(e)),
164 : (NULL));
165 0 : return e;
166 : }
167 2525 : e = gf_fs_get_last_process_error(ctx->session);
168 2525 : if (e != GF_OK) {
169 0 : GST_ELEMENT_ERROR(ctx->element,
170 : LIBRARY,
171 : FAILED,
172 : ("Failed to process filters: %s", gf_error_to_string(e)),
173 : (NULL));
174 0 : return e;
175 : }
176 :
177 : // Mark that we had data flow
178 2525 : if (!flush)
179 2498 : ctx->had_data_flow = TRUE;
180 :
181 2525 : return GF_OK;
182 : }
183 :
184 : GF_Err
185 16 : gpac_session_open(GPAC_SessionContext* ctx, gchar* graph)
186 : {
187 : // Initialize the variables
188 16 : GF_Err e = GF_OK;
189 :
190 : // Check if the session is initialized
191 16 : if (G_UNLIKELY(!ctx->session)) {
192 0 : GST_ELEMENT_ERROR(
193 : ctx->element,
194 : STREAM,
195 : FAILED,
196 : (NULL),
197 : ("Failed to open gpac filter session, session not initialized"));
198 0 : return GF_BAD_PARAM;
199 : }
200 :
201 16 : if (!graph)
202 0 : return GF_OK;
203 :
204 : // Load the graph
205 16 : GF_List* links_directives = gf_list_new();
206 16 : GF_List* loaded_filters = gf_list_new();
207 16 : gchar** nodes = g_strsplit(graph, " ", -1);
208 :
209 : // We must have a memory input filter, add it so that it can be referenced
210 16 : if (!ctx->memin) {
211 0 : GST_ELEMENT_ERROR(ctx->element,
212 : LIBRARY,
213 : FAILED,
214 : (NULL),
215 : ("Memory input filter is missing"));
216 0 : return GF_BAD_PARAM;
217 : }
218 16 : gf_list_add(loaded_filters, ctx->memin);
219 :
220 : // Loop through the nodes
221 32 : for (guint i = 0; nodes[i]; i++) {
222 16 : GF_Filter* filter = NULL;
223 16 : gboolean f_loaded = FALSE;
224 16 : gchar* node = nodes[i];
225 :
226 : // Check if this is an input or output node
227 16 : if (!strcmp(node, "-i")) {
228 0 : filter = gf_fs_load_source(ctx->session, nodes[++i], NULL, NULL, &e);
229 0 : f_loaded = TRUE;
230 16 : } else if (!strcmp(node, "-o")) {
231 3 : filter = gf_fs_load_destination(ctx->session, nodes[++i], NULL, NULL, &e);
232 3 : f_loaded = TRUE;
233 : } else {
234 13 : if (node[0] == '-') {
235 0 : GST_ELEMENT_WARNING(
236 : ctx->element,
237 : STREAM,
238 : FAILED,
239 : (NULL),
240 : ("Cannot parse global option within graph: %s", node));
241 : }
242 : }
243 :
244 16 : if (!f_loaded) {
245 13 : if (node[0] == GF_FS_DEFAULT_SEPS[SEP_LINK]) {
246 0 : char* next_sep = NULL;
247 0 : if (node[1] == GF_FS_DEFAULT_SEPS[SEP_LINK])
248 0 : next_sep = strchr(node + 2, GF_FS_DEFAULT_SEPS[SEP_LINK]);
249 : else
250 0 : next_sep = strchr(node + 1, GF_FS_DEFAULT_SEPS[SEP_LINK]);
251 0 : if (next_sep) {
252 0 : if (process_link_directive(node, NULL, loaded_filters, next_sep)) {
253 0 : GST_ERROR("Failed to process link directive: %s", node);
254 0 : e = GF_BAD_PARAM;
255 0 : goto finish;
256 : }
257 0 : continue;
258 : }
259 0 : gf_list_add(links_directives, node);
260 0 : continue;
261 : }
262 :
263 13 : if (ctx->destination) {
264 1 : if (strstr(node, "dasher")) {
265 : // We need to append "mname" option to the filter name
266 1 : const gchar* dst = ctx->destination;
267 1 : const gchar* after_slash = strrchr(ctx->destination, '/');
268 1 : dst = after_slash ? after_slash + 1 : ctx->destination;
269 1 : gchar* new_node = g_strdup_printf("%s:gpac:mname=%s", node, dst);
270 1 : node = new_node;
271 : }
272 : }
273 :
274 13 : filter = gf_fs_load_filter(ctx->session, node, &e);
275 : }
276 :
277 16 : if (G_UNLIKELY(!filter)) {
278 0 : GST_ERROR(
279 : "Failed to load filter \"%s\": %s", node, gf_error_to_string(e));
280 0 : e = GF_BAD_PARAM;
281 0 : goto finish;
282 : }
283 :
284 16 : while (gf_list_count(links_directives)) {
285 0 : char* link = gf_list_pop_front(links_directives);
286 0 : if (process_link_directive(link, filter, loaded_filters, NULL)) {
287 0 : GST_ERROR("Failed to process link directive: %s", link);
288 0 : e = GF_BAD_PARAM;
289 0 : goto finish;
290 : }
291 : }
292 16 : gf_list_add(loaded_filters, filter);
293 : }
294 :
295 16 : finish:
296 16 : gf_list_del(links_directives);
297 16 : gf_list_del(loaded_filters);
298 16 : g_strfreev(nodes);
299 :
300 16 : return e;
301 : }
302 :
303 : GF_Filter*
304 0 : gpac_session_load_filter(GPAC_SessionContext* ctx, const gchar* filter_name)
305 : {
306 0 : GF_Err e = GF_OK;
307 0 : GF_Filter* filter = gf_fs_load_filter(ctx->session, filter_name, &e);
308 0 : if (!filter) {
309 0 : GST_ELEMENT_ERROR(
310 : ctx->element,
311 : STREAM,
312 : FAILED,
313 : ("Failed to load filter \"%s\": %s", filter_name, gf_error_to_string(e)),
314 : (NULL));
315 : }
316 0 : return filter;
317 : }
318 :
319 : gboolean
320 16 : gpac_session_has_output(GPAC_SessionContext* ctx)
321 : {
322 16 : u32 count = gf_fs_get_filters_count(ctx->session);
323 16 : for (u32 i = 0; i < count; i++) {
324 16 : GF_Filter* filter = gf_fs_get_filter(ctx->session, i);
325 16 : if (gf_filter_is_sink(filter))
326 16 : return TRUE;
327 : }
328 0 : return FALSE;
329 : }
|