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 Library 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 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library 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/profile/gstprofile.h>
24 #include "gupnp-dlna-discoverer.h"
25 #include "gupnp-dlna-load.h"
26 #include "gupnp-dlna-profile.h"
29 * This file provides the infrastructure to load DLNA profiles and the
30 * corresponding restrictions from an on-disk representation, and use them to
31 * map a given stream to its DLNA profile, if possible.
33 * Each DLNA profile is represented as a GstEncodingProfile (there might be
34 * exceptions where a single DLNA profile is represented by multiple
35 * GstEncodingProfiles - right now that's only LPCM).
37 * For a GstEncodingProfile "profile", the following fields will be populated:
39 * profile.name = "<DLNA Profile Name>"
40 * profile.format = Muxing format caps (with restrictions) if specified,
42 * profile.encodingprofiles = GList of GstStreamEncodingProfiles
44 * For each stream of the given profile, "profile.encodingprofiles" will have
45 * a GstEncodingProfile representing the restrictions for that stream (for a
46 * video format there will be one audio and one video stream, for example).
48 * enc_profile.type = GST_ENCODING_PROFILE_{AUDIO,VIDEO,...} (UNKNOWN for
49 * container restrictions)
50 * enc_profile.format = GstCaps with all the restrictions for this format
51 * enc_profile.restriction = GST_CAPS_ANY
53 * We assume that all DLNA profiles have exactly one audio stream, or one audio
54 * stream and one video stream.
56 * Things yet to account for:
58 * 1. Multiple audio/video streams (we need to pick the "main" one - how?
59 * Possibly get information from the demuxer.)
61 * 2. How do we handle discovered metadata which is in tags, but not in caps?
62 * Could potentially move it to caps in a post-discovery, pre-guessing
66 /* New profile guessing API */
68 static GList *profiles = NULL;
70 static gboolean is_video_profile (GstEncodingProfile *profile)
74 for (i = profile->encodingprofiles; i; i = i->next)
75 if (((GstStreamEncodingProfile *) i->data)->type ==
76 GST_ENCODING_PROFILE_VIDEO)
82 static gboolean structure_can_intersect (const GstStructure *st1,
83 const GstStructure *st2)
85 /* Since there is no API to intersect GstStructures, we cheat (thanks
86 * for the idea, tpm!) and make caps from the structuresa */
88 GstCaps *caps1, *caps2;
91 caps1 = gst_caps_new_full (gst_structure_copy (st1), NULL);
92 caps2 = gst_caps_new_full (gst_structure_copy (st2), NULL);
94 ret = gst_caps_can_intersect (caps1, caps2);
96 gst_caps_unref (caps1);
97 gst_caps_unref (caps2);
101 static gboolean structure_is_subset (const GstStructure *st1,
102 const GstStructure *st2)
106 for (i = 0; i < gst_structure_n_fields (st2); i++) {
107 const gchar *name = gst_structure_nth_field_name (st2, i);
109 if (!gst_structure_has_field(st1, name))
117 * Returns TRUE if stream_caps and profile_caps can intersect, and the
118 * intersecting structure from profile_caps is a subset of stream_caps. Put
119 * simply, the condition being met is that stream_caps intersects with
120 * profile_caps, and that intersection includes *all* fields specified by
121 * profile_caps (viz. all the fields specified by the DLNA profile's
124 static gboolean caps_can_intersect_and_is_subset (GstCaps *stream_caps,
125 GstCaps *profile_caps)
128 GstStructure *stream_st, *profile_st;
130 stream_st = gst_caps_get_structure (stream_caps, 0);
132 for (i = 0; i < gst_caps_get_size (profile_caps); i++) {
133 profile_st = gst_caps_get_structure (profile_caps, i);
135 if (structure_can_intersect (stream_st, profile_st) &&
136 structure_is_subset (stream_st, profile_st))
144 match_profile (GstEncodingProfile *profile,
146 GstEncodingProfileType type)
150 /* Profiles with an empty name are used only for inheritance and should
151 * not be matched against. */
152 if (profile->name[0] == '\0')
155 for (i = profile->encodingprofiles; i; i = i->next) {
156 GstStreamEncodingProfile *enc_profile = i->data;
158 if (enc_profile->type == type &&
159 caps_can_intersect_and_is_subset (caps,
160 enc_profile->format))
168 check_container (GstDiscovererInformation *info, GstEncodingProfile *profile)
170 /* Top-level GstStreamInformation in the topology will be
172 if (info->stream_info->streamtype == GST_STREAM_CONTAINER &&
173 gst_caps_can_intersect (info->stream_info->caps, profile->format))
175 else if (info->stream_info->streamtype != GST_STREAM_CONTAINER &&
176 gst_caps_is_empty (profile->format))
183 caps_from_audio_stream_info (GstStreamAudioInformation *audio)
185 GstCaps *caps = gst_caps_copy ((GST_STREAM_INFORMATION (audio))->caps);
187 if (audio->sample_rate)
188 gst_caps_set_simple (caps, "rate", G_TYPE_INT,
189 audio->sample_rate, NULL);
191 gst_caps_set_simple (caps, "channels", G_TYPE_INT,
192 audio->channels, NULL);
194 gst_caps_set_simple (caps, "bitrate", G_TYPE_INT,
195 audio->bitrate, NULL);
196 if (audio->max_bitrate)
197 gst_caps_set_simple (caps, "maximum-bitrate", G_TYPE_INT,
198 audio->max_bitrate, NULL);
200 gst_caps_set_simple (caps, "depth", G_TYPE_INT,
207 check_audio_profile (GstEncodingProfile *profile,
208 GstDiscovererInformation *info)
212 gboolean found = FALSE;
214 /* Optimisation TODO: this can be pre-computed */
215 if (is_video_profile (profile))
218 for (i = info->stream_list; !found && i; i = i->next) {
219 GstStreamInformation *stream = (GstStreamInformation *) i->data;
220 GstStreamAudioInformation *audio;
222 if (stream->streamtype != GST_STREAM_AUDIO)
225 audio = GST_STREAM_AUDIO_INFORMATION (stream);
226 caps = caps_from_audio_stream_info (audio);
228 if (match_profile (profile, caps, GST_ENCODING_PROFILE_AUDIO)) {
233 gst_caps_unref (caps);
240 guess_audio_profile (GstDiscovererInformation *info, gchar **name, gchar **mime)
244 for (i = profiles; i; i = i->next) {
245 GUPnPDLNAProfile *profile = (GUPnPDLNAProfile *)(i->data);
247 if (check_audio_profile (profile->enc_profile, info) &&
248 check_container (info, profile->enc_profile)) {
249 *name = g_strdup (profile->name);
250 *mime = g_strdup (profile->mime);
257 caps_from_video_stream_info (GstStreamVideoInformation *video)
259 GstStreamInformation *stream = GST_STREAM_INFORMATION (video);
260 GstCaps *caps = gst_caps_copy (stream->caps);
264 gst_caps_set_simple (caps, "height", G_TYPE_INT,
265 video->height, NULL);
267 gst_caps_set_simple (caps, "width", G_TYPE_INT,
269 if (GST_VALUE_HOLDS_FRACTION (&video->frame_rate)) {
270 n = gst_value_get_fraction_numerator (&video->frame_rate);
271 d = gst_value_get_fraction_denominator (&video->frame_rate);
272 gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION,
275 if (GST_VALUE_HOLDS_FRACTION (&video->pixel_aspect_ratio)) {
276 n = gst_value_get_fraction_numerator (
277 &video->pixel_aspect_ratio);
278 d = gst_value_get_fraction_denominator (
279 &video->pixel_aspect_ratio);
280 gst_caps_set_simple (caps, "pixel-aspect-ratio",
281 GST_TYPE_FRACTION, n, d, NULL);
283 if (video->format != GST_VIDEO_FORMAT_UNKNOWN)
284 gst_caps_set_simple (caps, "format", GST_TYPE_FOURCC,
285 gst_video_format_to_fourcc (video->format),
289 if (gst_tag_list_get_uint (stream->tags, "bitrate", &bitrate))
290 gst_caps_set_simple (caps, "bitrate", G_TYPE_INT,
291 (int) bitrate, NULL);
292 if (gst_tag_list_get_uint (stream->tags, "maximum-bitrate",
294 gst_caps_set_simple (caps, "maximum-bitrate",
295 G_TYPE_INT, (int) bitrate, NULL);
302 check_video_profile (GstEncodingProfile *profile,
303 GstDiscovererInformation *info)
306 gboolean found_video = FALSE, found_audio = FALSE;;
308 /* Check video and audio restrictions */
309 for (i = info->stream_list;
310 i && !(found_video && found_audio);
312 GstStreamInformation *stream;
313 GstCaps *caps = NULL;
315 stream = (GstStreamInformation *) i->data;
318 stream->streamtype == GST_STREAM_VIDEO) {
319 caps = caps_from_video_stream_info (
320 GST_STREAM_VIDEO_INFORMATION (stream));
321 if (match_profile (profile,
323 GST_ENCODING_PROFILE_VIDEO))
325 } else if (!found_audio &&
326 stream->streamtype == GST_STREAM_AUDIO) {
327 caps = caps_from_audio_stream_info (
328 GST_STREAM_AUDIO_INFORMATION (stream));
329 if (match_profile (profile,
331 GST_ENCODING_PROFILE_AUDIO))
336 gst_caps_unref (caps);
339 if (!found_video || !found_audio)
342 /* Check container restrictions */
343 if (!check_container (info, profile))
350 guess_video_profile (GstDiscovererInformation *info, gchar **name, gchar **mime)
352 GUPnPDLNAProfile *profile = NULL;
355 for (i = profiles; i; i = i->next) {
356 profile = (GUPnPDLNAProfile *)(i->data);
358 if (check_video_profile (profile->enc_profile, info)) {
359 *name = g_strdup (profile->name);
360 *mime = g_strdup (profile->mime);
367 guess_image_profile (GstStreamInformation *info, gchar **name, gchar **mime)
369 GstStreamVideoInformation *video = GST_STREAM_VIDEO_INFORMATION (info);
372 gboolean found = FALSE;
374 if (!info || info->streamtype != GST_STREAM_IMAGE)
377 caps = caps_from_video_stream_info (video);
379 for (i = profiles; !found && i; i = i->next) {
380 GUPnPDLNAProfile *profile = (GUPnPDLNAProfile *)(i->data);
382 /* Optimisation TODO: this can be pre-computed */
383 if (is_video_profile (profile->enc_profile))
386 if (match_profile (profile->enc_profile,
388 GST_ENCODING_PROFILE_IMAGE)) {
390 *name = g_strdup (profile->name);
391 *mime = g_strdup (profile->mime);
396 gst_caps_unref (caps);
399 GUPnPDLNAInformation *
400 gupnp_dlna_information_new_from_discoverer_info (GstDiscovererInformation * info)
402 GUPnPDLNAInformation *dlna;
403 GstStreamType type = GST_STREAM_UNKNOWN;
404 GList *tmp = info->stream_list;
405 gchar *name = NULL, *mime = NULL;
408 profiles = g_list_concat (profiles,
409 gupnp_dlna_load_profiles_from_disk ());
413 GstStreamInformation *stream_info =
414 (GstStreamInformation *) tmp->data;
416 if (stream_info->streamtype == GST_STREAM_VIDEO)
417 type = GST_STREAM_VIDEO;
418 else if (stream_info->streamtype == GST_STREAM_IMAGE)
419 type = GST_STREAM_IMAGE;
420 else if (stream_info->streamtype == GST_STREAM_AUDIO &&
421 type != GST_STREAM_VIDEO)
422 type = GST_STREAM_AUDIO;
427 if (type == GST_STREAM_AUDIO)
428 guess_audio_profile (info, &name, &mime);
429 else if (type == GST_STREAM_VIDEO)
430 guess_video_profile (info, &name, &mime);
431 else if (type == GST_STREAM_IMAGE)
432 /* There will be only one GstStreamInformation for images */
433 guess_image_profile (info->stream_info, &name, &mime);
435 dlna = gupnp_dlna_information_new (name, mime, info);
443 gupnp_dlna_profile_from_name (const gchar *name)
448 profiles = g_list_concat (profiles,
449 gupnp_dlna_load_profiles_from_disk ());
452 for (i = profiles; i != NULL; i = i->next) {
453 GUPnPDLNAProfile *profile = (GUPnPDLNAProfile *) i->data;
455 if (g_str_equal (profile->name, name)) {
456 g_object_ref (profile);