2 * Copyright (C) 2010 Nokia Corporation.
4 * Authors: Arun Raghavan <arun.raghavan@collabora.co.uk>
6 * This library 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 of the License, or (at your option) any later version.
11 * This library 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.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
23 #include <gst/pbutils/pbutils.h>
24 #include "gupnp-dlna-discoverer.h"
25 #include "gupnp-dlna-profile.h"
28 * This file provides the infrastructure to load DLNA profiles and the
29 * corresponding restrictions from an on-disk representation, and use them to
30 * map a given stream to its DLNA profile, if possible.
32 * Each DLNA profile is represented as a GstEncodingProfile (there might be
33 * exceptions where a single DLNA profile is represented by multiple
34 * GstEncodingProfiles - right now that's only LPCM).
36 * For a GstEncodingProfile "profile", the following fields will be populated:
38 * profile.name = "<DLNA Profile Name>"
39 * profile.format = Muxing format caps (with restrictions) if specified,
41 * profile.encodingprofiles = GList of GstStreamEncodingProfiles
43 * For each stream of the given profile, "profile.encodingprofiles" will have
44 * a GstEncodingProfile representing the restrictions for that stream (for a
45 * video format there will be one audio and one video stream, for example).
47 * enc_profile.type = GST_ENCODING_PROFILE_{AUDIO,VIDEO,...} (UNKNOWN for
48 * container restrictions)
49 * enc_profile.format = GstCaps with all the restrictions for this format
50 * enc_profile.restriction = GST_CAPS_ANY
52 * We assume that all DLNA profiles have exactly one audio stream, or one audio
53 * stream and one video stream.
55 * Things yet to account for:
57 * 1. Multiple audio/video streams (we need to pick the "main" one - how?
58 * Possibly get information from the demuxer.)
60 * 2. How do we handle discovered metadata which is in tags, but not in caps?
61 * Could potentially move it to caps in a post-discovery, pre-guessing
65 /* New profile guessing API */
67 #define GUPNP_DLNA_DEBUG_ENV "GUPNP_DLNA_DEBUG"
69 #define gupnp_dlna_debug(args...) \
71 const gchar *_e = g_getenv (GUPNP_DLNA_DEBUG_ENV); \
72 if (_e && !g_str_equal (_e, "0")) \
77 is_video_profile (const GstEncodingProfile *profile)
79 const GList *i, *profiles_list;
81 if (GST_IS_ENCODING_CONTAINER_PROFILE (profile)) {
82 profiles_list = gst_encoding_container_profile_get_profiles
83 (GST_ENCODING_CONTAINER_PROFILE (profile));
85 for (i = profiles_list ; i; i = i->next)
86 if (GST_IS_ENCODING_VIDEO_PROFILE (i->data))
94 structure_can_intersect (const GstStructure *st1, const GstStructure *st2)
96 /* Since there is no API to intersect GstStructures, we cheat (thanks
97 * for the idea, tpm!) and make caps from the structuresa */
99 GstCaps *caps1, *caps2;
102 caps1 = gst_caps_new_full (gst_structure_copy (st1), NULL);
103 caps2 = gst_caps_new_full (gst_structure_copy (st2), NULL);
105 ret = gst_caps_can_intersect (caps1, caps2);
107 gst_caps_unref (caps1);
108 gst_caps_unref (caps2);
114 structure_is_subset (const GstStructure *st1, const GstStructure *st2)
118 for (i = 0; i < gst_structure_n_fields (st2); i++) {
119 const gchar *name = gst_structure_nth_field_name (st2, i);
121 if (!gst_structure_has_field(st1, name)) {
122 gupnp_dlna_debug (" missing field %s", name);
131 * Returns TRUE if stream_caps and profile_caps can intersect, and the
132 * intersecting structure from profile_caps is a subset of stream_caps. Put
133 * simply, the condition being met is that stream_caps intersects with
134 * profile_caps, and that intersection includes *all* fields specified by
135 * profile_caps (viz. all the fields specified by the DLNA profile's
139 caps_can_intersect_and_is_subset (GstCaps *stream_caps,
140 const GstCaps *profile_caps)
143 GstStructure *stream_st, *profile_st;
145 stream_st = gst_caps_get_structure (stream_caps, 0);
147 for (i = 0; i < gst_caps_get_size (profile_caps); i++) {
148 profile_st = gst_caps_get_structure (profile_caps, i);
150 if (structure_can_intersect (stream_st, profile_st) &&
151 structure_is_subset (stream_st, profile_st))
159 match_profile (GstEncodingProfile *profile,
163 const GList *i, *profiles_list;
166 /* Profiles with an empty name are used only for inheritance and should
167 * not be matched against. */
168 name = gst_encoding_profile_get_name (profile);
172 profiles_list = gst_encoding_container_profile_get_profiles
173 (GST_ENCODING_CONTAINER_PROFILE (profile));
175 for (i = profiles_list; i; i = i->next){
176 GstEncodingProfile *enc_profile = GST_ENCODING_PROFILE
178 const GstCaps *format = gst_encoding_profile_get_format
181 if (type == G_TYPE_FROM_INSTANCE (enc_profile) &&
182 caps_can_intersect_and_is_subset (caps, format))
190 check_container (GstDiscovererInfo *info,
191 GstEncodingProfile *profile)
193 GstDiscovererStreamInfo *stream_info;
195 GstCaps *stream_caps;
196 gboolean ret = FALSE;
198 const GstCaps *profile_caps = gst_encoding_profile_get_format (profile);
200 /* Top-level GstStreamInformation in the topology will be
202 stream_info = gst_discoverer_info_get_stream_info (info);
203 stream_caps = gst_discoverer_stream_info_get_caps (stream_info);
204 stream_type = G_TYPE_FROM_INSTANCE (stream_info);
206 if (stream_type == GST_TYPE_DISCOVERER_CONTAINER_INFO &&
207 gst_caps_can_intersect (stream_caps, profile_caps))
209 else if (stream_type != GST_TYPE_DISCOVERER_CONTAINER_INFO &&
210 gst_caps_is_empty (profile_caps))
213 gst_discoverer_stream_info_unref (stream_info);
214 gst_caps_unref (stream_caps);
220 caps_from_audio_stream_info (GstDiscovererStreamInfo *info)
222 GstCaps *temp = gst_discoverer_stream_info_get_caps (info);
223 GstCaps *caps = gst_caps_copy (temp);
224 const GstDiscovererAudioInfo *audio_info =
225 GST_DISCOVERER_AUDIO_INFO(info);
228 gst_caps_unref (temp);
230 data = gst_discoverer_audio_info_get_sample_rate (audio_info);
232 gst_caps_set_simple (caps, "rate", G_TYPE_INT, data, NULL);
234 data = gst_discoverer_audio_info_get_channels (audio_info);
236 gst_caps_set_simple (caps, "channels", G_TYPE_INT, data, NULL);
238 data = gst_discoverer_audio_info_get_bitrate (audio_info);
240 gst_caps_set_simple (caps, "bitrate", G_TYPE_INT, data, NULL);
242 data = gst_discoverer_audio_info_get_max_bitrate (audio_info);
245 (caps, "maximum-bitrate", G_TYPE_INT, data, NULL);
247 data = gst_discoverer_audio_info_get_depth (audio_info);
249 gst_caps_set_simple (caps, "depth", G_TYPE_INT, data, NULL);
255 check_audio_profile (GstDiscovererInfo *info,
256 GstEncodingProfile *profile)
259 GList *i, *stream_list;
260 gboolean found = FALSE;
262 /* Optimisation TODO: this can be pre-computed */
263 if (is_video_profile (profile))
266 stream_list = gst_discoverer_info_get_stream_list (info);
268 for (i = stream_list; !found && i; i = i->next) {
269 GstDiscovererStreamInfo *stream =
270 GST_DISCOVERER_STREAM_INFO(i->data);
271 GType stream_type = G_TYPE_FROM_INSTANCE (stream);
273 if (stream_type != GST_TYPE_DISCOVERER_AUDIO_INFO)
276 caps = caps_from_audio_stream_info (stream);
278 if (match_profile (profile,
280 GST_TYPE_ENCODING_AUDIO_PROFILE)) {
285 gst_caps_unref (caps);
288 gst_discoverer_stream_info_list_free (stream_list);
294 guess_audio_profile (GstDiscovererInfo *info,
300 GUPnPDLNAProfile *profile;
301 GstEncodingProfile *enc_profile;
303 for (i = profiles; i; i = i->next) {
304 profile = (GUPnPDLNAProfile *)(i->data);
305 enc_profile = gupnp_dlna_profile_get_encoding_profile (profile);
307 gupnp_dlna_debug ("Checking DLNA profile %s",
308 gupnp_dlna_profile_get_name (profile));
310 if (!check_audio_profile (info, enc_profile))
311 gupnp_dlna_debug (" Audio did not match");
312 else if (!check_container (info, enc_profile))
313 gupnp_dlna_debug (" Container did not match");
316 (gupnp_dlna_profile_get_name (profile));
318 (gupnp_dlna_profile_get_mime (profile));
325 caps_from_video_stream_info (GstDiscovererStreamInfo *info)
327 GstCaps *temp = gst_discoverer_stream_info_get_caps (info);
328 GstCaps *caps = gst_caps_copy (temp);
329 const GstDiscovererVideoInfo *video_info =
330 GST_DISCOVERER_VIDEO_INFO (info);
331 const GstTagList *stream_tag_list;
335 gst_caps_unref (temp);
337 data = gst_discoverer_video_info_get_height (video_info);
339 gst_caps_set_simple (caps, "height", G_TYPE_INT, data, NULL);
341 data = gst_discoverer_video_info_get_width (video_info);
343 gst_caps_set_simple (caps, "width", G_TYPE_INT, data, NULL);
345 data = gst_discoverer_video_info_get_depth (video_info);
347 gst_caps_set_simple (caps, "depth", G_TYPE_INT, data, NULL);
349 n = gst_discoverer_video_info_get_framerate_num (video_info);
350 d = gst_discoverer_video_info_get_framerate_denom (video_info);
352 gst_caps_set_simple (caps,
354 GST_TYPE_FRACTION, n, d,
357 n = gst_discoverer_video_info_get_par_num (video_info);
358 d = gst_discoverer_video_info_get_par_denom (video_info);
360 gst_caps_set_simple (caps,
361 "pixel-aspect-ratio",
362 GST_TYPE_FRACTION, n, d,
365 value = gst_discoverer_video_info_is_interlaced (video_info);
368 (caps, "interlaced", G_TYPE_BOOLEAN, value, NULL);
370 stream_tag_list = gst_discoverer_stream_info_get_tags (info);
371 if (stream_tag_list) {
373 if (gst_tag_list_get_uint (stream_tag_list, "bitrate", &bitrate))
375 (caps, "bitrate", G_TYPE_INT, (int) bitrate, NULL);
377 if (gst_tag_list_get_uint (stream_tag_list,
380 gst_caps_set_simple (caps,
391 check_video_profile (GstDiscovererInfo *info,
392 GstEncodingProfile *profile)
394 GList *i, *stream_list;
395 gboolean found_video = FALSE, found_audio = FALSE;;
397 stream_list = gst_discoverer_info_get_stream_list (info);
399 /* Check video and audio restrictions */
400 for (i = stream_list;
401 i && !(found_video && found_audio);
403 GstDiscovererStreamInfo *stream;
405 GstCaps *caps = NULL;
407 stream = GST_DISCOVERER_STREAM_INFO(i->data);
408 stream_type = G_TYPE_FROM_INSTANCE (stream);
411 stream_type == GST_TYPE_DISCOVERER_VIDEO_INFO) {
412 caps = caps_from_video_stream_info (stream);
413 if (match_profile (profile,
415 GST_TYPE_ENCODING_VIDEO_PROFILE))
418 gupnp_dlna_debug (" Video did not match");
419 } else if (!found_audio &&
420 stream_type == GST_TYPE_DISCOVERER_AUDIO_INFO) {
421 caps = caps_from_audio_stream_info (stream);
422 if (match_profile (profile,
424 GST_TYPE_ENCODING_AUDIO_PROFILE))
427 gupnp_dlna_debug (" Audio did not match");
431 gst_caps_unref (caps);
434 gst_discoverer_stream_info_list_free (stream_list);
436 if (!found_video || !found_audio)
439 /* Check container restrictions */
440 if (!check_container (info, profile)) {
441 gupnp_dlna_debug (" Container did not match");
449 guess_video_profile (GstDiscovererInfo *info,
454 GUPnPDLNAProfile *profile = NULL;
455 GstEncodingProfile *enc_profile;
458 for (i = profiles; i; i = i->next) {
459 profile = (GUPnPDLNAProfile *)(i->data);
460 enc_profile = gupnp_dlna_profile_get_encoding_profile (profile);
462 gupnp_dlna_debug ("Checking DLNA profile %s",
463 gupnp_dlna_profile_get_name (profile));
464 if (check_video_profile (info, enc_profile)) {
465 *name = g_strdup (gupnp_dlna_profile_get_name (profile));
466 *mime = g_strdup (gupnp_dlna_profile_get_mime (profile));
473 guess_image_profile (GstDiscovererStreamInfo *info,
480 gboolean found = FALSE;
481 GUPnPDLNAProfile *profile;
482 GstEncodingProfile *enc_profile;
483 const GstDiscovererVideoInfo *video_info =
484 GST_DISCOVERER_VIDEO_INFO (info);
486 if (!info || !gst_discoverer_video_info_is_image (video_info))
489 caps = caps_from_video_stream_info (info);
491 for (i = profiles; !found && i; i = i->next) {
492 profile = (GUPnPDLNAProfile *)(i->data);
493 enc_profile = gupnp_dlna_profile_get_encoding_profile (profile);
495 /* Optimisation TODO: this can be pre-computed */
496 if (!is_video_profile (enc_profile))
499 if (match_profile (enc_profile,
501 GST_TYPE_ENCODING_VIDEO_PROFILE)) {
503 *name = g_strdup (gupnp_dlna_profile_get_name (profile));
504 *mime = g_strdup (gupnp_dlna_profile_get_mime (profile));
509 gst_caps_unref (caps);
512 GUPnPDLNAInformation *
513 gupnp_dlna_information_new_from_discoverer_info (GstDiscovererInfo *info,
516 GUPnPDLNAInformation *dlna;
517 GList *video_list, *audio_list;
518 gchar *name = NULL, *mime = NULL;
520 video_list = gst_discoverer_info_get_video_streams (info);
521 audio_list = gst_discoverer_info_get_audio_streams (info);
523 if ((g_list_length (video_list) ==1 ) &&
524 gst_discoverer_video_info_is_image
525 (GST_DISCOVERER_VIDEO_INFO
526 (video_list->data))) {
527 GstDiscovererStreamInfo *stream;
528 stream = (GstDiscovererStreamInfo *) video_list->data;
529 guess_image_profile (stream, &name, &mime, profiles);
531 guess_video_profile (info, &name, &mime, profiles);
532 } else if (audio_list)
533 guess_audio_profile (info, &name, &mime, profiles);
535 gst_discoverer_stream_info_list_free (audio_list);
536 gst_discoverer_stream_info_list_free (video_list);
538 dlna = gupnp_dlna_information_new (name, mime, info);