rtsp: Port to GIO
[platform/upstream/gstreamer.git] / gst-libs / gst / pbutils / descriptions.c
1 /* GStreamer Plugins Base utils library source/sink/codec description support
2  * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /**
21  * SECTION:gstpbutilsdescriptions
22  * @short_description: Provides human-readable descriptions for caps/codecs
23  * and encoder, decoder, URI source and URI sink elements
24  *
25  * <refsect2>
26  * <para>
27  * The above functions provide human-readable strings for media formats
28  * and decoder/demuxer/depayloader/encoder/muxer/payloader elements for use
29  * in error dialogs or other messages shown to users.
30  * </para>
31  * <para>
32  * gst_pb_utils_add_codec_description_to_tag_list() is a utility function
33  * for demuxer and decoder elements to add audio/video codec tags from a
34  * given (fixed) #GstCaps.
35  * </para>
36  * </refsect2>
37  */
38
39 #ifdef HAVE_CONFIG_H
40 # include "config.h"
41 #endif
42
43 #include "gst/gst-i18n-plugin.h"
44
45 #include <gst/audio/audio.h>
46 #include <gst/video/video.h>
47
48 #include "pbutils.h"
49 #include "pbutils-private.h"
50
51 #include <string.h>
52
53 typedef enum
54 {
55   FLAG_CONTAINER = (1 << 0),    /* format is a container format (muxed)             */
56   FLAG_SYSTEMSTREAM = (1 << 1)  /* match record only if caps have systemstream=true */
57 } FormatFlags;
58
59 typedef struct
60 {
61   const gchar *type;
62   const gchar *desc;
63   FormatFlags flags;
64 } FormatInfo;
65
66 static const FormatInfo formats[] = {
67   /* container/tag formats with static descriptions */
68   {"application/gxf", "General Exchange Format (GXF)", FLAG_CONTAINER},
69   {"application/ogg", "Ogg", FLAG_CONTAINER},
70   {"application/mxf", "Material eXchange Format (MXF)", FLAG_CONTAINER},
71   {"application/vnd.rn-realmedia", "Realmedia", FLAG_CONTAINER},
72   {"application/x-annodex", "Ogg", FLAG_CONTAINER},
73   {"application/x-id3", N_("ID3 tag"), FLAG_CONTAINER},
74   {"application/x-ape", N_("APE tag"), FLAG_CONTAINER},
75   {"application/x-apetag", N_("APE tag"), FLAG_CONTAINER},
76   {"application/x-icy", N_("ICY internet radio"), FLAG_CONTAINER},
77   {"application/x-3gp", "3GP", FLAG_CONTAINER},
78   {"application/x-pn-realaudio", "RealAudio", FLAG_CONTAINER},
79   {"application/x-yuv4mpeg", "Y4M", FLAG_CONTAINER},
80   {"multipart/x-mixed-replace", "Multipart", FLAG_CONTAINER},
81   {"video/x-fli", "FLI/FLC/FLX Animation", FLAG_CONTAINER},
82   {"video/x-flv", "Flash", FLAG_CONTAINER},
83   {"video/x-matroska", "Matroska", FLAG_CONTAINER},
84   {"video/webm", "WebM", FLAG_CONTAINER},
85   {"video/x-ms-asf", "Advanced Streaming Format (ASF)", FLAG_CONTAINER},
86   {"video/x-msvideo", "Audio Video Interleave (AVI)", FLAG_CONTAINER},
87   {"video/x-quicktime", "Quicktime", FLAG_CONTAINER},
88   {"video/quicktime", "Quicktime", FLAG_CONTAINER},
89   {"video/mj2", "Motion JPEG 2000", FLAG_CONTAINER},
90
91   /* audio formats with static descriptions */
92   {"audio/x-ac3", "AC-3 (ATSC A/52)", 0},
93   {"audio/ac3", "AC-3 (ATSC A/52)", 0},
94   {"audio/x-private-ac3", "DVD AC-3 (ATSC A/52)", 0},
95   {"audio/x-private1-ac3", "DVD AC-3 (ATSC A/52)", 0},
96   {"audio/x-alaw", "A-Law", 0},
97   {"audio/amr", "Adaptive Multi Rate (AMR)", 0},
98   {"audio/AMR", "Adaptive Multi Rate (AMR)", 0},
99   {"audio/AMR-WB", "Adaptive Multi Rate WideBand (AMR-WB)", 0},
100   {"audio/iLBC-sh", "Internet Low Bitrate Codec (iLBC)", 0},
101   {"audio/ms-gsm", "MS GSM", 0},
102   {"audio/qcelp", "QCELP", 0},
103   {"audio/aiff", "Audio Interchange File Format (AIFF)", 0},
104   {"audio/x-aiff", "Audio Interchange File Format (AIFF)", 0},
105   {"audio/x-alac", N_("Apple Lossless Audio (ALAC)"), 0},
106   {"audio/x-amr-nb-sh", "Adaptive Multi Rate NarrowBand (AMR-NB)", 0},
107   {"audio/x-amr-wb-sh", "Adaptive Multi Rate WideBand (AMR-WB)", 0},
108   {"audio/x-au", "Sun .au", 0},
109   {"audio/x-celt", "Constrained Energy Lapped Transform (CELT)", 0},
110   {"audio/x-cinepak", "Cinepak Audio", 0},
111   {"audio/x-dpcm", "DPCM", 0},
112   {"audio/x-dts", "DTS", 0},
113   {"audio/x-private1-dts", "DTS", 0},
114   {"audio/x-dv", "DV Audio", 0},
115   {"audio/x-flac", N_("Free Lossless Audio Codec (FLAC)"), 0},
116   {"audio/x-gsm", "GSM", 0},
117   {"audio/x-iec958", "S/PDIF IEC958", 0},       /* TODO: check description */
118   {"audio/x-iLBC", "Internet Low Bitrate Codec (iLBC)", 0},
119   {"audio/x-ircam", "Berkeley/IRCAM/CARL", 0},
120   {"audio/x-lpcm", "LPCM", 0},
121   {"audio/x-private1-lpcm", "DVD LPCM", 0},
122   {"audio/x-m4a", "MPEG-4 AAC", FLAG_CONTAINER},
123   {"audio/x-mod", "Module Music Format (MOD)", 0},
124   {"audio/x-mulaw", "Mu-Law", 0},
125   {"audio/x-musepack", "Musepack (MPC)", 0},
126   {"audio/x-nellymoser", "Nellymoser Asao", 0},
127   {"audio/x-nist", "Sphere NIST", 0},
128   {"audio/x-nsf", "Nintendo NSF", 0},
129   {"audio/x-paris", "Ensoniq PARIS", 0},
130   {"audio/x-qdm", "QDesign Music (QDM)", 0},
131   {"audio/x-qdm2", "QDesign Music (QDM) 2", 0},
132   {"audio/x-ralf-mpeg4-generic", "Real Audio Lossless (RALF)", 0},
133   {"audio/x-sds", "SDS", 0},
134   {"audio/x-shorten", "Shorten Lossless", 0},
135   {"audio/x-sid", "Sid", 0},
136   {"audio/x-sipro", "Sipro/ACELP.NET Voice", 0},
137   {"audio/x-siren", "Siren", 0},
138   {"audio/x-spc", "SNES-SPC700 Sound File Data", 0},
139   {"audio/x-speex", "Speex", 0},
140   {"audio/x-svx", "Amiga IFF / SVX8 / SV16", 0},
141   {"audio/x-tta", N_("Lossless True Audio (TTA)"), 0},
142   {"audio/x-ttafile", N_("Lossless True Audio (TTA)"), 0},
143   {"audio/x-vnd.sony.atrac3", "Sony ATRAC3", 0},
144   {"audio/x-vorbis", "Vorbis", 0},
145   {"audio/x-voc", "SoundBlaster VOC", 0},
146   {"audio/x-w64", "Sonic Foundry Wave64", 0},
147   {"audio/x-wav", "WAV", 0},
148   {"audio/x-wavpack", "Wavpack", 0},
149   {"audio/x-wavpack-correction", "Wavpack", 0},
150   {"audio/x-wms", N_("Windows Media Speech"), 0},
151   {"audio/x-voxware", "Voxware", 0},
152
153
154   /* video formats with static descriptions */
155   {"video/sp5x", "Sunplus JPEG 5.x", 0},
156   {"video/vivo", "Vivo", 0},
157   {"video/x-3ivx", "3ivx", 0},
158   {"video/x-4xm", "4X Technologies Video", 0},
159   {"video/x-apple-video", "Apple video", 0},
160   {"video/x-aasc", "Autodesk Animator", 0},
161   {"video/x-camtasia", "TechSmith Camtasia", 0},
162   {"video/x-cdxa", "RIFF/CDXA (VCD)", 0},
163   {"video/x-cinepak", "Cinepak Video", 0},
164   {"video/x-cirrus-logic-accupak", "Cirrus Logipak AccuPak", 0},
165   {"video/x-compressed-yuv", N_("CYUV Lossless"), 0},
166   {"video/x-dirac", "Dirac", 0},
167   {"video/x-dnxhd", "Digital Nonlinear Extensible High Definition (DNxHD)", 0},
168   /* FIXME 0.11: rename to subpicture/x-dvd or so */
169   {"video/x-dvd-subpicture", "DVD subpicture", 0},
170   {"video/x-ffv", N_("FFMpeg v1"), 0},
171   {"video/x-flash-screen", "Flash Screen Video", 0},
172   {"video/x-flash-video", "Sorenson Spark Video", 0},
173   {"video/x-h261", "H.261", 0},
174   {"video/x-huffyuv", "Huffyuv", 0},
175   {"video/x-intel-h263", "Intel H.263", 0},
176   {"video/x-jpeg", "Motion JPEG", 0},
177   /* { "video/x-jpeg-b", "", 0 }, does this actually exist? */
178   {"video/x-loco", "LOCO Lossless", 0},
179   {"video/x-mimic", "MIMIC", 0},
180   {"video/x-mjpeg", "Motion-JPEG", 0},
181   {"video/x-mjpeg-b", "Motion-JPEG format B", 0},
182   {"video/mpegts", "MPEG-2 Transport Stream", FLAG_CONTAINER},
183   {"video/x-mng", "Multiple Image Network Graphics (MNG)", 0},
184   {"video/x-mszh", N_("Lossless MSZH"), 0},
185   {"video/x-msvideocodec", "Microsoft Video 1", 0},
186   {"video/x-mve", "Interplay MVE", FLAG_CONTAINER},
187   {"video/x-nut", "NUT", FLAG_CONTAINER},
188   {"video/x-nuv", "MythTV NuppelVideo (NUV)", FLAG_CONTAINER},
189   {"video/x-qdrw", "Apple QuickDraw", 0},
190   {"video/x-smc", "Apple SMC", 0},
191   {"video/x-smoke", "Smoke", 0},
192   {"video/x-tarkin", "Tarkin", 0},
193   {"video/x-theora", "Theora", 0},
194   {"video/x-rle", N_("Run-length encoding"), 0},
195   {"video/x-ultimotion", "IBM UltiMotion", 0},
196   {"video/x-vcd", "VideoCD (VCD)", 0},
197   {"video/x-vmnc", "VMWare NC", 0},
198   {"video/x-vp3", "On2 VP3", 0},
199   {"video/x-vp5", "On2 VP5", 0},
200   {"video/x-vp6", "On2 VP6", 0},
201   {"video/x-vp6-flash", "On2 VP6/Flash", 0},
202   {"video/x-vp6-alpha", "On2 VP6 with alpha", 0},
203   {"video/x-vp7", "On2 VP7", 0},
204   {"video/x-vp8", "VP8", 0},
205   {"video/x-xvid", "XVID MPEG-4", 0},
206   {"video/x-zlib", "Lossless zlib video", 0},
207   {"video/x-zmbv", "Zip Motion Block video", 0},
208
209   /* image formats with static descriptions */
210   {"image/bmp", "BMP", 0},
211   {"image/x-bmp", "BMP", 0},
212   {"image/x-MS-bmp", "BMP", 0},
213   {"image/gif", "GIF", 0},
214   {"image/jpeg", "JPEG", 0},
215   {"image/jng", "JPEG Network Graphics (JNG)", 0},
216   {"image/png", "PNG", 0},
217   {"image/pbm", "Portable BitMap (PBM)", 0},
218   {"image/ppm", "Portable PixMap (PPM)", 0},
219   {"image/svg+xml", "Scalable Vector Graphics (SVG)", 0},
220   {"image/tiff", "TIFF", 0},
221   {"image/x-cmu-raster", "CMU Raster Format", 0},
222   {"image/x-degas", "DEGAS", 0},
223   {"image/x-icon", "ICO", 0},
224   {"image/x-j2c", "JPEG 2000", 0},
225   {"image/x-jpc", "JPEG 2000", 0},
226   {"image/jp2", "JPEG 2000", 0},
227   {"image/x-pcx", "PCX", 0},
228   {"image/x-xcf", "XFC", 0},
229   {"image/x-pixmap", "XPM", 0},
230   {"image/x-portable-anymap", "Portable AnyMap (PAM)", 0},
231   {"image/x-portable-graymap", "Portable GrayMap (PGM)", 0},
232   {"image/x-xpixmap", "XPM", 0},
233   {"image/x-quicktime", "QuickTime Image Format (QTIF)", 0},
234   {"image/x-sun-raster", "Sun Raster Format (RAS)", 0},
235   {"image/x-tga", "TGA", 0},
236   {"image/vnd.wap.wbmp", "Wireless Bitmap", 0},
237
238   /* subtitle formats with static descriptions */
239   {"application/x-ass", "ASS", 0},
240   {"application/x-subtitle-sami", N_("Sami subtitle format"), 0},
241   {"application/x-subtitle-tmplayer", N_("TMPlayer subtitle format"), 0},
242   {"application/x-kate", "Kate", 0},
243   {"subtitle/x-kate", N_("Kate subtitle format"), 0},
244   {"subpicture/x-dvb", "DVB subtitles", 0},
245   /* add variant field to typefinder? { "application/x-subtitle", N_("subtitle"), 0}, */
246
247   /* non-audio/video/container formats */
248   {"hdv/aux-v", "HDV AUX-V", 0},
249   {"hdv/aux-a", "HDV AUX-A", 0},
250
251   /* formats with dynamic descriptions */
252   {"audio/mpeg", NULL, 0},
253   {"audio/x-adpcm", NULL, 0},
254   {"audio/x-mace", NULL, 0},
255   {"audio/x-pn-realaudio", NULL, 0},
256   {"audio/x-raw", NULL, 0},
257   {"audio/x-wma", NULL, 0},
258   {"video/mpeg", NULL, FLAG_CONTAINER | FLAG_SYSTEMSTREAM},
259   {"video/mpeg", NULL, 0},
260   {"video/x-asus", NULL, 0},
261   {"video/x-ati-vcr", NULL, 0},
262   {"video/x-divx", NULL, 0},
263   {"video/x-dv", "Digital Video (DV) System Stream",
264       FLAG_CONTAINER | FLAG_SYSTEMSTREAM},
265   {"video/x-dv", "Digital Video (DV)", 0},
266   {"video/x-h263", NULL, 0},
267   {"video/x-h264", NULL, 0},
268   {"video/x-indeo", NULL, 0},
269   {"video/x-msmpeg", NULL, 0},
270   {"video/x-pn-realvideo", NULL, 0},
271 #if 0
272   /* do these exist? are they used anywhere? */
273   {"video/x-pn-multirate-realvideo", NULL, 0},
274   {"audio/x-pn-multirate-realaudio", NULL, 0},
275   {"audio/x-pn-multirate-realaudio-live", NULL, 0},
276 #endif
277   {"video/x-truemotion", NULL, 0},
278   {"video/x-raw", NULL, 0},
279   {"video/x-svq", NULL, 0},
280   {"video/x-wmv", NULL, 0},
281   {"video/x-xan", NULL, 0}
282 };
283
284 /* returns static descriptions and dynamic ones (such as video/x-raw),
285  * or NULL if caps aren't known at all */
286 static gchar *
287 format_info_get_desc (const FormatInfo * info, const GstCaps * caps)
288 {
289   const GstStructure *s;
290
291   g_assert (info != NULL);
292
293   if (info->desc != NULL)
294     return g_strdup (_(info->desc));
295
296   s = gst_caps_get_structure (caps, 0);
297
298   if (strcmp (info->type, "video/x-raw") == 0) {
299     gchar *ret = NULL;
300     const gchar *str = 0;
301     GstVideoFormat format;
302     const GstVideoFormatInfo *finfo;
303
304     str = gst_structure_get_string (s, "format");
305     if (str == NULL)
306       return g_strdup (_("Uncompressed video"));
307     format = gst_video_format_from_string (str);
308     if (format == GST_VIDEO_FORMAT_UNKNOWN)
309       return g_strdup (_("Uncompressed video"));
310
311     finfo = gst_video_format_get_info (format);
312
313     if (GST_VIDEO_FORMAT_INFO_IS_GRAY (finfo)) {
314       ret = g_strdup (_("Uncompressed gray"));
315     } else if (GST_VIDEO_FORMAT_INFO_IS_YUV (finfo)) {
316       const gchar *layout;
317       const gchar *subs;
318       gint w_sub, h_sub;
319
320       w_sub = GST_VIDEO_FORMAT_INFO_W_SUB (finfo, 1);
321       h_sub = GST_VIDEO_FORMAT_INFO_H_SUB (finfo, 1);
322
323       if (GST_VIDEO_FORMAT_INFO_N_PLANES (finfo) == 1) {
324         layout = "planar";
325       } else {
326         layout = "packed";
327       }
328
329       if (w_sub == 1 && h_sub == 1) {
330         subs = "4:4:4";
331       } else if (w_sub == 2 && h_sub == 1) {
332         subs = "4:2:2";
333       } else if (w_sub == 2 && h_sub == 2) {
334         subs = "4:2:0";
335       } else if (w_sub == 4 && h_sub == 1) {
336         subs = "4:1:1";
337       } else {
338         subs = "";
339       }
340       ret = g_strdup_printf (_("Uncompressed %s YUV %s"), layout, subs);
341     } else if (GST_VIDEO_FORMAT_INFO_IS_RGB (finfo)) {
342       gboolean alpha, palette;
343       gint bits;
344
345       alpha = GST_VIDEO_FORMAT_INFO_HAS_ALPHA (finfo);
346       palette = GST_VIDEO_FORMAT_INFO_HAS_PALETTE (finfo);
347       bits = GST_VIDEO_FORMAT_INFO_BITS (finfo);
348
349       ret = g_strdup_printf (_("Uncompressed %s%d-bit %s"),
350           palette ? "palettized " : "", bits, alpha ? "RGBA" : "RGB");
351     } else {
352       ret = g_strdup (_("Uncompressed video"));
353     }
354     return ret;
355   } else if (strcmp (info->type, "video/x-h263") == 0) {
356     const gchar *variant, *ret;
357
358     variant = gst_structure_get_string (s, "variant");
359     if (variant == NULL)
360       ret = "H.263";
361     else if (strcmp (variant, "itu") == 0)
362       ret = "ITU H.26n";        /* why not ITU H.263? (tpm) */
363     else if (strcmp (variant, "lead") == 0)
364       ret = "Lead H.263";
365     else if (strcmp (variant, "microsoft") == 0)
366       ret = "Microsoft H.263";
367     else if (strcmp (variant, "vdolive") == 0)
368       ret = "VDOLive";
369     else if (strcmp (variant, "vivo") == 0)
370       ret = "Vivo H.263";
371     else if (strcmp (variant, "xirlink") == 0)
372       ret = "Xirlink H.263";
373     else {
374       GST_WARNING ("Unknown H263 variant '%s'", variant);
375       ret = "H.263";
376     }
377     return g_strdup (ret);
378   } else if (strcmp (info->type, "video/x-h264") == 0) {
379     const gchar *variant, *ret;
380
381     variant = gst_structure_get_string (s, "variant");
382     if (variant == NULL)
383       ret = "H.264";
384     else if (strcmp (variant, "itu") == 0)
385       ret = "ITU H.264";
386     else if (strcmp (variant, "videosoft") == 0)
387       ret = "Videosoft H.264";
388     else if (strcmp (variant, "lead") == 0)
389       ret = "Lead H.264";
390     else {
391       GST_WARNING ("Unknown H264 variant '%s'", variant);
392       ret = "H.264";
393     }
394     return g_strdup (ret);
395   } else if (strcmp (info->type, "video/x-divx") == 0) {
396     gint ver = 0;
397
398     if (!gst_structure_get_int (s, "divxversion", &ver) || ver <= 2) {
399       GST_WARNING ("Unexpected DivX version in %" GST_PTR_FORMAT, caps);
400       return g_strdup ("DivX MPEG-4");
401     }
402     return g_strdup_printf (_("DivX MPEG-4 Version %d"), ver);
403   } else if (strcmp (info->type, "video/x-msmpeg") == 0) {
404     gint ver = 0;
405
406     if (!gst_structure_get_int (s, "msmpegversion", &ver) ||
407         ver < 40 || ver > 49) {
408       GST_WARNING ("Unexpected msmpegversion in %" GST_PTR_FORMAT, caps);
409       return g_strdup ("Microsoft MPEG-4 4.x");
410     }
411     return g_strdup_printf ("Microsoft MPEG-4 4.%d", ver % 10);
412   } else if (strcmp (info->type, "video/x-truemotion") == 0) {
413     gint ver = 0;
414
415     gst_structure_get_int (s, "trueversion", &ver);
416     switch (ver) {
417       case 1:
418         return g_strdup_printf ("Duck TrueMotion 1");
419       case 2:
420         return g_strdup_printf ("TrueMotion 2.0");
421       default:
422         GST_WARNING ("Unexpected trueversion in %" GST_PTR_FORMAT, caps);
423         break;
424     }
425     return g_strdup_printf ("TrueMotion");
426   } else if (strcmp (info->type, "video/x-xan") == 0) {
427     gint ver = 0;
428
429     if (!gst_structure_get_int (s, "wcversion", &ver) || ver < 1) {
430       GST_WARNING ("Unexpected wcversion in %" GST_PTR_FORMAT, caps);
431       return g_strdup ("Xan Wing Commander");
432     }
433     return g_strdup_printf ("Xan Wing Commander %u", ver);
434   } else if (strcmp (info->type, "video/x-indeo") == 0) {
435     gint ver = 0;
436
437     if (!gst_structure_get_int (s, "indeoversion", &ver) || ver < 2) {
438       GST_WARNING ("Unexpected indeoversion in %" GST_PTR_FORMAT, caps);
439       return g_strdup ("Intel Indeo");
440     }
441     return g_strdup_printf ("Intel Indeo %u", ver);
442   } else if (strcmp (info->type, "audio/x-wma") == 0) {
443     gint ver = 0;
444
445     gst_structure_get_int (s, "wmaversion", &ver);
446     switch (ver) {
447       case 1:
448       case 2:
449       case 3:
450         return g_strdup_printf ("Windows Media Audio %d", ver + 6);
451       default:
452         break;
453     }
454     GST_WARNING ("Unexpected wmaversion in %" GST_PTR_FORMAT, caps);
455     return g_strdup ("Windows Media Audio");
456   } else if (strcmp (info->type, "video/x-wmv") == 0) {
457     gint ver = 0;
458
459     gst_structure_get_int (s, "wmvversion", &ver);
460     switch (ver) {
461       case 1:
462       case 2:
463       case 3:
464         return g_strdup_printf ("Windows Media Video %d", ver + 6);
465       default:
466         break;
467     }
468     GST_WARNING ("Unexpected wmvversion in %" GST_PTR_FORMAT, caps);
469     return g_strdup ("Windows Media Video");
470   } else if (strcmp (info->type, "audio/x-mace") == 0) {
471     gint ver = 0;
472
473     gst_structure_get_int (s, "maceversion", &ver);
474     if (ver == 3 || ver == 6) {
475       return g_strdup_printf ("MACE-%d", ver);
476     } else {
477       GST_WARNING ("Unexpected maceversion in %" GST_PTR_FORMAT, caps);
478       return g_strdup ("MACE");
479     }
480   } else if (strcmp (info->type, "video/x-svq") == 0) {
481     gint ver = 0;
482
483     gst_structure_get_int (s, "svqversion", &ver);
484     if (ver == 1 || ver == 3) {
485       return g_strdup_printf ("Sorensen Video %d", ver);
486     } else {
487       GST_WARNING ("Unexpected svqversion in %" GST_PTR_FORMAT, caps);
488       return g_strdup ("Sorensen Video");
489     }
490   } else if (strcmp (info->type, "video/x-asus") == 0) {
491     gint ver = 0;
492
493     gst_structure_get_int (s, "asusversion", &ver);
494     if (ver == 1 || ver == 2) {
495       return g_strdup_printf ("Asus Video %d", ver);
496     } else {
497       GST_WARNING ("Unexpected asusversion in %" GST_PTR_FORMAT, caps);
498       return g_strdup ("Asus Video");
499     }
500   } else if (strcmp (info->type, "video/x-ati-vcr") == 0) {
501     gint ver = 0;
502
503     gst_structure_get_int (s, "vcrversion", &ver);
504     if (ver == 1 || ver == 2) {
505       return g_strdup_printf ("ATI VCR %d", ver);
506     } else {
507       GST_WARNING ("Unexpected acrversion in %" GST_PTR_FORMAT, caps);
508       return g_strdup ("ATI VCR");
509     }
510   } else if (strcmp (info->type, "audio/x-adpcm") == 0) {
511     const GValue *layout_val;
512
513     layout_val = gst_structure_get_value (s, "layout");
514     if (layout_val != NULL && G_VALUE_HOLDS_STRING (layout_val)) {
515       const gchar *layout;
516
517       if ((layout = g_value_get_string (layout_val))) {
518         gchar *layout_upper, *ret;
519
520         if (strcmp (layout, "swf") == 0)
521           return g_strdup ("Shockwave ADPCM");
522         if (strcmp (layout, "microsoft") == 0)
523           return g_strdup ("Microsoft ADPCM");
524         if (strcmp (layout, "quicktime") == 0)
525           return g_strdup ("Quicktime ADPCM");
526         if (strcmp (layout, "westwood") == 0)
527           return g_strdup ("Westwood ADPCM");
528         if (strcmp (layout, "yamaha") == 0)
529           return g_strdup ("Yamaha ADPCM");
530         /* FIXME: other layouts: sbpro2, sbpro3, sbpro4, ct, g726, ea,
531          * adx, xa, 4xm, smjpeg, dk4, dk3, dvi */
532         layout_upper = g_ascii_strup (layout, -1);
533         ret = g_strdup_printf ("%s ADPCM", layout_upper);
534         g_free (layout_upper);
535         return ret;
536       }
537     }
538     return g_strdup ("ADPCM");
539   } else if (strcmp (info->type, "audio/mpeg") == 0) {
540     gint ver = 0, layer = 0;
541
542     gst_structure_get_int (s, "mpegversion", &ver);
543
544     switch (ver) {
545       case 1:
546         gst_structure_get_int (s, "layer", &layer);
547         switch (layer) {
548           case 1:
549           case 2:
550           case 3:
551             return g_strdup_printf ("MPEG-1 Layer %d (MP%d)", layer, layer);
552           default:
553             break;
554         }
555         GST_WARNING ("Unexpected MPEG-1 layer in %" GST_PTR_FORMAT, caps);
556         return g_strdup ("MPEG-1 Audio");
557       case 4:
558         return g_strdup ("MPEG-4 AAC");
559       default:
560         break;
561     }
562     GST_WARNING ("Unexpected audio mpegversion in %" GST_PTR_FORMAT, caps);
563     return g_strdup ("MPEG Audio");
564   } else if (strcmp (info->type, "audio/x-pn-realaudio") == 0) {
565     gint ver = 0;
566
567     gst_structure_get_int (s, "raversion", &ver);
568     switch (ver) {
569       case 1:
570         return g_strdup ("RealAudio 14k4bps");
571       case 2:
572         return g_strdup ("RealAudio 28k8bps");
573       case 8:
574         return g_strdup ("RealAudio G2 (Cook)");
575       default:
576         break;
577     }
578     GST_WARNING ("Unexpected raversion in %" GST_PTR_FORMAT, caps);
579     return g_strdup ("RealAudio");
580   } else if (strcmp (info->type, "video/x-pn-realvideo") == 0) {
581     gint ver = 0;
582
583     gst_structure_get_int (s, "rmversion", &ver);
584     switch (ver) {
585       case 1:
586         return g_strdup ("RealVideo 1.0");
587       case 2:
588         return g_strdup ("RealVideo 2.0");
589       case 3:
590         return g_strdup ("RealVideo 3.0");
591       case 4:
592         return g_strdup ("RealVideo 4.0");
593       default:
594         break;
595     }
596     GST_WARNING ("Unexpected rmversion in %" GST_PTR_FORMAT, caps);
597     return g_strdup ("RealVideo");
598   } else if (strcmp (info->type, "video/mpeg") == 0) {
599     gboolean sysstream;
600     gint ver = 0;
601
602     if (!gst_structure_get_boolean (s, "systemstream", &sysstream) ||
603         !gst_structure_get_int (s, "mpegversion", &ver) || ver < 1 || ver > 4) {
604       GST_WARNING ("Missing fields in mpeg video caps %" GST_PTR_FORMAT, caps);
605     } else {
606       if (sysstream) {
607         return g_strdup_printf ("MPEG-%d System Stream", ver);
608       } else {
609         return g_strdup_printf ("MPEG-%d Video", ver);
610       }
611     }
612     return g_strdup ("MPEG Video");
613   } else if (strcmp (info->type, "audio/x-raw") == 0) {
614     gint depth = 0;
615     gboolean is_float;
616     const gchar *str;
617     GstAudioFormat format;
618     const GstAudioFormatInfo *finfo;
619
620     str = gst_structure_get_string (s, "format");
621     format = gst_audio_format_from_string (str);
622     if (format == GST_AUDIO_FORMAT_UNKNOWN)
623       return g_strdup (_("Uncompressed audio"));
624
625     finfo = gst_audio_format_get_info (format);
626     depth = GST_AUDIO_FORMAT_INFO_DEPTH (finfo);
627     is_float = GST_AUDIO_FORMAT_INFO_IS_FLOAT (finfo);
628
629     return g_strdup_printf (_("Raw %d-bit %s audio"), depth,
630         is_float ? "floating-point" : "PCM");
631   }
632   return NULL;
633 }
634
635 /* returns format info structure, will return NULL for dynamic media types! */
636 static const FormatInfo *
637 find_format_info (const GstCaps * caps)
638 {
639   const GstStructure *s;
640   const gchar *media_type;
641   guint i;
642
643   s = gst_caps_get_structure (caps, 0);
644   media_type = gst_structure_get_name (s);
645
646   for (i = 0; i < G_N_ELEMENTS (formats); ++i) {
647     if (strcmp (media_type, formats[i].type) == 0) {
648       gboolean is_sys = FALSE;
649
650       if ((formats[i].flags & FLAG_SYSTEMSTREAM) == 0)
651         return &formats[i];
652
653       /* this record should only be matched if the systemstream field is set */
654       if (gst_structure_get_boolean (s, "systemstream", &is_sys) && is_sys)
655         return &formats[i];
656     }
657   }
658
659   return NULL;
660 }
661
662 static gboolean
663 caps_are_rtp_caps (const GstCaps * caps, const gchar * media, gchar ** format)
664 {
665   const GstStructure *s;
666   const gchar *str;
667
668   g_assert (media != NULL && format != NULL);
669
670   s = gst_caps_get_structure (caps, 0);
671   if (!gst_structure_has_name (s, "application/x-rtp"))
672     return FALSE;
673   if (!gst_structure_has_field_typed (s, "media", G_TYPE_STRING))
674     return FALSE;
675   str = gst_structure_get_string (s, "media");
676   if (str == NULL || !g_str_equal (str, media))
677     return FALSE;
678   str = gst_structure_get_string (s, "encoding-name");
679   if (str == NULL || *str == '\0')
680     return FALSE;
681
682   if (strcmp (str, "X-ASF-PF") == 0) {
683     *format = g_strdup ("Windows Media");
684   } else if (g_str_has_prefix (str, "X-")) {
685     *format = g_strdup (str + 2);
686   } else {
687     *format = g_strdup (str);
688   }
689
690   return TRUE;
691 }
692
693 /**
694  * gst_pb_utils_get_source_description:
695  * @protocol: the protocol the source element needs to handle, e.g. "http"
696  *
697  * Returns a localised string describing a source element handling the protocol
698  * specified in @protocol, for use in error dialogs or other messages to be
699  * seen by the user. Should never return NULL unless @protocol is invalid.
700  *
701  * This function is mainly for internal use, applications would typically
702  * use gst_missing_plugin_message_get_description() to get a description of
703  * a missing feature from a missing-plugin message.
704  *
705  * Returns: a newly-allocated description string, or NULL on error. Free
706  *          string with g_free() when not needed any longer.
707  */
708 gchar *
709 gst_pb_utils_get_source_description (const gchar * protocol)
710 {
711   gchar *proto_uc, *ret;
712
713   g_return_val_if_fail (protocol != NULL, NULL);
714
715   if (strcmp (protocol, "cdda") == 0)
716     return g_strdup (_("Audio CD source"));
717
718   if (strcmp (protocol, "dvd") == 0)
719     return g_strdup (_("DVD source"));
720
721   if (strcmp (protocol, "rtsp") == 0)
722     return g_strdup (_("Real Time Streaming Protocol (RTSP) source"));
723
724   /* TODO: what about mmst, mmsu, mmsh? */
725   if (strcmp (protocol, "mms") == 0)
726     return g_strdup (_("Microsoft Media Server (MMS) protocol source"));
727
728   /* make protocol uppercase */
729   proto_uc = g_ascii_strup (protocol, -1);
730
731   /* TODO: find out how to add a comment for translators to the source code
732    * (and tell them to make the first letter uppercase below if they move
733    * the protocol to the middle or end of the string) */
734   ret = g_strdup_printf (_("%s protocol source"), proto_uc);
735
736   g_free (proto_uc);
737
738   return ret;
739 }
740
741 /**
742  * gst_pb_utils_get_sink_description:
743  * @protocol: the protocol the sink element needs to handle, e.g. "http"
744  *
745  * Returns a localised string describing a sink element handling the protocol
746  * specified in @protocol, for use in error dialogs or other messages to be
747  * seen by the user. Should never return NULL unless @protocol is invalid.
748  *
749  * This function is mainly for internal use, applications would typically
750  * use gst_missing_plugin_message_get_description() to get a description of
751  * a missing feature from a missing-plugin message.
752  *
753  * Returns: a newly-allocated description string, or NULL on error. Free
754  *          string with g_free() when not needed any longer.
755  */
756 gchar *
757 gst_pb_utils_get_sink_description (const gchar * protocol)
758 {
759   gchar *proto_uc, *ret;
760
761   g_return_val_if_fail (protocol != NULL, NULL);
762
763   /* make protocol uppercase */
764   proto_uc = g_ascii_strup (protocol, -1);
765
766   /* TODO: find out how to add a comment for translators to the source code
767    * (and tell them to make the first letter uppercase below if they move
768    * the protocol to the middle or end of the string) */
769   ret = g_strdup_printf ("%s protocol sink", proto_uc);
770
771   g_free (proto_uc);
772
773   return ret;
774 }
775
776 /**
777  * gst_pb_utils_get_decoder_description:
778  * @caps: the (fixed) #GstCaps for which an decoder description is needed
779  *
780  * Returns a localised string describing an decoder for the format specified
781  * in @caps, for use in error dialogs or other messages to be seen by the user.
782  * Should never return NULL unless @factory_name or @caps are invalid.
783  *
784  * This function is mainly for internal use, applications would typically
785  * use gst_missing_plugin_message_get_description() to get a description of
786  * a missing feature from a missing-plugin message.
787  *
788  * Returns: a newly-allocated description string, or NULL on error. Free
789  *          string with g_free() when not needed any longer.
790  */
791 gchar *
792 gst_pb_utils_get_decoder_description (const GstCaps * caps)
793 {
794   gchar *str, *ret;
795   GstCaps *tmp;
796
797   g_return_val_if_fail (caps != NULL, NULL);
798   g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
799
800   tmp = copy_and_clean_caps (caps);
801
802   g_return_val_if_fail (gst_caps_is_fixed (tmp), NULL);
803
804   /* special-case RTP caps */
805   if (caps_are_rtp_caps (tmp, "video", &str)) {
806     ret = g_strdup_printf (_("%s video RTP depayloader"), str);
807   } else if (caps_are_rtp_caps (tmp, "audio", &str)) {
808     ret = g_strdup_printf (_("%s audio RTP depayloader"), str);
809   } else if (caps_are_rtp_caps (tmp, "application", &str)) {
810     ret = g_strdup_printf (_("%s RTP depayloader"), str);
811   } else {
812     const FormatInfo *info;
813
814     str = gst_pb_utils_get_codec_description (tmp);
815     info = find_format_info (tmp);
816     if (info != NULL && (info->flags & FLAG_CONTAINER) != 0) {
817       ret = g_strdup_printf (_("%s demuxer"), str);
818     } else {
819       ret = g_strdup_printf (_("%s decoder"), str);
820     }
821   }
822
823   g_free (str);
824   gst_caps_unref (tmp);
825
826   return ret;
827 }
828
829 /**
830  * gst_pb_utils_get_encoder_description:
831  * @caps: the (fixed) #GstCaps for which an encoder description is needed
832  *
833  * Returns a localised string describing an encoder for the format specified
834  * in @caps, for use in error dialogs or other messages to be seen by the user.
835  * Should never return NULL unless @factory_name or @caps are invalid.
836  *
837  * This function is mainly for internal use, applications would typically
838  * use gst_missing_plugin_message_get_description() to get a description of
839  * a missing feature from a missing-plugin message.
840  *
841  * Returns: a newly-allocated description string, or NULL on error. Free
842  *          string with g_free() when not needed any longer.
843  */
844 gchar *
845 gst_pb_utils_get_encoder_description (const GstCaps * caps)
846 {
847   gchar *str, *ret;
848   GstCaps *tmp;
849
850   g_return_val_if_fail (caps != NULL, NULL);
851   g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
852   tmp = copy_and_clean_caps (caps);
853   g_return_val_if_fail (gst_caps_is_fixed (tmp), NULL);
854
855   /* special-case RTP caps */
856   if (caps_are_rtp_caps (tmp, "video", &str)) {
857     ret = g_strdup_printf (_("%s video RTP payloader"), str);
858   } else if (caps_are_rtp_caps (tmp, "audio", &str)) {
859     ret = g_strdup_printf (_("%s audio RTP payloader"), str);
860   } else if (caps_are_rtp_caps (tmp, "application", &str)) {
861     ret = g_strdup_printf (_("%s RTP payloader"), str);
862   } else {
863     const FormatInfo *info;
864
865     str = gst_pb_utils_get_codec_description (tmp);
866     info = find_format_info (tmp);
867     if (info != NULL && (info->flags & FLAG_CONTAINER) != 0) {
868       ret = g_strdup_printf (_("%s muxer"), str);
869     } else {
870       ret = g_strdup_printf (_("%s encoder"), str);
871     }
872   }
873
874   g_free (str);
875   gst_caps_unref (tmp);
876
877   return ret;
878 }
879
880 /**
881  * gst_pb_utils_get_element_description:
882  * @factory_name: the name of the element, e.g. "giosrc"
883  *
884  * Returns a localised string describing the given element, for use in
885  * error dialogs or other messages to be seen by the user. Should never
886  * return NULL unless @factory_name is invalid.
887  *
888  * This function is mainly for internal use, applications would typically
889  * use gst_missing_plugin_message_get_description() to get a description of
890  * a missing feature from a missing-plugin message.
891  *
892  * Returns: a newly-allocated description string, or NULL on error. Free
893  *          string with g_free() when not needed any longer.
894  */
895 gchar *
896 gst_pb_utils_get_element_description (const gchar * factory_name)
897 {
898   gchar *ret;
899
900   g_return_val_if_fail (factory_name != NULL, NULL);
901
902   ret = g_strdup_printf (_("GStreamer element %s"), factory_name);
903   if (ret && g_str_has_prefix (ret, factory_name))
904     *ret = g_ascii_toupper (*ret);
905
906   return ret;
907 }
908
909 /**
910  * gst_pb_utils_add_codec_description_to_tag_list:
911  * @taglist: a #GstTagList
912  * @codec_tag: a GStreamer codec tag such as #GST_TAG_AUDIO_CODEC,
913  *             #GST_TAG_VIDEO_CODEC or #GST_TAG_CODEC
914  * @caps: the (fixed) #GstCaps for which a codec tag should be added.
915  *
916  * Adds a codec tag describing the format specified by @caps to @taglist.
917  *
918  * Returns: TRUE if a codec tag was added, FALSE otherwise.
919  */
920 gboolean
921 gst_pb_utils_add_codec_description_to_tag_list (GstTagList * taglist,
922     const gchar * codec_tag, const GstCaps * caps)
923 {
924   const FormatInfo *info;
925   gchar *desc;
926
927   g_return_val_if_fail (taglist != NULL, FALSE);
928   g_return_val_if_fail (GST_IS_TAG_LIST (taglist), FALSE);
929   g_return_val_if_fail (codec_tag != NULL, FALSE);
930   g_return_val_if_fail (gst_tag_exists (codec_tag), FALSE);
931   g_return_val_if_fail (gst_tag_get_type (codec_tag) == G_TYPE_STRING, FALSE);
932   g_return_val_if_fail (caps != NULL, FALSE);
933   g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
934
935   info = find_format_info (caps);
936   if (info == NULL)
937     return FALSE;
938
939   desc = format_info_get_desc (info, caps);
940   gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, codec_tag, desc, NULL);
941   g_free (desc);
942
943   return TRUE;
944 }
945
946 /**
947  * gst_pb_utils_get_codec_description:
948  * @caps: the (fixed) #GstCaps for which an format description is needed
949  *
950  * Returns a localised (as far as this is possible) string describing the
951  * media format specified in @caps, for use in error dialogs or other messages
952  * to be seen by the user. Should never return NULL unless @caps is invalid.
953  *
954  * Also see the convenience function
955  * gst_pb_utils_add_codec_description_to_tag_list().
956  *
957  * Returns: a newly-allocated description string, or NULL on error. Free
958  *          string with g_free() when not needed any longer.
959  */
960 gchar *
961 gst_pb_utils_get_codec_description (const GstCaps * caps)
962 {
963   const FormatInfo *info;
964   gchar *str, *comma;
965   GstCaps *tmp;
966
967   g_return_val_if_fail (caps != NULL, NULL);
968   g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
969   tmp = copy_and_clean_caps (caps);
970   g_return_val_if_fail (gst_caps_is_fixed (tmp), NULL);
971
972   info = find_format_info (tmp);
973
974   if (info) {
975     str = format_info_get_desc (info, tmp);
976   } else {
977     str = gst_caps_to_string (tmp);
978
979     /* cut off everything after the media type, if there is anything */
980     if ((comma = strchr (str, ','))) {
981       *comma = '\0';
982       g_strchomp (str);
983       /* we could do something more elaborate here, like taking into account
984        * audio/, video/, image/ and application/ prefixes etc. */
985     }
986
987     GST_WARNING ("No description available for media type: %s", str);
988   }
989   gst_caps_unref (tmp);
990
991   return str;
992 }
993
994 #if 0
995 void
996 gst_pb_utils_list_all (void)
997 {
998   gint i;
999
1000   g_print ("static const gchar *caps_strings[] = { ");
1001
1002   for (i = 0; i < G_N_ELEMENTS (formats); ++i) {
1003     if (formats[i].desc != NULL)
1004       g_print ("  \"%s\", ", formats[i].type);
1005   }
1006   g_print ("\n#if 0\n");
1007   for (i = 0; i < G_N_ELEMENTS (formats); ++i) {
1008     if (formats[i].desc == NULL)
1009       g_print ("  \"%s\", \n", formats[i].type);
1010   }
1011   g_print ("\n#endif\n");
1012 }
1013 #endif