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 "common.h"
27 : #include "gpacmessages.h"
28 : #include "lib/pid.h"
29 :
30 : #define CAPS_HANDLER_SIGNATURE(prop_nickname) \
31 : gboolean prop_nickname##_caps_handler(GPAC_PID_PROP_IMPL_ARGS)
32 :
33 : #define DEFAULT_HANDLER(prop_nickname) \
34 : gboolean prop_nickname##_caps_handler(GPAC_PID_PROP_IMPL_ARGS) \
35 : { \
36 : return FALSE; \
37 : }
38 :
39 : #define GET_MEDIA_AND_CODEC \
40 : GstStructure* structure = gst_caps_get_structure(priv->caps, 0); \
41 : const gchar* media_type_full = gst_structure_get_name(structure); \
42 : g_auto(GStrv) media_type_parts = g_strsplit(media_type_full, "/", 2); \
43 : gchar* media = media_type_parts[0]; \
44 : gchar* codec = media_type_parts[1];
45 :
46 : void
47 66 : gpac_push_capability(GpacPadPrivate* priv, u32 code, GF_PropertyValue* val)
48 : {
49 132 : for (GList* l = priv->gpac_caps; l != NULL; l = l->next) {
50 72 : GF_FilterCapability* cap = l->data;
51 72 : if (cap->code == code) {
52 : // Check if the value is the same
53 6 : if (!gf_props_equal(&cap->val, val))
54 0 : priv->caps_changed = TRUE;
55 :
56 : // Update existing capability
57 6 : cap->val = *val;
58 6 : return;
59 : }
60 : }
61 :
62 : // Add new capability
63 60 : GF_FilterCapability* new_cap = g_new0(GF_FilterCapability, 1);
64 60 : new_cap->code = code;
65 60 : new_cap->val = *val;
66 60 : new_cap->flags = GF_CAPS_OUTPUT;
67 60 : priv->gpac_caps = g_list_append(priv->gpac_caps, new_cap);
68 60 : priv->caps_changed = TRUE;
69 : }
70 :
71 : //
72 : // Default Caps handlers
73 : //
74 22 : DEFAULT_HANDLER(duration)
75 :
76 : //
77 : // Caps handlers
78 : //
79 22 : CAPS_HANDLER_SIGNATURE(stream_type)
80 : {
81 44 : GET_MEDIA_AND_CODEC
82 :
83 : // Get the stream type
84 22 : u32 stream_type = gf_stream_type_by_name(media);
85 22 : if (stream_type == GF_STREAM_UNKNOWN) {
86 0 : GST_ELEMENT_ERROR(
87 : element, LIBRARY, FAILED, (NULL), ("Unknown stream type"));
88 0 : return FALSE;
89 : }
90 :
91 : // Check the subtype
92 22 : if (!g_strcmp0(media, "video")) {
93 19 : const gchar* subtype = gst_structure_get_string(structure, "stream-format");
94 19 : if (!g_strcmp0(subtype, "avc") || !g_strcmp0(subtype, "hev1"))
95 17 : SET_PROP(GF_PROP_PID_ISOM_SUBTYPE, PROP_STRING(subtype));
96 : }
97 :
98 : // Push the caps with the codecid property
99 22 : gpac_push_capability(priv, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(stream_type));
100 :
101 : // Set the stream type
102 22 : SET_PROP(GF_PROP_PID_STREAM_TYPE, PROP_UINT(stream_type));
103 22 : return TRUE;
104 : }
105 :
106 22 : CAPS_HANDLER_SIGNATURE(codec_id)
107 : {
108 44 : GET_MEDIA_AND_CODEC
109 22 : GF_CodecID codec_id = GF_CODECID_NONE;
110 :
111 : // In most cases the substring after "x-" can be used to query the codec id
112 22 : if (g_str_has_prefix(codec, "x-")) {
113 19 : size_t offset = 2;
114 : // Subtitle doesn't have "raw" codec
115 19 : if (!g_strcmp0(media, "text") && !g_strcmp0(codec, "x-raw")) {
116 0 : codec_id = GF_CODECID_SIMPLE_TEXT;
117 0 : goto finish;
118 : }
119 :
120 19 : codec_id = gf_codecid_parse(codec + offset);
121 19 : if (codec_id != GF_CODECID_NONE)
122 19 : goto finish;
123 : }
124 :
125 : // See caps.h for the supported formats
126 : // For the other cases, we will match the codec id manually
127 3 : if (!g_strcmp0(media, "audio")) {
128 3 : if (!g_strcmp0(codec, "mpeg"))
129 3 : codec_id = GF_CODECID_AAC_MPEG4;
130 : }
131 :
132 : // If we still couldn't determine the codec id, log a warning
133 3 : if (codec_id == GF_CODECID_NONE) {
134 0 : GST_ELEMENT_ERROR(element,
135 : STREAM,
136 : FAILED,
137 : (NULL),
138 : ("Could not determine codec id for %s/%s", media, codec));
139 0 : return FALSE;
140 : }
141 :
142 3 : finish:
143 : // Push the caps with the codecid property
144 22 : gpac_push_capability(priv, GF_PROP_PID_CODECID, &PROP_UINT(codec_id));
145 :
146 : // Set the codec id
147 22 : SET_PROP(GF_PROP_PID_CODECID, PROP_UINT(codec_id));
148 22 : return TRUE;
149 : }
150 :
151 22 : CAPS_HANDLER_SIGNATURE(unframed)
152 : {
153 44 : GET_MEDIA_AND_CODEC
154 :
155 : const gchar* stream_format =
156 22 : gst_structure_get_string(structure, "stream-format");
157 :
158 : // Check if the stream is framed
159 22 : gboolean framed = TRUE;
160 22 : if (!g_strcmp0(media, "video")) {
161 19 : if (stream_format) {
162 37 : if (!g_strcmp0(stream_format, "byte-stream") ||
163 18 : !g_strcmp0(stream_format, "obu-stream"))
164 2 : framed = FALSE;
165 : }
166 3 : } else if (!g_strcmp0(media, "audio")) {
167 3 : gst_structure_get_boolean(structure, "framed", &framed);
168 0 : } else if (!g_strcmp0(media, "text")) {
169 : // For text media we assume unframed data
170 0 : framed = FALSE;
171 : }
172 :
173 : //* For AVC, HEVC, AV1, etc. our caps always ask for streams with start codes.
174 : //* So we'll always have unframed data. But for maximum compatibility, we may
175 : //* allow framed data and explicitly load "unframer" in gpac.
176 :
177 : // Push the caps with the unframed property
178 22 : gpac_push_capability(priv, GF_PROP_PID_UNFRAMED, &PROP_BOOL(!framed));
179 :
180 : // Set the unframed property
181 22 : SET_PROP(GF_PROP_PID_UNFRAMED, PROP_BOOL(!framed));
182 22 : return TRUE;
183 : }
184 :
185 22 : CAPS_HANDLER_SIGNATURE(width)
186 : {
187 44 : GET_MEDIA_AND_CODEC
188 :
189 : // Only process video media
190 22 : if (g_strcmp0(media, "video"))
191 3 : return TRUE;
192 :
193 19 : gint width = -1;
194 19 : gst_structure_get_int(structure, "width", &width);
195 19 : if (width <= 0) {
196 0 : GST_ELEMENT_ERROR(element, LIBRARY, FAILED, (NULL), ("Invalid width"));
197 0 : return FALSE;
198 : }
199 :
200 : // Set the width property
201 19 : SET_PROP(GF_PROP_PID_WIDTH, PROP_UINT(width));
202 19 : return TRUE;
203 : }
204 :
205 22 : CAPS_HANDLER_SIGNATURE(height)
206 : {
207 44 : GET_MEDIA_AND_CODEC
208 :
209 : // Only process video media
210 22 : if (g_strcmp0(media, "video"))
211 3 : return TRUE;
212 :
213 19 : gint height = -1;
214 19 : gst_structure_get_int(structure, "height", &height);
215 19 : if (height <= 0) {
216 0 : GST_ELEMENT_ERROR(element, LIBRARY, FAILED, (NULL), ("Invalid height"));
217 0 : return FALSE;
218 : }
219 :
220 : // Set the height property
221 19 : SET_PROP(GF_PROP_PID_HEIGHT, PROP_UINT(height));
222 19 : return TRUE;
223 : }
224 :
225 22 : CAPS_HANDLER_SIGNATURE(sample_rate)
226 : {
227 44 : GET_MEDIA_AND_CODEC
228 :
229 : // Only process audio media
230 22 : if (g_strcmp0(media, "audio"))
231 19 : return TRUE;
232 :
233 3 : gint rate = -1;
234 3 : gst_structure_get_int(structure, "rate", &rate);
235 3 : if (rate <= 0) {
236 0 : GST_ELEMENT_ERROR(
237 : element, LIBRARY, FAILED, (NULL), ("Invalid sample rate"));
238 0 : return FALSE;
239 : }
240 :
241 : // Set the sample rate property
242 3 : SET_PROP(GF_PROP_PID_SAMPLE_RATE, PROP_UINT(rate));
243 3 : return TRUE;
244 : }
245 :
246 22 : CAPS_HANDLER_SIGNATURE(fps)
247 : {
248 44 : GET_MEDIA_AND_CODEC
249 :
250 : // Only process video media
251 22 : if (g_strcmp0(media, "video"))
252 3 : return TRUE;
253 :
254 19 : gint num = -1, denom = -1;
255 19 : gst_structure_get_fraction(structure, "framerate", &num, &denom);
256 19 : if (num < 0 || denom < 0)
257 0 : return FALSE;
258 :
259 : // Set the framerate property
260 19 : SET_PROP(GF_PROP_PID_FPS, PROP_FRAC_INT(num, denom));
261 19 : return TRUE;
262 : }
263 :
264 22 : CAPS_HANDLER_SIGNATURE(timescale)
265 : {
266 44 : GET_MEDIA_AND_CODEC
267 :
268 22 : guint64 timescale = 0;
269 22 : if (!g_strcmp0(media, "video")) {
270 : const GF_PropertyValue* p =
271 19 : gf_filter_pid_get_property(pid, GF_PROP_PID_FPS);
272 19 : if (p)
273 19 : timescale = p->value.frac.num;
274 3 : } else if (!g_strcmp0(media, "audio")) {
275 : const GF_PropertyValue* p =
276 3 : gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLE_RATE);
277 3 : if (p)
278 3 : timescale = p->value.uint;
279 0 : } else if (!g_strcmp0(media, "text")) {
280 : // For text media we can use a default timescale
281 0 : timescale = 1000;
282 : } else {
283 0 : GST_ELEMENT_ERROR(element,
284 : LIBRARY,
285 : FAILED,
286 : ("Unsupported media type (%s) for timescale", media),
287 : (NULL));
288 0 : return FALSE;
289 : }
290 :
291 22 : if (timescale == 0)
292 0 : return FALSE;
293 :
294 : // Set the timescale property
295 22 : SET_PROP(GF_PROP_PID_TIMESCALE, PROP_UINT(timescale));
296 22 : return TRUE;
297 : }
298 :
299 22 : CAPS_HANDLER_SIGNATURE(num_channels)
300 : {
301 44 : GET_MEDIA_AND_CODEC
302 :
303 : // Only process audio media
304 22 : if (g_strcmp0(media, "audio"))
305 19 : return TRUE;
306 :
307 3 : gint channels = -1;
308 3 : gst_structure_get_int(structure, "channels", &channels);
309 :
310 : // Set the number of channels property
311 3 : SET_PROP(GF_PROP_PID_NUM_CHANNELS, PROP_UINT(channels));
312 3 : return TRUE;
313 : }
314 :
315 22 : CAPS_HANDLER_SIGNATURE(decoder_config)
316 : {
317 22 : SKIP_IF_SET(GF_PROP_PID_DECODER_CONFIG);
318 :
319 21 : GstStructure* structure = gst_caps_get_structure(priv->caps, 0);
320 :
321 : // Get the codec data
322 21 : const GValue* codec_data = gst_structure_get_value(structure, "codec_data");
323 21 : if (!GST_VALUE_HOLDS_BUFFER(codec_data))
324 2 : return TRUE;
325 :
326 19 : GstBuffer* buffer = gst_value_get_buffer(codec_data);
327 19 : g_auto(GstBufferMapInfo) map = GST_MAP_INFO_INIT;
328 19 : if (!gst_buffer_map(buffer, &map, GST_MAP_READ)) {
329 0 : GST_ELEMENT_ERROR(
330 : element, STREAM, FAILED, (NULL), ("Failed to map codec_data buffer"));
331 0 : return FALSE;
332 : }
333 :
334 : // Copy the data
335 19 : u8* data = (u8*)g_malloc0(map.size);
336 19 : memcpy((void*)data, map.data, map.size);
337 :
338 : // Set the decoder config property
339 19 : SET_PROP(GF_PROP_PID_DECODER_CONFIG, PROP_CONST_DATA(data, map.size));
340 19 : return TRUE;
341 : }
|