1 /* GStreamer
2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 * Copyright (C) 2000,2005 Wim Taymans <wim@fluendo.com>
4 * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
5 *
6 * gsttypefindhelper.c:
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 /**
25 * SECTION:gsttypefindhelper
26 * @title: GstTypeFindHelper
27 * @short_description: Utility functions for typefinding
28 *
29 * Utility functions for elements doing typefinding:
30 * gst_type_find_helper() does typefinding in pull mode, while
31 * gst_type_find_helper_for_buffer() is useful for elements needing to do
32 * typefinding in push mode from a chain function.
33 */
34
35 #ifdef HAVE_CONFIG_H
36 # include "config.h"
37 #endif
38
39 #include <stdlib.h>
40 #include <string.h>
41
42 #include "gsttypefindhelper.h"
43
44 /* ********************** typefinding in pull mode ************************ */
45
46 static void
47 helper_find_suggest (gpointer data, guint probability, GstCaps * caps);
48
49 typedef struct
50 {
51 GstBuffer *buffer;
52 GstMapInfo map;
53 } GstMappedBuffer;
54
55 typedef struct
56 {
57 GSList *buffers; /* buffer cache */
58 guint64 size;
59 guint64 last_offset;
60 GstTypeFindHelperGetRangeFunction func;
61 GstTypeFindProbability best_probability;
62 GstCaps *caps;
63 GstTypeFindFactory *factory; /* for logging */
64 GstObject *obj; /* for logging */
65 GstObject *parent;
66 GstFlowReturn flow_ret;
67 } GstTypeFindHelper;
68
69 /*
70 * helper_find_peek:
71 * @data: helper data struct
72 * @off: stream offset
73 * @size: block size
74 *
75 * Get data pointer within a stream. Keeps a cache of read buffers (partly
76 * for performance reasons, but mostly because pointers returned by us need
77 * to stay valid until typefinding has finished)
78 *
79 * Returns: (nullable): address of the data or %NULL if buffer does not cover
80 * the requested range.
81 */
82 static const guint8 *
helper_find_peek(gpointer data,gint64 offset,guint size)83 helper_find_peek (gpointer data, gint64 offset, guint size)
84 {
85 GstTypeFindHelper *helper;
86 GstBuffer *buffer;
87 GSList *insert_pos = NULL;
88 gsize buf_size;
89 guint64 buf_offset;
90 GstMappedBuffer *bmap;
91 #if 0
92 GstCaps *caps;
93 #endif
94
95 helper = (GstTypeFindHelper *) data;
96
97 GST_LOG_OBJECT (helper->obj, "'%s' called peek (%" G_GINT64_FORMAT
98 ", %u)", GST_OBJECT_NAME (helper->factory), offset, size);
99
100 if (size == 0)
101 return NULL;
102
103 if (offset < 0) {
104 if (helper->size == -1 || helper->size < -offset)
105 return NULL;
106
107 offset += helper->size;
108 }
109
110 /* see if we have a matching buffer already in our list */
111 if (size > 0 && offset <= helper->last_offset) {
112 GSList *walk;
113
114 for (walk = helper->buffers; walk; walk = walk->next) {
115 GstMappedBuffer *bmp = (GstMappedBuffer *) walk->data;
116 GstBuffer *buf = GST_BUFFER_CAST (bmp->buffer);
117
118 buf_offset = GST_BUFFER_OFFSET (buf);
119 buf_size = bmp->map.size;
120
121 /* buffers are kept sorted by end offset (highest first) in the list, so
122 * at this point we save the current position and stop searching if
123 * we're after the searched end offset */
124 if (buf_offset <= offset) {
125 if ((offset + size) < (buf_offset + buf_size)) {
126 /* must already have been mapped before */
127 return (guint8 *) bmp->map.data + (offset - buf_offset);
128 }
129 } else if (offset + size >= buf_offset + buf_size) {
130 insert_pos = walk;
131 break;
132 }
133 }
134 }
135
136 buffer = NULL;
137 /* some typefinders go in 1 byte steps over 1k of data and request
138 * small buffers. It is really inefficient to pull each time, and pulling
139 * a larger chunk is almost free. Trying to pull a larger chunk at the end
140 * of the file is also not a problem here, we'll just get a truncated buffer
141 * in that case (and we'll have to double-check the size we actually get
142 * anyway, see below) */
143 helper->flow_ret =
144 helper->func (helper->obj, helper->parent, offset, MAX (size, 4096),
145 &buffer);
146
147 if (helper->flow_ret != GST_FLOW_OK)
148 goto error;
149
150 #if 0
151 caps = GST_BUFFER_CAPS (buffer);
152
153 if (caps && !gst_caps_is_empty (caps) && !gst_caps_is_any (caps)) {
154 GST_DEBUG ("buffer has caps %" GST_PTR_FORMAT ", suggest max probability",
155 caps);
156
157 gst_caps_replace (&helper->caps, caps);
158 helper->best_probability = GST_TYPE_FIND_MAXIMUM;
159
160 gst_buffer_unref (buffer);
161 return NULL;
162 }
163 #endif
164
165 /* getrange might silently return shortened buffers at the end of a file,
166 * we must, however, always return either the full requested data or %NULL */
167 buf_offset = GST_BUFFER_OFFSET (buffer);
168 buf_size = gst_buffer_get_size (buffer);
169
170 if (buf_size < size) {
171 GST_DEBUG ("dropping short buffer of size %" G_GSIZE_FORMAT ","
172 "requested size was %u", buf_size, size);
173 gst_buffer_unref (buffer);
174 return NULL;
175 }
176
177 if (buf_offset != -1 && buf_offset != offset) {
178 GST_DEBUG ("dropping buffer with unexpected offset %" G_GUINT64_FORMAT ", "
179 "expected offset was %" G_GUINT64_FORMAT, buf_offset, offset);
180 gst_buffer_unref (buffer);
181 return NULL;
182 }
183
184 bmap = g_slice_new0 (GstMappedBuffer);
185
186 if (!gst_buffer_map (buffer, &bmap->map, GST_MAP_READ))
187 goto map_failed;
188
189 bmap->buffer = buffer;
190
191 if (insert_pos) {
192 helper->buffers = g_slist_insert_before (helper->buffers, insert_pos, bmap);
193 } else {
194 /* if insert_pos is not set, our offset is bigger than the largest offset
195 * we have so far; since we keep the list sorted with highest offsets
196 * first, we need to prepend the buffer to the list */
197 helper->last_offset = GST_BUFFER_OFFSET (buffer) + buf_size;
198 helper->buffers = g_slist_prepend (helper->buffers, bmap);
199 }
200
201 return bmap->map.data;
202
203 error:
204 {
205 GST_INFO ("typefind function returned: %s",
206 gst_flow_get_name (helper->flow_ret));
207 return NULL;
208 }
209 map_failed:
210 {
211 GST_ERROR ("map failed");
212 gst_buffer_unref (buffer);
213 g_slice_free (GstMappedBuffer, bmap);
214 return NULL;
215 }
216 }
217
218 /*
219 * helper_find_suggest:
220 * @data: helper data struct
221 * @probability: probability of the match
222 * @caps: caps of the type
223 *
224 * If given @probability is higher, replace previously store caps.
225 */
226 static void
helper_find_suggest(gpointer data,guint probability,GstCaps * caps)227 helper_find_suggest (gpointer data, guint probability, GstCaps * caps)
228 {
229 GstTypeFindHelper *helper = (GstTypeFindHelper *) data;
230
231 GST_LOG_OBJECT (helper->obj,
232 "'%s' called suggest (%u, %" GST_PTR_FORMAT ")",
233 GST_OBJECT_NAME (helper->factory), probability, caps);
234
235 if (probability > helper->best_probability) {
236 gst_caps_replace (&helper->caps, caps);
237 helper->best_probability = probability;
238 }
239 }
240
241 static guint64
helper_find_get_length(gpointer data)242 helper_find_get_length (gpointer data)
243 {
244 GstTypeFindHelper *helper = (GstTypeFindHelper *) data;
245
246 GST_LOG_OBJECT (helper->obj, "'%s' called get_length, returning %"
247 G_GUINT64_FORMAT, GST_OBJECT_NAME (helper->factory), helper->size);
248
249 return helper->size;
250 }
251
252 static GList *
prioritize_extension(GstObject * obj,GList * type_list,const gchar * extension)253 prioritize_extension (GstObject * obj, GList * type_list,
254 const gchar * extension)
255 {
256 gint pos = 0;
257 GList *next, *l;
258
259 if (!extension)
260 return type_list;
261
262 /* move the typefinders for the extension first in the list. The idea is that
263 * when one of them returns MAX we don't need to search further as there is a
264 * very high chance we got the right type. */
265
266 GST_LOG_OBJECT (obj, "sorting typefind for extension %s to head", extension);
267
268 for (l = type_list; l; l = next) {
269 const gchar *const *ext;
270 GstTypeFindFactory *factory;
271
272 next = l->next;
273
274 factory = GST_TYPE_FIND_FACTORY (l->data);
275
276 ext = gst_type_find_factory_get_extensions (factory);
277 if (ext == NULL)
278 continue;
279
280 GST_LOG_OBJECT (obj, "testing factory %s for extension %s",
281 GST_OBJECT_NAME (factory), extension);
282
283 while (*ext != NULL) {
284 if (strcmp (*ext, extension) == 0) {
285 /* found extension, move in front */
286 GST_LOG_OBJECT (obj, "moving typefind for extension %s to head",
287 extension);
288 /* remove entry from list */
289 type_list = g_list_delete_link (type_list, l);
290 /* insert at the position */
291 type_list = g_list_insert (type_list, factory, pos);
292 /* next element will be inserted after this one */
293 pos++;
294 break;
295 }
296 ++ext;
297 }
298 }
299
300 return type_list;
301 }
302
303 /**
304 * gst_type_find_helper_get_range:
305 * @obj: A #GstObject that will be passed as first argument to @func
306 * @parent: (allow-none): the parent of @obj or %NULL
307 * @func: (scope call): A generic #GstTypeFindHelperGetRangeFunction that will
308 * be used to access data at random offsets when doing the typefinding
309 * @size: The length in bytes
310 * @extension: (allow-none): extension of the media, or %NULL
311 * @prob: (out) (allow-none): location to store the probability of the found
312 * caps, or %NULL
313 *
314 * Utility function to do pull-based typefinding. Unlike gst_type_find_helper()
315 * however, this function will use the specified function @func to obtain the
316 * data needed by the typefind functions, rather than operating on a given
317 * source pad. This is useful mostly for elements like tag demuxers which
318 * strip off data at the beginning and/or end of a file and want to typefind
319 * the stripped data stream before adding their own source pad (the specified
320 * callback can then call the upstream peer pad with offsets adjusted for the
321 * tag size, for example).
322 *
323 * When @extension is not %NULL, this function will first try the typefind
324 * functions for the given extension, which might speed up the typefinding
325 * in many cases.
326 *
327 * Free-function: gst_caps_unref
328 *
329 * Returns: (transfer full) (nullable): the #GstCaps corresponding to the data
330 * stream. Returns %NULL if no #GstCaps matches the data stream.
331 */
332 GstCaps *
gst_type_find_helper_get_range(GstObject * obj,GstObject * parent,GstTypeFindHelperGetRangeFunction func,guint64 size,const gchar * extension,GstTypeFindProbability * prob)333 gst_type_find_helper_get_range (GstObject * obj, GstObject * parent,
334 GstTypeFindHelperGetRangeFunction func, guint64 size,
335 const gchar * extension, GstTypeFindProbability * prob)
336 {
337 GstCaps *caps = NULL;
338
339 gst_type_find_helper_get_range_full (obj, parent, func, size, extension,
340 &caps, prob);
341
342 return caps;
343 }
344
345 /**
346 * gst_type_find_helper_get_range_full:
347 * @obj: A #GstObject that will be passed as first argument to @func
348 * @parent: (allow-none): the parent of @obj or %NULL
349 * @func: (scope call): A generic #GstTypeFindHelperGetRangeFunction that will
350 * be used to access data at random offsets when doing the typefinding
351 * @size: The length in bytes
352 * @extension: (allow-none): extension of the media, or %NULL
353 * @caps: (out) (transfer full): returned caps
354 * @prob: (out) (allow-none): location to store the probability of the found
355 * caps, or %NULL
356 *
357 * Utility function to do pull-based typefinding. Unlike gst_type_find_helper()
358 * however, this function will use the specified function @func to obtain the
359 * data needed by the typefind functions, rather than operating on a given
360 * source pad. This is useful mostly for elements like tag demuxers which
361 * strip off data at the beginning and/or end of a file and want to typefind
362 * the stripped data stream before adding their own source pad (the specified
363 * callback can then call the upstream peer pad with offsets adjusted for the
364 * tag size, for example).
365 *
366 * When @extension is not %NULL, this function will first try the typefind
367 * functions for the given extension, which might speed up the typefinding
368 * in many cases.
369 *
370 * Returns: the last %GstFlowReturn from pulling a buffer or %GST_FLOW_OK if
371 * typefinding was successful.
372 *
373 * Since: 1.14.3
374 */
375 GstFlowReturn
gst_type_find_helper_get_range_full(GstObject * obj,GstObject * parent,GstTypeFindHelperGetRangeFunction func,guint64 size,const gchar * extension,GstCaps ** caps,GstTypeFindProbability * prob)376 gst_type_find_helper_get_range_full (GstObject * obj, GstObject * parent,
377 GstTypeFindHelperGetRangeFunction func, guint64 size,
378 const gchar * extension, GstCaps ** caps, GstTypeFindProbability * prob)
379 {
380 GstTypeFindHelper helper;
381 GstTypeFind find;
382 GSList *walk;
383 GList *l, *type_list;
384 GstCaps *result = NULL;
385
386 g_return_val_if_fail (GST_IS_OBJECT (obj), GST_FLOW_ERROR);
387 g_return_val_if_fail (func != NULL, GST_FLOW_ERROR);
388 g_return_val_if_fail (caps != NULL, GST_FLOW_ERROR);
389
390 *caps = NULL;
391
392 helper.buffers = NULL;
393 helper.size = size;
394 helper.last_offset = 0;
395 helper.func = func;
396 helper.best_probability = GST_TYPE_FIND_NONE;
397 helper.caps = NULL;
398 helper.obj = obj;
399 helper.parent = parent;
400 helper.flow_ret = GST_FLOW_OK;
401
402 find.data = &helper;
403 find.peek = helper_find_peek;
404 find.suggest = helper_find_suggest;
405 #ifdef OHOS_OPT_COMPAT
406 // ohos.opt.compat.0004
407 find.need_typefind_again = FALSE;
408 #endif
409
410 if (size == 0 || size == (guint64) - 1) {
411 find.get_length = NULL;
412 } else {
413 find.get_length = helper_find_get_length;
414 }
415
416 type_list = gst_type_find_factory_get_list ();
417 type_list = prioritize_extension (obj, type_list, extension);
418
419 for (l = type_list; l; l = l->next) {
420 helper.factory = GST_TYPE_FIND_FACTORY (l->data);
421 gst_type_find_factory_call_function (helper.factory, &find);
422 if (helper.best_probability >= GST_TYPE_FIND_MAXIMUM) {
423 /* Any other flow return can be ignored here, we found
424 * something before any error with highest probability */
425 helper.flow_ret = GST_FLOW_OK;
426 break;
427 } else if (helper.flow_ret != GST_FLOW_OK
428 && helper.flow_ret != GST_FLOW_EOS) {
429 /* We had less than maximum probability and an error, don't return
430 * any caps as they might be with a lower probability than what
431 * we would've gotten when continuing if there was no error */
432 gst_caps_replace (&helper.caps, NULL);
433 break;
434 }
435 }
436 gst_plugin_feature_list_free (type_list);
437
438 for (walk = helper.buffers; walk; walk = walk->next) {
439 GstMappedBuffer *bmap = (GstMappedBuffer *) walk->data;
440
441 gst_buffer_unmap (bmap->buffer, &bmap->map);
442 gst_buffer_unref (bmap->buffer);
443 g_slice_free (GstMappedBuffer, bmap);
444 }
445 g_slist_free (helper.buffers);
446
447 if (helper.best_probability > 0)
448 result = helper.caps;
449
450 if (prob)
451 *prob = helper.best_probability;
452
453 *caps = result;
454 if (helper.flow_ret == GST_FLOW_EOS) {
455 /* Some typefinder might've tried to read too much, if we
456 * didn't get any meaningful caps because of that this is
457 * just a normal error */
458 #ifdef OHOS_OPT_COMPAT
459 // ohos.opt.compat.0005
460 helper.flow_ret = helper.best_probability > 0 ? GST_FLOW_OK : GST_FLOW_ERROR;
461 #else
462 helper.flow_ret = GST_FLOW_ERROR;
463 #endif
464 }
465
466 GST_LOG_OBJECT (obj, "Returning %" GST_PTR_FORMAT " (probability = %u)",
467 result, (guint) helper.best_probability);
468
469 return helper.flow_ret;
470 }
471
472 /**
473 * gst_type_find_helper:
474 * @src: A source #GstPad
475 * @size: The length in bytes
476 *
477 * Tries to find what type of data is flowing from the given source #GstPad.
478 *
479 * Free-function: gst_caps_unref
480 *
481 * Returns: (transfer full) (nullable): the #GstCaps corresponding to the data
482 * stream. Returns %NULL if no #GstCaps matches the data stream.
483 */
484
485 GstCaps *
gst_type_find_helper(GstPad * src,guint64 size)486 gst_type_find_helper (GstPad * src, guint64 size)
487 {
488 GstTypeFindHelperGetRangeFunction func;
489
490 g_return_val_if_fail (GST_IS_OBJECT (src), NULL);
491 g_return_val_if_fail (GST_PAD_GETRANGEFUNC (src) != NULL, NULL);
492
493 func = (GstTypeFindHelperGetRangeFunction) (GST_PAD_GETRANGEFUNC (src));
494
495 return gst_type_find_helper_get_range (GST_OBJECT (src),
496 GST_OBJECT_PARENT (src), func, size, NULL, NULL);
497 }
498
499 /* ********************** typefinding for buffers ************************* */
500
501 typedef struct
502 {
503 const guint8 *data; /* buffer data */
504 gsize size;
505 GstTypeFindProbability best_probability;
506 GstCaps *caps;
507 GstTypeFindFactory *factory; /* for logging */
508 GstObject *obj; /* for logging */
509 } GstTypeFindBufHelper;
510
511 /*
512 * buf_helper_find_peek:
513 * @data: helper data struct
514 * @off: stream offset
515 * @size: block size
516 *
517 * Get data pointer within a buffer.
518 *
519 * Returns: (nullable): address inside the buffer or %NULL if buffer does not
520 * cover the requested range.
521 */
522 static const guint8 *
buf_helper_find_peek(gpointer data,gint64 off,guint size)523 buf_helper_find_peek (gpointer data, gint64 off, guint size)
524 {
525 GstTypeFindBufHelper *helper;
526
527 helper = (GstTypeFindBufHelper *) data;
528 GST_LOG_OBJECT (helper->obj, "'%s' called peek (%" G_GINT64_FORMAT ", %u)",
529 GST_OBJECT_NAME (helper->factory), off, size);
530
531 if (size == 0)
532 return NULL;
533
534 if (off < 0) {
535 GST_LOG_OBJECT (helper->obj, "'%s' wanted to peek at end; not supported",
536 GST_OBJECT_NAME (helper->factory));
537 return NULL;
538 }
539
540 /* If we request beyond the available size, we're sure we can't return
541 * anything regardless of the requested offset */
542 if (size > helper->size)
543 return NULL;
544
545 /* Only return data if there's enough room left for the given offset.
546 * This is the same as "if (off + size <= helper->size)" except that
547 * it doesn't exceed type limits */
548 if (off <= helper->size - size)
549 return helper->data + off;
550
551 return NULL;
552 }
553
554 /*
555 * buf_helper_find_suggest:
556 * @data: helper data struct
557 * @probability: probability of the match
558 * @caps: caps of the type
559 *
560 * If given @probability is higher, replace previously store caps.
561 */
562 static void
buf_helper_find_suggest(gpointer data,guint probability,GstCaps * caps)563 buf_helper_find_suggest (gpointer data, guint probability, GstCaps * caps)
564 {
565 GstTypeFindBufHelper *helper = (GstTypeFindBufHelper *) data;
566
567 GST_LOG_OBJECT (helper->obj,
568 "'%s' called suggest (%u, %" GST_PTR_FORMAT ")",
569 GST_OBJECT_NAME (helper->factory), probability, caps);
570
571 /* Note: not >= as we call typefinders in order of rank, highest first */
572 if (probability > helper->best_probability) {
573 gst_caps_replace (&helper->caps, caps);
574 helper->best_probability = probability;
575 }
576 }
577
578 /**
579 * gst_type_find_helper_for_data:
580 * @obj: (allow-none): object doing the typefinding, or %NULL (used for logging)
581 * @data: (transfer none) (array length=size): * a pointer with data to typefind
582 * @size: the size of @data
583 * @prob: (out) (allow-none): location to store the probability of the found
584 * caps, or %NULL
585 *
586 * Tries to find what type of data is contained in the given @data, the
587 * assumption being that the data represents the beginning of the stream or
588 * file.
589 *
590 * All available typefinders will be called on the data in order of rank. If
591 * a typefinding function returns a probability of %GST_TYPE_FIND_MAXIMUM,
592 * typefinding is stopped immediately and the found caps will be returned
593 * right away. Otherwise, all available typefind functions will the tried,
594 * and the caps with the highest probability will be returned, or %NULL if
595 * the content of @data could not be identified.
596 *
597 * Free-function: gst_caps_unref
598 *
599 * Returns: (transfer full) (nullable): the #GstCaps corresponding to the data,
600 * or %NULL if no type could be found. The caller should free the caps
601 * returned with gst_caps_unref().
602 */
603 GstCaps *
gst_type_find_helper_for_data(GstObject * obj,const guint8 * data,gsize size,GstTypeFindProbability * prob)604 gst_type_find_helper_for_data (GstObject * obj, const guint8 * data, gsize size,
605 GstTypeFindProbability * prob)
606 {
607 return gst_type_find_helper_for_data_with_extension (obj, data, size, NULL,
608 prob);
609 }
610
611 #ifdef OHOS_OPT_COMPAT
612 // ohos.opt.compat.0004
613 static gboolean
gst_type_find_helper_is_support_retypefind(GstObject * obj)614 gst_type_find_helper_is_support_retypefind (GstObject * obj)
615 {
616 gboolean support = FALSE;
617 if (obj == NULL) {
618 return support;
619 }
620
621 GParamSpec *pspec = g_object_class_find_property (G_OBJECT_GET_CLASS(obj), "re-typefind-factory-list");
622 if (pspec != NULL && G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_POINTER) {
623 support = TRUE;
624 }
625 return support;
626 }
627
628 static GList *
gst_type_find_helper_get_type_list(GstObject * obj,gboolean support_retypefind)629 gst_type_find_helper_get_type_list (GstObject * obj, gboolean support_retypefind)
630 {
631 GList *type_list = NULL;
632 if (support_retypefind && obj != NULL) {
633 g_object_get (obj, "re-typefind-factory-list", &type_list, NULL);
634 if (type_list == NULL) {
635 GST_INFO_OBJECT (obj,
636 "there is no re_typefind factory list, get type_list from registered typefind factories");
637 type_list = gst_type_find_factory_get_list ();
638 }
639 } else {
640 type_list = gst_type_find_factory_get_list ();
641 }
642
643 return type_list;
644 }
645 #endif
646
647 /**
648 * gst_type_find_helper_for_data_with_extension:
649 * @obj: (allow-none): object doing the typefinding, or %NULL (used for logging)
650 * @data: (transfer none) (array length=size): * a pointer with data to typefind
651 * @size: the size of @data
652 * @extension: (allow-none): extension of the media, or %NULL
653 * @prob: (out) (allow-none): location to store the probability of the found
654 * caps, or %NULL
655 *
656 * Tries to find what type of data is contained in the given @data, the
657 * assumption being that the data represents the beginning of the stream or
658 * file.
659 *
660 * All available typefinders will be called on the data in order of rank. If
661 * a typefinding function returns a probability of %GST_TYPE_FIND_MAXIMUM,
662 * typefinding is stopped immediately and the found caps will be returned
663 * right away. Otherwise, all available typefind functions will the tried,
664 * and the caps with the highest probability will be returned, or %NULL if
665 * the content of @data could not be identified.
666 *
667 * When @extension is not %NULL, this function will first try the typefind
668 * functions for the given extension, which might speed up the typefinding
669 * in many cases.
670 *
671 * Free-function: gst_caps_unref
672 *
673 * Returns: (transfer full) (nullable): the #GstCaps corresponding to the data,
674 * or %NULL if no type could be found. The caller should free the caps
675 * returned with gst_caps_unref().
676 *
677 * Since: 1.16
678 *
679 */
680 GstCaps *
gst_type_find_helper_for_data_with_extension(GstObject * obj,const guint8 * data,gsize size,const gchar * extension,GstTypeFindProbability * prob)681 gst_type_find_helper_for_data_with_extension (GstObject * obj,
682 const guint8 * data, gsize size, const gchar * extension,
683 GstTypeFindProbability * prob)
684 {
685 GstTypeFindBufHelper helper;
686 GstTypeFind find;
687 GList *l, *type_list;
688 #ifdef OHOS_OPT_COMPAT
689 // ohos.opt.compat.0004
690 GList *re_typefind_factory_list = NULL;
691 gboolean support_retypefind = FALSE;
692 #endif
693 GstCaps *result = NULL;
694
695 g_return_val_if_fail (data != NULL, NULL);
696
697 helper.data = data;
698 helper.size = size;
699 helper.best_probability = GST_TYPE_FIND_NONE;
700 helper.caps = NULL;
701 helper.obj = obj;
702
703 if (helper.data == NULL || helper.size == 0)
704 return NULL;
705
706 find.data = &helper;
707 find.peek = buf_helper_find_peek;
708 find.suggest = buf_helper_find_suggest;
709 find.get_length = NULL;
710 #ifdef OHOS_OPT_COMPAT
711 // ohos.opt.compat.0004
712 find.need_typefind_again = FALSE;
713 #endif
714
715 #ifdef OHOS_OPT_COMPAT
716 // ohos.opt.compat.0004
717 support_retypefind = gst_type_find_helper_is_support_retypefind (obj);
718 type_list = gst_type_find_helper_get_type_list (obj, support_retypefind);
719 #else
720 type_list = gst_type_find_factory_get_list ();
721 #endif
722
723 type_list = prioritize_extension (obj, type_list, extension);
724
725 for (l = type_list; l; l = l->next) {
726 helper.factory = GST_TYPE_FIND_FACTORY (l->data);
727 gst_type_find_factory_call_function (helper.factory, &find);
728 if (helper.best_probability >= GST_TYPE_FIND_MAXIMUM)
729 break;
730
731 #ifdef OHOS_OPT_COMPAT
732 // ohos.opt.compat.0004:if current factory need typefind again, we add it to list
733 if (support_retypefind && find.need_typefind_again) {
734 GST_INFO_OBJECT (obj, "need_typefind_again, name:%s", gst_plugin_feature_get_name (helper.factory));
735 re_typefind_factory_list = g_list_append (re_typefind_factory_list, gst_object_ref (helper.factory));
736 find.need_typefind_again = FALSE;
737 }
738 #endif
739 }
740
741 #ifdef OHOS_OPT_COMPAT
742 // ohos.opt.compat.0004:set the list to the object doing the typefinding
743 if (obj != NULL && support_retypefind && helper.best_probability < GST_TYPE_FIND_MAXIMUM) {
744 g_object_set (obj, "re-typefind-factory-list", re_typefind_factory_list, NULL);
745 }
746 if (re_typefind_factory_list) {
747 g_list_free_full (re_typefind_factory_list, gst_object_unref);
748 }
749 #endif
750 gst_plugin_feature_list_free (type_list);
751
752 if (helper.best_probability > 0)
753 result = helper.caps;
754
755 if (prob)
756 *prob = helper.best_probability;
757
758 GST_LOG_OBJECT (obj, "Returning %" GST_PTR_FORMAT " (probability = %u)",
759 result, (guint) helper.best_probability);
760
761 return result;
762 }
763
764 /**
765 * gst_type_find_helper_for_buffer:
766 * @obj: (allow-none): object doing the typefinding, or %NULL (used for logging)
767 * @buf: (in) (transfer none): a #GstBuffer with data to typefind
768 * @prob: (out) (allow-none): location to store the probability of the found
769 * caps, or %NULL
770 *
771 * Tries to find what type of data is contained in the given #GstBuffer, the
772 * assumption being that the buffer represents the beginning of the stream or
773 * file.
774 *
775 * All available typefinders will be called on the data in order of rank. If
776 * a typefinding function returns a probability of %GST_TYPE_FIND_MAXIMUM,
777 * typefinding is stopped immediately and the found caps will be returned
778 * right away. Otherwise, all available typefind functions will the tried,
779 * and the caps with the highest probability will be returned, or %NULL if
780 * the content of the buffer could not be identified.
781 *
782 * Free-function: gst_caps_unref
783 *
784 * Returns: (transfer full) (nullable): the #GstCaps corresponding to the data,
785 * or %NULL if no type could be found. The caller should free the caps
786 * returned with gst_caps_unref().
787 */
788 GstCaps *
gst_type_find_helper_for_buffer(GstObject * obj,GstBuffer * buf,GstTypeFindProbability * prob)789 gst_type_find_helper_for_buffer (GstObject * obj, GstBuffer * buf,
790 GstTypeFindProbability * prob)
791 {
792 return gst_type_find_helper_for_buffer_with_extension (obj, buf, NULL, prob);
793 }
794
795 /**
796 * gst_type_find_helper_for_buffer_with_extension:
797 * @obj: (allow-none): object doing the typefinding, or %NULL (used for logging)
798 * @buf: (in) (transfer none): a #GstBuffer with data to typefind
799 * @extension: (allow-none): extension of the media, or %NULL
800 * @prob: (out) (allow-none): location to store the probability of the found
801 * caps, or %NULL
802 *
803 * Tries to find what type of data is contained in the given #GstBuffer, the
804 * assumption being that the buffer represents the beginning of the stream or
805 * file.
806 *
807 * All available typefinders will be called on the data in order of rank. If
808 * a typefinding function returns a probability of %GST_TYPE_FIND_MAXIMUM,
809 * typefinding is stopped immediately and the found caps will be returned
810 * right away. Otherwise, all available typefind functions will the tried,
811 * and the caps with the highest probability will be returned, or %NULL if
812 * the content of the buffer could not be identified.
813 *
814 * When @extension is not %NULL, this function will first try the typefind
815 * functions for the given extension, which might speed up the typefinding
816 * in many cases.
817 *
818 * Free-function: gst_caps_unref
819 *
820 * Returns: (transfer full) (nullable): the #GstCaps corresponding to the data,
821 * or %NULL if no type could be found. The caller should free the caps
822 * returned with gst_caps_unref().
823 *
824 * Since: 1.16
825 *
826 */
827 GstCaps *
gst_type_find_helper_for_buffer_with_extension(GstObject * obj,GstBuffer * buf,const gchar * extension,GstTypeFindProbability * prob)828 gst_type_find_helper_for_buffer_with_extension (GstObject * obj,
829 GstBuffer * buf, const gchar * extension, GstTypeFindProbability * prob)
830 {
831 GstCaps *result;
832 GstMapInfo info;
833
834 g_return_val_if_fail (buf != NULL, NULL);
835 g_return_val_if_fail (GST_IS_BUFFER (buf), NULL);
836 g_return_val_if_fail (GST_BUFFER_OFFSET (buf) == 0 ||
837 GST_BUFFER_OFFSET (buf) == GST_BUFFER_OFFSET_NONE, NULL);
838
839 if (!gst_buffer_map (buf, &info, GST_MAP_READ))
840 return NULL;
841 result =
842 gst_type_find_helper_for_data_with_extension (obj, info.data, info.size,
843 extension, prob);
844 gst_buffer_unmap (buf, &info);
845
846 return result;
847 }
848
849 /**
850 * gst_type_find_helper_for_extension:
851 * @obj: (allow-none): object doing the typefinding, or %NULL (used for logging)
852 * @extension: an extension
853 *
854 * Tries to find the best #GstCaps associated with @extension.
855 *
856 * All available typefinders will be checked against the extension in order
857 * of rank. The caps of the first typefinder that can handle @extension will be
858 * returned.
859 *
860 * Free-function: gst_caps_unref
861 *
862 * Returns: (transfer full) (nullable): the #GstCaps corresponding to
863 * @extension, or %NULL if no type could be found. The caller should free
864 * the caps returned with gst_caps_unref().
865 */
866 GstCaps *
gst_type_find_helper_for_extension(GstObject * obj,const gchar * extension)867 gst_type_find_helper_for_extension (GstObject * obj, const gchar * extension)
868 {
869 GList *l, *type_list;
870 GstCaps *result = NULL;
871
872 g_return_val_if_fail (extension != NULL, NULL);
873
874 GST_LOG_OBJECT (obj, "finding caps for extension %s", extension);
875
876 type_list = gst_type_find_factory_get_list ();
877
878 for (l = type_list; l; l = g_list_next (l)) {
879 GstTypeFindFactory *factory;
880 const gchar *const *ext;
881
882 factory = GST_TYPE_FIND_FACTORY (l->data);
883
884 /* we only want to check those factories without a function */
885 if (gst_type_find_factory_has_function (factory))
886 continue;
887
888 /* get the extension that this typefind factory can handle */
889 ext = gst_type_find_factory_get_extensions (factory);
890 if (ext == NULL)
891 continue;
892
893 /* there are extension, see if one of them matches the requested
894 * extension */
895 while (*ext != NULL) {
896 if (strcmp (*ext, extension) == 0) {
897 /* we found a matching extension, take the caps */
898 if ((result = gst_type_find_factory_get_caps (factory))) {
899 gst_caps_ref (result);
900 goto done;
901 }
902 }
903 ++ext;
904 }
905 }
906 done:
907 gst_plugin_feature_list_free (type_list);
908
909 GST_LOG_OBJECT (obj, "Returning %" GST_PTR_FORMAT, result);
910
911 return result;
912 }
913