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