pbutils: descriptions: add meta/x-klv
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-base / 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., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 /**
21  * SECTION:gstpbutilsdescriptions
22  * @title: Descriptions
23  * @short_description: Provides human-readable descriptions for caps/codecs
24  * and encoder, decoder, URI source and URI sink elements
25  *
26  * The above functions provide human-readable strings for media formats
27  * and decoder/demuxer/depayloader/encoder/muxer/payloader elements for use
28  * in error dialogs or other messages shown to users.
29  *
30  * gst_pb_utils_add_codec_description_to_tag_list() is a utility function
31  * for demuxer and decoder elements to add audio/video codec tags from a
32  * given (fixed) #GstCaps.
33  *
34  */
35
36 #ifdef HAVE_CONFIG_H
37 # include "config.h"
38 #endif
39
40 #include <glib/gi18n-lib.h>
41
42 #include <gst/audio/audio.h>
43 #include <gst/video/video.h>
44
45 #include "pbutils.h"
46 #include "pbutils-private.h"
47
48 #include <string.h>
49
50 typedef enum
51 {
52   FLAG_SYSTEMSTREAM = (1 << 0), /* match record only if caps have systemstream=true   */
53   FLAG_CONTAINER = (1 << 1),    /* format is a container format (muxed)               */
54   FLAG_AUDIO = (1 << 2),        /* format is an audio format, or audio container/tag  */
55   FLAG_VIDEO = (1 << 3),        /* format is a video format, or video container/tag   */
56   FLAG_IMAGE = (1 << 4),        /* format is an image format, or image container/tag  */
57   FLAG_SUB = (1 << 5),          /* format is a subtitle format, or subtitle container */
58   FLAG_TAG = (1 << 6),          /* format is a tag/container                          */
59   FLAG_GENERIC = (1 << 7),      /* format is a generic container (e.g. multipart)     */
60   FLAG_METADATA = (1 << 8),     /* format is a metadata format, or metadata container/tag */
61 } FormatFlags;
62
63 typedef struct
64 {
65   const gchar *type;
66   const gchar *desc;
67   FormatFlags flags:24;
68   gchar ext[5];                 /* file extension */
69 } FormatInfo;
70
71 #define AV_CONTAINER    (FLAG_CONTAINER | FLAG_AUDIO | FLAG_VIDEO)
72 #define AVS_CONTAINER   (AV_CONTAINER | FLAG_SUB)
73 #define AVI_CONTAINER   (AV_CONTAINER | FLAG_IMAGE)
74 #define AVIS_CONTAINER  (AV_CONTAINER | FLAG_IMAGE | FLAG_SUB)
75 #define AUDIO_CONTAINER (FLAG_CONTAINER | FLAG_AUDIO)
76 #define VIDEO_CONTAINER (FLAG_CONTAINER | FLAG_VIDEO)
77 #define AUDIO_TAG       (AUDIO_CONTAINER | FLAG_TAG)
78
79 static const FormatInfo formats[] = {
80   /* container/tag formats with static descriptions */
81   /* FIXME: does anyone use oga in practice? */
82   {"audio/ogg", "Ogg", AUDIO_CONTAINER, "ogg"},
83   {"audio/webm", "WebM", AUDIO_CONTAINER, "webm"},
84   {"audio/x-matroska", "Matroska", AUDIO_CONTAINER, "mka"},
85   {"application/gxf", "General Exchange Format (GXF)", AVI_CONTAINER, "gxf"},
86   {"application/ogg", "Ogg", AVIS_CONTAINER, "ogg"},
87   {"application/kate", "Ogg", FLAG_CONTAINER | FLAG_SUB, "ogg"},
88   {"application/mxf", "Material eXchange Format (MXF)", AVIS_CONTAINER, "mxf"},
89   {"application/vnd.rn-realmedia", "Realmedia", AV_CONTAINER, "rm"},
90   {"application/x-id3", N_("ID3 tag"), AUDIO_TAG, ""},
91   {"application/x-ape", N_("APE tag"), AUDIO_TAG, ""},
92   {"application/x-apetag", N_("APE tag"), AUDIO_TAG, ""},
93   {"application/x-icy", N_("ICY internet radio"), AUDIO_TAG, ""},
94   {"application/x-3gp", "3GP", AV_CONTAINER, "3gp"},
95   {"application/x-pn-realaudio", "RealAudio", AUDIO_CONTAINER, "ra"},
96   {"application/x-yuv4mpeg", "Y4M", VIDEO_CONTAINER, "y4m"},
97   {"multipart/x-mixed-replace", "Multipart", FLAG_CONTAINER | FLAG_GENERIC, ""},
98   {"video/ogg", "Ogg", AVIS_CONTAINER, "ogv"},
99   {"video/x-fli", "FLI/FLC/FLX Animation", VIDEO_CONTAINER, "fli"},
100   {"video/x-flv", "Flash", AV_CONTAINER, "flv"},
101   {"video/x-matroska", "Matroska", AVIS_CONTAINER, "mkv"},
102   /* FIXME: does anyone use .mk3d in practice, rather than .mkv? */
103   {"video/x-matroska-3d", "Matroska", AVIS_CONTAINER, "mk3d"},
104   {"video/webm", "WebM", AVS_CONTAINER, "webm"},
105   {"video/x-ms-asf", "Advanced Streaming Format (ASF)", AVIS_CONTAINER, "asf"},
106   {"video/x-msvideo", "Audio Video Interleave (AVI)", AVIS_CONTAINER, "avi"},
107   {"video/x-quicktime", "Quicktime", AVIS_CONTAINER, "mov"},
108   {"video/quicktime", "Quicktime", AVIS_CONTAINER, "mov"},
109   {"video/mj2", "Motion JPEG 2000", AVIS_CONTAINER, "mj2"},
110
111   /* audio formats with static descriptions */
112   {"audio/x-ac3", "AC-3 (ATSC A/52)", FLAG_AUDIO, "ac3"},
113   {"audio/ac3", "AC-3 (ATSC A/52)", FLAG_AUDIO, "ac3"},
114   {"audio/x-private-ac3", "DVD AC-3 (ATSC A/52)", FLAG_AUDIO, "ac3"},
115   {"audio/x-private1-ac3", "DVD AC-3 (ATSC A/52)", FLAG_AUDIO, "ac3"},
116   {"audio/x-alaw", "A-Law", FLAG_AUDIO, ""},
117   {"audio/amr", "Adaptive Multi Rate (AMR)", FLAG_AUDIO, "amr"},
118   {"audio/AMR", "Adaptive Multi Rate (AMR)", FLAG_AUDIO, "amr"},
119   {"audio/AMR-WB", "Adaptive Multi Rate WideBand (AMR-WB)", FLAG_AUDIO, "amr"},
120   {"audio/iLBC-sh", "Internet Low Bitrate Codec (iLBC)", AUDIO_CONTAINER,
121       "ilbc"},
122   {"audio/ms-gsm", "MS GSM", FLAG_AUDIO, "gsm"},
123   {"audio/qcelp", "QCELP", FLAG_AUDIO, ""},
124   {"audio/aiff", "Audio Interchange File Format (AIFF)", AUDIO_CONTAINER,
125       "aiff"},
126   {"audio/x-aiff", "Audio Interchange File Format (AIFF)", AUDIO_CONTAINER,
127       "aiff"},
128   {"audio/x-alac", N_("Apple Lossless Audio (ALAC)"), FLAG_AUDIO, ""},
129   {"audio/x-amr-nb-sh", "Adaptive Multi Rate NarrowBand (AMR-NB)",
130       AUDIO_CONTAINER, "amr"},
131   {"audio/x-amr-wb-sh", "Adaptive Multi Rate WideBand (AMR-WB)",
132       AUDIO_CONTAINER, "amr"},
133   {"audio/x-au", "Sun .au", AUDIO_CONTAINER, "au"},
134   {"audio/x-audible", "Audible Audio", AUDIO_CONTAINER, "aa"},
135   {"audio/x-caf", "Apple Core Audio Format", AUDIO_CONTAINER, "caf"},
136   {"audio/x-celt", "Constrained Energy Lapped Transform (CELT)", FLAG_AUDIO,
137       ""},
138   {"audio/x-cinepak", "Cinepak Audio", FLAG_AUDIO, ""},
139   {"audio/x-dpcm", "DPCM", FLAG_AUDIO, ""},
140   {"audio/x-dts", "DTS", FLAG_AUDIO, "dts"},
141   {"audio/x-private1-dts", "DTS", FLAG_AUDIO, "dts"},
142   {"audio/x-dv", "DV Audio", FLAG_AUDIO, ""},
143   {"audio/x-eac3", "E-AC-3 (ATSC A/52B)", FLAG_AUDIO, "eac3"},
144   {"audio/x-flac", N_("Free Lossless Audio Codec (FLAC)"), FLAG_AUDIO, "flac"},
145   {"audio/x-gsm", "GSM", FLAG_AUDIO, "gsm"},
146   {"audio/x-iec958", "S/PDIF IEC958", 0, ""},   /* TODO: check description */
147   {"audio/x-iLBC", "Internet Low Bitrate Codec (iLBC)", FLAG_AUDIO, "ilbc"},
148   {"audio/x-ircam", "Berkeley/IRCAM/CARL", FLAG_AUDIO, ""},
149   {"audio/x-lpcm", "LPCM", FLAG_AUDIO, ""},
150   {"audio/x-private1-lpcm", "DVD LPCM", FLAG_AUDIO, ""},
151   {"audio/x-m4a", "MPEG-4 AAC", FLAG_CONTAINER, "m4a"},
152   {"audio/x-mod", "Module Music Format (MOD)", FLAG_AUDIO, "mod"},
153   {"audio/x-mulaw", "Mu-Law", FLAG_AUDIO, ""},
154   {"audio/x-musepack", "Musepack (MPC)", FLAG_AUDIO, "mpc"},
155   {"audio/x-nellymoser", "Nellymoser Asao", FLAG_AUDIO, ""},
156   {"audio/x-nist", "Sphere NIST", FLAG_AUDIO, ""},
157   {"audio/x-nsf", "Nintendo NSF", FLAG_AUDIO, ""},
158   {"audio/x-opus", "Opus", FLAG_AUDIO, ""},
159   {"audio/x-paris", "Ensoniq PARIS", FLAG_AUDIO, ""},
160   {"audio/x-qdm", "QDesign Music (QDM)", FLAG_AUDIO, ""},
161   {"audio/x-qdm2", "QDesign Music (QDM) 2", FLAG_AUDIO, ""},
162   {"audio/x-ralf-mpeg4-generic", "Real Audio Lossless (RALF)", FLAG_AUDIO, ""},
163   {"audio/x-rf64", "Broadcast Wave Format", AUDIO_CONTAINER, "rf64"},
164   {"audio/x-sbc", "Low Complexity Subband Coding", FLAG_AUDIO, "sbc"},
165   {"audio/x-sds", "Midi Sample Dump Standard", FLAG_AUDIO, ""},
166   {"audio/x-shorten", "Shorten Lossless", FLAG_AUDIO, "shn"},
167   {"audio/x-sid", "Sid", FLAG_AUDIO, "sid"},
168   {"audio/x-sipro", "Sipro/ACELP.NET Voice", FLAG_AUDIO, ""},
169   {"audio/x-siren", "Siren", FLAG_AUDIO, ""},
170   {"audio/x-spc", "SNES-SPC700 Sound File Data", FLAG_AUDIO, "spc"},
171   {"audio/x-speex", "Speex", FLAG_AUDIO, ""},
172   {"audio/x-svx", "Amiga IFF / SVX8 / SV16", FLAG_AUDIO, ""},
173   {"audio/x-true-hd", "Dolby TrueHD", FLAG_AUDIO, ""},
174   {"audio/x-tta", N_("Lossless True Audio (TTA)"), FLAG_AUDIO, "tta"},
175   {"audio/x-ttafile", N_("Lossless True Audio (TTA)"), FLAG_AUDIO, "tta"},
176   {"audio/x-vnd.sony.atrac3", "Sony ATRAC3", FLAG_AUDIO, ""},
177   {"audio/x-vorbis", "Vorbis", FLAG_AUDIO, ""},
178   {"audio/x-voc", "SoundBlaster VOC", FLAG_AUDIO, ""},
179   {"audio/x-w64", "Sonic Foundry Wave64", AUDIO_CONTAINER, "w64"},
180   {"audio/x-wav", "WAV", AUDIO_CONTAINER, "wav"},
181   {"audio/x-wavpack", "Wavpack", FLAG_AUDIO, "wp"},
182   {"audio/x-wavpack-correction", "Wavpack", 0, "wpc"},
183   {"audio/x-wms", N_("Windows Media Speech"), FLAG_AUDIO, ""},
184   {"audio/x-voxware", "Voxware", FLAG_AUDIO, ""},
185   {"audio/x-xi", "Fasttracker 2 Extended Instrument", FLAG_AUDIO, "xi"},
186
187
188   /* video formats with static descriptions */
189   {"video/sp5x", "Sunplus JPEG 5.x", FLAG_VIDEO, ""},
190   {"video/vivo", "Vivo", FLAG_VIDEO, ""},
191   {"video/x-4xm", "4X Technologies Video", FLAG_VIDEO, ""},
192   {"video/x-apple-video", "Apple video", FLAG_VIDEO, ""},
193   {"video/x-aasc", "Autodesk Animator", FLAG_VIDEO, ""},
194   {"video/x-av1", "AV1", FLAG_VIDEO, ""},
195   {"video/x-camtasia", "TechSmith Camtasia", FLAG_VIDEO, ""},
196   {"video/x-cavs", "Chinese AVS (CAVS)", FLAG_VIDEO, ""},
197   {"video/x-cdxa", "RIFF/CDXA (VCD)", AV_CONTAINER, ""},
198   {"video/x-cineform", "CineForm", FLAG_VIDEO, ""},
199   {"video/x-cinepak", "Cinepak Video", FLAG_VIDEO, ""},
200   {"video/x-cirrus-logic-accupak", "Cirrus Logipak AccuPak", FLAG_VIDEO, ""},
201   {"video/x-compressed-yuv", N_("CYUV Lossless"), FLAG_VIDEO, ""},
202   {"video/x-dnxhd", "Digital Nonlinear Extensible High Definition (DNxHD)",
203       FLAG_VIDEO, ""},
204   {"subpicture/x-dvd", "DVD subpicture", FLAG_VIDEO, ""},
205   {"video/x-ffv", N_("FFMpeg v1"), FLAG_VIDEO, ""},
206   {"video/x-flash-screen", "Flash Screen Video", FLAG_VIDEO, ""},
207   {"video/x-flash-video", "Sorenson Spark Video", FLAG_VIDEO, ""},
208   {"video/x-h261", "H.261", FLAG_VIDEO, ""},
209   {"video/x-huffyuv", "Huffyuv", FLAG_VIDEO, ""},
210   {"video/x-intel-h263", "Intel H.263", FLAG_VIDEO, ""},
211   {"video/x-jpeg", "Motion JPEG", FLAG_VIDEO, ""},
212   /* { "video/x-jpeg-b", "", 0 }, does this actually exist? */
213   {"video/x-loco", "LOCO Lossless", FLAG_VIDEO, ""},
214   {"video/x-mimic", "MIMIC", FLAG_VIDEO, ""},
215   {"video/x-mjpeg", "Motion-JPEG", FLAG_VIDEO, ""},
216   {"video/x-mjpeg-b", "Motion-JPEG format B", FLAG_VIDEO, ""},
217   {"video/mpegts", "MPEG-2 Transport Stream", AVS_CONTAINER, "ts"},
218   {"video/x-mng", "Multiple Image Network Graphics (MNG)", FLAG_VIDEO, ""},
219   {"video/x-mszh", N_("Lossless MSZH"), FLAG_VIDEO, ""},
220   {"video/x-msvideocodec", "Microsoft Video 1", FLAG_VIDEO, ""},
221   {"video/x-mve", "Interplay MVE", AV_CONTAINER, "mve"},
222   {"video/x-nut", "NUT", AV_CONTAINER, "nut"},
223   {"video/x-nuv", "MythTV NuppelVideo (NUV)", AV_CONTAINER, "nuv"},
224   {"video/x-prores", "Apple ProRes", FLAG_VIDEO, ""},
225   {"video/x-qdrw", "Apple QuickDraw", FLAG_VIDEO, ""},
226   {"video/x-smc", "Apple SMC", FLAG_VIDEO, ""},
227   {"video/x-smoke", "Smoke", FLAG_VIDEO, ""},
228   {"video/x-tarkin", "Tarkin", FLAG_VIDEO, ""},
229   {"video/x-theora", "Theora", FLAG_VIDEO, ""},
230   {"video/x-rle", N_("Run-length encoding"), FLAG_VIDEO, ""},
231   {"video/x-ultimotion", "IBM UltiMotion", FLAG_VIDEO, ""},
232   {"video/x-vcd", "VideoCD (VCD)", 0},
233   {"video/x-vmnc", "VMWare NC", FLAG_VIDEO, ""},
234   {"video/x-vp3", "On2 VP3", FLAG_VIDEO, ""},
235   {"video/x-vp5", "On2 VP5", FLAG_VIDEO, ""},
236   {"video/x-vp6", "On2 VP6", FLAG_VIDEO, ""},
237   {"video/x-vp6-flash", "On2 VP6/Flash", FLAG_VIDEO, ""},
238   {"video/x-vp6-alpha", "On2 VP6 with alpha", FLAG_VIDEO, ""},
239   {"video/x-vp7", "On2 VP7", FLAG_VIDEO, ""},
240   {"video/x-vp8", "VP8", FLAG_VIDEO, ""},
241   {"video/x-vp9", "VP9", FLAG_VIDEO, ""},
242   {"video/x-zlib", "Lossless zlib video", FLAG_VIDEO, ""},
243   {"video/x-zmbv", "Zip Motion Block video", FLAG_VIDEO, ""},
244
245   /* image formats with static descriptions */
246   {"image/bmp", "BMP", FLAG_IMAGE, "bmp"},
247   {"image/x-bmp", "BMP", FLAG_IMAGE, "bmp"},
248   {"image/x-MS-bmp", "BMP", FLAG_IMAGE, "bmp"},
249   {"image/gif", "GIF", FLAG_IMAGE, "gif"},
250   {"image/jpeg", "JPEG", FLAG_IMAGE | FLAG_VIDEO, "jpg"},
251   {"image/jng", "JPEG Network Graphics (JNG)", FLAG_IMAGE, ""},
252   {"image/png", "PNG", FLAG_VIDEO | FLAG_IMAGE, "png"},
253   {"image/pbm", "Portable BitMap (PBM)", FLAG_IMAGE, "pbm"},
254   {"image/ppm", "Portable PixMap (PPM)", FLAG_IMAGE, "ppm"},
255   {"image/svg+xml", "Scalable Vector Graphics (SVG)", FLAG_IMAGE, "svg"},
256   {"image/tiff", "TIFF", FLAG_IMAGE, "tiff"},
257   {"image/x-cmu-raster", "CMU Raster Format", FLAG_IMAGE, ""},
258   {"image/x-degas", "DEGAS", FLAG_IMAGE, ""},
259   {"image/x-icon", "ICO", FLAG_IMAGE, "ico"},
260   {"image/x-j2c", "JPEG 2000", FLAG_VIDEO | FLAG_IMAGE, ""},
261   {"image/x-jpc", "JPEG 2000", FLAG_VIDEO | FLAG_IMAGE, ""},
262   {"image/jp2", "JPEG 2000", FLAG_VIDEO | FLAG_IMAGE, ""},
263   {"image/x-pcx", "PCX", FLAG_IMAGE, ""},
264   {"image/x-xcf", "XFC", FLAG_IMAGE, ""},
265   {"image/x-pixmap", "XPM", FLAG_IMAGE, "xpm"},
266   {"image/x-portable-anymap", "Portable AnyMap (PNM)", FLAG_IMAGE, "pnm"},
267   {"image/x-portable-graymap", "Portable GrayMap (PGM)", FLAG_IMAGE, "pgm"},
268   {"image/x-xpixmap", "XPM", FLAG_IMAGE, "xpm"},
269   {"image/x-quicktime", "QuickTime Image Format (QTIF)",
270       FLAG_IMAGE | FLAG_CONTAINER, ".mov"},
271   {"image/x-sun-raster", "Sun Raster Format (RAS)", FLAG_IMAGE, ""},
272   {"image/x-tga", "TGA", FLAG_IMAGE, "tga"},
273   {"image/vnd.wap.wbmp", "Wireless Bitmap", FLAG_IMAGE, "wbmp"},
274
275   /* subtitle formats with static descriptions */
276   {"text/x-raw", N_("Timed Text"), FLAG_SUB, ""},
277   {"application/x-ssa", "SubStation Alpha", FLAG_SUB, ""},
278   {"application/x-ass", "Advanced SubStation Alpha", FLAG_SUB, ""},
279   /* FIXME: add variant field to typefinder? */
280   {"application/x-subtitle", N_("Subtitle"), FLAG_SUB, ""},
281   {"application/x-subtitle-mpl2", N_("MPL2 subtitle format"), FLAG_SUB, ""},
282   {"application/x-subtitle-dks", N_("DKS subtitle format"), FLAG_SUB, ""},
283   {"application/x-subtitle-qttext", N_("QTtext subtitle format"), FLAG_SUB, ""},
284   {"application/x-subtitle-sami", N_("Sami subtitle format"), FLAG_SUB, ""},
285   {"application/x-subtitle-tmplayer", N_("TMPlayer subtitle format"), FLAG_SUB,
286       ""},
287   {"application/x-teletext", "Teletext", 0, ""},
288   {"application/x-kate", "Kate", 0, ""},
289   {"closedcaption/x-cea-608", N_("CEA 608 Closed Caption"), FLAG_SUB, ""},
290   {"closedcaption/x-cea-708", N_("CEA 708 Closed Caption"), FLAG_SUB, ""},
291   {"subtitle/x-kate", N_("Kate subtitle format"), FLAG_SUB, ""},
292   {"application/x-subtitle-vtt", N_("WebVTT subtitle format"), FLAG_SUB, ""},
293   {"subpicture/x-dvb", "DVB subtitles", FLAG_SUB, ""},
294   {"subpicture/x-pgs", "PGS subtitles", FLAG_SUB, ""},
295   {"subpicture/x-xsub", "XSUB subtitles", FLAG_SUB, ""},
296
297   /* non-audio/video/container formats */
298   {"hdv/aux-v", "HDV AUX-V", 0, ""},
299   {"hdv/aux-a", "HDV AUX-A", 0, ""},
300
301   /* formats with dynamic descriptions */
302   {"audio/mpeg", NULL, FLAG_AUDIO, ""},
303   {"audio/x-adpcm", NULL, FLAG_AUDIO, ""},
304   {"audio/x-mace", NULL, FLAG_AUDIO, ""},
305   {"audio/x-pn-realaudio", NULL, FLAG_AUDIO, ""},
306   {"audio/x-raw", NULL, FLAG_AUDIO, ""},
307   {"audio/x-wma", NULL, FLAG_AUDIO, ""},
308   {"video/mpeg", NULL, AVS_CONTAINER | FLAG_SYSTEMSTREAM, "mpg"},
309   {"video/mpeg", NULL, FLAG_VIDEO, ""},
310   {"video/x-asus", NULL, FLAG_VIDEO, ""},
311   {"video/x-ati-vcr", NULL, FLAG_VIDEO, ""},
312   {"video/x-dirac", NULL, FLAG_VIDEO, ""},
313   {"video/x-divx", NULL, FLAG_VIDEO, ""},
314   {"video/x-dv", "Digital Video (DV) System Stream",
315       FLAG_CONTAINER | FLAG_SYSTEMSTREAM, "dv"},
316   {"video/x-dv", "Digital Video (DV)", FLAG_VIDEO, ""},
317   {"video/x-h263", NULL, FLAG_VIDEO, "h263"},
318   {"video/x-h264", NULL, FLAG_VIDEO, "h264"},
319   {"video/x-h265", NULL, FLAG_VIDEO, "h265"},
320   {"video/x-indeo", NULL, FLAG_VIDEO, ""},
321   {"video/x-msmpeg", NULL, FLAG_VIDEO, ""},
322   {"video/x-pn-realvideo", NULL, FLAG_VIDEO, ""},
323 #if 0
324   /* do these exist? are they used anywhere? */
325   {"video/x-pn-multirate-realvideo", NULL, 0},
326   {"audio/x-pn-multirate-realaudio", NULL, 0},
327   {"audio/x-pn-multirate-realaudio-live", NULL, 0},
328 #endif
329   {"video/x-truemotion", NULL, FLAG_VIDEO, ""},
330   {"video/x-raw", NULL, FLAG_VIDEO, ""},
331   {"video/x-svq", NULL, FLAG_VIDEO, ""},
332   {"video/x-wmv", NULL, FLAG_VIDEO, ""},
333   {"video/x-xan", NULL, FLAG_VIDEO, ""},
334   {"video/x-tscc", NULL, FLAG_VIDEO, ""},
335   /* metadata */
336   {"application/x-onvif-metadata", "ONVIF Timed Metadata", FLAG_METADATA, ""},
337   {"meta/x-klv", "KLV Metadata", FLAG_METADATA, ""},
338 };
339
340 static const gchar *
341 pbutils_desc_get_profile_name_from_nick (const gchar * map, gsize map_len,
342     const gchar * nick)
343 {
344   const gchar *end = map + map_len;
345   const gchar *p;
346
347   p = map;
348   while (*p != '\0' && p < end) {
349     guint len = strlen (p);
350
351     if (strcmp (p, nick) == 0)
352       return p + len + 1;
353     p += len + 1;
354     p += strlen (p) + 1;
355   }
356   return NULL;
357 }
358
359 static const gchar *
360 pbutils_desc_get_mpeg2v_profile_name_from_nick (const gchar * nick)
361 {
362   static const gchar map[] =
363       "simple\000Simple\000main\000Main\000high\000High\000";
364
365   return pbutils_desc_get_profile_name_from_nick (map, sizeof (map), nick);
366 }
367
368 static const gchar *
369 pbutils_desc_get_mpeg4v_profile_name_from_nick (const gchar * nick)
370 {
371   static const gchar map[] = "simple\000Simple\000"
372       "simple-scalable\000Simple Scalable\000"
373       "core\000Core\000"
374       "main\000Main\000"
375       "n-bit\000N-bit\000"
376       "scalable\000Scalable\000"
377       "hybrid\000Hybrid\000"
378       "advanced-real-time-simple\000Advanced Real-Time Simple\000"
379       "core-scalable\000Core-Scalable\000"
380       "advanced-coding-efficiency\000Advanced Coding Efficiency\000"
381       "advanced-core\000Advanced Core\000"
382       "advanced-scalable-texture\000Advanced Scalable Texture\000"
383       "simple-face\000Simple Face Animation\000"
384       "simple-fba\000Simple FBA\000"
385       "simple-studio\000Simple Studio\000"
386       "core-studio\000Core Studio\000"
387       "advanced-simple\000Advanced Simple\000"
388       "fine-granularity-scalable\000Fine Granularity Scalable\000"
389       "basic-animated-texture\000Basic Animated Texture\000"
390       "baseline\000Baseline Profile\000";
391
392   return pbutils_desc_get_profile_name_from_nick (map, sizeof (map), nick);
393 }
394
395 static const gchar *
396 pbutils_desc_get_h264_profile_name_from_nick (const gchar * nick)
397 {
398   static const gchar map[] = "baseline\000Baseline\000"
399       "constrained-baseline\000Constrained Baseline\000"
400       "main\000Main\000"
401       "extended\000Extended\000"
402       "high\000High\000"
403       "high-10-intra\000High 10 Intra\000"
404       "high-10\000High 10\000"
405       "high-4:2:2-intra\000High 4:2:2 Intra\000"
406       "high-4:2:2\000High 4:2:2\000"
407       "high-4:4:4-intra\000High 4:4:4 Intra\000"
408       "high-4:4:4\000High 4:4:4\000"
409       "cavlc-4:4:4-intra\000CAVLC 4:4:4 Intra\000"
410       "multiview-high\000Multiview High\000"
411       "stereo-high\000Stereo High\000"
412       "scalable-constrained-baseline\000Scalable Constrained Baseline\000"
413       "scalable-baseline\000Scalable Baseline\000"
414       "scalable-high\000Scalable High\000";
415
416   return pbutils_desc_get_profile_name_from_nick (map, sizeof (map), nick);
417 }
418
419 static const gchar *
420 pbutils_desc_get_h265_profile_name_from_nick (const gchar * nick)
421 {
422   static const gchar map[] = "main\000Main\000"
423       "main-10\000Main 10\000"
424       "main-12\000Main 12\000"
425       "main-4:2:2-10\000Main 4:2:2 10\000"
426       "main-4:2:2-12\000Main 4:2:2 12\000"
427       "main-4:4:4\000Main 4:4:4\000"
428       "main-4:4:4-10\000Main 4:4:4 10\000"
429       "main-4:4:4-12\000Main 4:4:4 12\000"
430       "main-4:4:4-16-intra\000Main 4:4:4 16 Intra\000"
431       "main-still-picture\000Main Still Picture\000";
432
433   return pbutils_desc_get_profile_name_from_nick (map, sizeof (map), nick);
434 }
435
436 /* returns static descriptions and dynamic ones (such as video/x-raw),
437  * or NULL if caps aren't known at all */
438 static gchar *
439 format_info_get_desc (const FormatInfo * info, const GstCaps * caps)
440 {
441   const GstStructure *s;
442
443   g_assert (info != NULL);
444
445   gst_pb_utils_init_locale_text_domain ();
446
447   if (info->desc != NULL)
448     return g_strdup (_(info->desc));
449
450   s = gst_caps_get_structure (caps, 0);
451
452   if (strcmp (info->type, "video/x-raw") == 0) {
453     gchar *ret = NULL;
454     const gchar *str = 0;
455     GstVideoFormat format;
456     const GstVideoFormatInfo *finfo;
457
458     str = gst_structure_get_string (s, "format");
459     if (str == NULL)
460       return g_strdup (_("Uncompressed video"));
461     format = gst_video_format_from_string (str);
462     if (format == GST_VIDEO_FORMAT_UNKNOWN)
463       return g_strdup (_("Uncompressed video"));
464
465     finfo = gst_video_format_get_info (format);
466
467     if (GST_VIDEO_FORMAT_INFO_IS_GRAY (finfo)) {
468       ret = g_strdup (_("Uncompressed gray"));
469     } else if (GST_VIDEO_FORMAT_INFO_IS_YUV (finfo)) {
470       const gchar *subs;
471       gint w_sub, h_sub, n_semi;
472
473       w_sub = GST_VIDEO_FORMAT_INFO_W_SUB (finfo, 1);
474       h_sub = GST_VIDEO_FORMAT_INFO_H_SUB (finfo, 1);
475
476       if (w_sub == 1 && h_sub == 1) {
477         subs = "4:4:4";
478       } else if (w_sub == 2 && h_sub == 1) {
479         subs = "4:2:2";
480       } else if (w_sub == 2 && h_sub == 2) {
481         subs = "4:2:0";
482       } else if (w_sub == 4 && h_sub == 1) {
483         subs = "4:1:1";
484       } else {
485         subs = "";
486       }
487
488       n_semi = GST_VIDEO_FORMAT_INFO_HAS_ALPHA (finfo) ? 3 : 2;
489
490       if (GST_VIDEO_FORMAT_INFO_N_PLANES (finfo) == 1) {
491         ret = g_strdup_printf (_("Uncompressed packed YUV %s"), subs);
492       } else if (GST_VIDEO_FORMAT_INFO_N_PLANES (finfo) == n_semi) {
493         ret = g_strdup_printf (_("Uncompressed semi-planar YUV %s"), subs);
494       } else {
495         ret = g_strdup_printf (_("Uncompressed planar YUV %s"), subs);
496       }
497     } else if (GST_VIDEO_FORMAT_INFO_IS_RGB (finfo)) {
498       gboolean alpha, palette;
499       gint bits;
500
501       alpha = GST_VIDEO_FORMAT_INFO_HAS_ALPHA (finfo);
502       palette = GST_VIDEO_FORMAT_INFO_HAS_PALETTE (finfo);
503       bits = GST_VIDEO_FORMAT_INFO_BITS (finfo);
504
505       if (palette) {
506         ret = g_strdup_printf (_("Uncompressed palettized %d-bit %s"),
507             bits, alpha ? "RGBA" : "RGB");
508       } else {
509         ret = g_strdup_printf (_("Uncompressed %d-bit %s"),
510             bits, alpha ? "RGBA" : "RGB");
511       }
512     } else {
513       ret = g_strdup (_("Uncompressed video"));
514     }
515     return ret;
516   } else if (strcmp (info->type, "video/x-h263") == 0) {
517     const gchar *variant, *ret;
518
519     variant = gst_structure_get_string (s, "variant");
520     if (variant == NULL)
521       ret = "H.263";
522     else if (strcmp (variant, "itu") == 0)
523       ret = "ITU H.26n";        /* why not ITU H.263? (tpm) */
524     else if (strcmp (variant, "lead") == 0)
525       ret = "Lead H.263";
526     else if (strcmp (variant, "microsoft") == 0)
527       ret = "Microsoft H.263";
528     else if (strcmp (variant, "vdolive") == 0)
529       ret = "VDOLive";
530     else if (strcmp (variant, "vivo") == 0)
531       ret = "Vivo H.263";
532     else if (strcmp (variant, "xirlink") == 0)
533       ret = "Xirlink H.263";
534     else {
535       GST_WARNING ("Unknown H263 variant '%s'", variant);
536       ret = "H.263";
537     }
538     return g_strdup (ret);
539   } else if (strcmp (info->type, "video/x-h264") == 0) {
540     const gchar *variant, *ret;
541     const gchar *profile;
542
543     variant = gst_structure_get_string (s, "variant");
544     if (variant == NULL)
545       ret = "H.264";
546     else if (strcmp (variant, "itu") == 0)
547       ret = "ITU H.264";
548     else if (strcmp (variant, "videosoft") == 0)
549       ret = "Videosoft H.264";
550     else if (strcmp (variant, "lead") == 0)
551       ret = "Lead H.264";
552     else {
553       GST_WARNING ("Unknown H264 variant '%s'", variant);
554       ret = "H.264";
555     }
556     /* profile */
557     profile = gst_structure_get_string (s, "profile");
558     if (profile != NULL)
559       profile = pbutils_desc_get_h264_profile_name_from_nick (profile);
560     if (profile == NULL)
561       return g_strdup (ret);
562     return g_strdup_printf ("%s (%s Profile)", ret, profile);
563   } else if (strcmp (info->type, "video/x-h265") == 0) {
564     const gchar *profile = gst_structure_get_string (s, "profile");
565
566     if (profile != NULL)
567       profile = pbutils_desc_get_h265_profile_name_from_nick (profile);
568     if (profile != NULL)
569       return g_strdup_printf ("H.265 (%s Profile)", profile);
570
571     return g_strdup ("H.265");
572   } else if (strcmp (info->type, "video/x-dirac") == 0) {
573     const gchar *profile = gst_structure_get_string (s, "profile");
574     if (profile == NULL)
575       return g_strdup ("Dirac");
576     if (strcmp (profile, "vc2-low-delay") == 0)
577       return g_strdup_printf ("Dirac (%s)", "VC-2 Low Delay Profile");
578     else if (strcmp (profile, "vc2-simple") == 0)
579       return g_strdup_printf ("Dirac (%s)", "VC-2 Simple Profile");
580     else if (strcmp (profile, "vc2-main") == 0)
581       return g_strdup_printf ("Dirac (%s)", "VC-2 Main Profile");
582     else
583       return g_strdup ("Dirac");
584   } else if (strcmp (info->type, "video/x-divx") == 0) {
585     gint ver = 0;
586
587     if (!gst_structure_get_int (s, "divxversion", &ver) || ver <= 2) {
588       GST_WARNING ("Unexpected DivX version in %" GST_PTR_FORMAT, caps);
589       return g_strdup ("DivX MPEG-4");
590     }
591     return g_strdup_printf (_("DivX MPEG-4 Version %d"), ver);
592   } else if (strcmp (info->type, "video/x-msmpeg") == 0) {
593     gint ver = 0;
594
595     if (!gst_structure_get_int (s, "msmpegversion", &ver) ||
596         ver < 40 || ver > 49) {
597       GST_WARNING ("Unexpected msmpegversion in %" GST_PTR_FORMAT, caps);
598       return g_strdup ("Microsoft MPEG-4 4.x");
599     }
600     return g_strdup_printf ("Microsoft MPEG-4 4.%d", ver % 10);
601   } else if (strcmp (info->type, "video/x-truemotion") == 0) {
602     gint ver = 0;
603
604     gst_structure_get_int (s, "trueversion", &ver);
605     switch (ver) {
606       case 1:
607         return g_strdup_printf ("Duck TrueMotion 1");
608       case 2:
609         return g_strdup_printf ("TrueMotion 2.0");
610       default:
611         GST_WARNING ("Unexpected trueversion in %" GST_PTR_FORMAT, caps);
612         break;
613     }
614     return g_strdup_printf ("TrueMotion");
615   } else if (strcmp (info->type, "video/x-xan") == 0) {
616     gint ver = 0;
617
618     if (!gst_structure_get_int (s, "wcversion", &ver) || ver < 1) {
619       GST_WARNING ("Unexpected wcversion in %" GST_PTR_FORMAT, caps);
620       return g_strdup ("Xan Wing Commander");
621     }
622     return g_strdup_printf ("Xan Wing Commander %u", ver);
623   } else if (strcmp (info->type, "video/x-indeo") == 0) {
624     gint ver = 0;
625
626     if (!gst_structure_get_int (s, "indeoversion", &ver) || ver < 2) {
627       GST_WARNING ("Unexpected indeoversion in %" GST_PTR_FORMAT, caps);
628       return g_strdup ("Intel Indeo");
629     }
630     return g_strdup_printf ("Intel Indeo %u", ver);
631   } else if (strcmp (info->type, "audio/x-wma") == 0) {
632     gint ver = 0;
633
634     gst_structure_get_int (s, "wmaversion", &ver);
635     switch (ver) {
636       case 1:
637       case 2:
638       case 3:
639         return g_strdup_printf ("Windows Media Audio %d", ver + 6);
640       default:
641         break;
642     }
643     GST_WARNING ("Unexpected wmaversion in %" GST_PTR_FORMAT, caps);
644     return g_strdup ("Windows Media Audio");
645   } else if (strcmp (info->type, "video/x-wmv") == 0) {
646     gint ver = 0;
647     const gchar *str;
648
649     gst_structure_get_int (s, "wmvversion", &ver);
650     str = gst_structure_get_string (s, "format");
651
652     switch (ver) {
653       case 1:
654       case 2:
655       case 3:
656         if (str && !strncmp (str, "MSS", 3)) {
657           return g_strdup_printf ("Windows Media Video %d Screen", ver + 6);
658         } else {
659           return g_strdup_printf ("Windows Media Video %d", ver + 6);
660         }
661       default:
662         break;
663     }
664     GST_WARNING ("Unexpected wmvversion in %" GST_PTR_FORMAT, caps);
665     return g_strdup ("Windows Media Video");
666   } else if (strcmp (info->type, "audio/x-mace") == 0) {
667     gint ver = 0;
668
669     gst_structure_get_int (s, "maceversion", &ver);
670     if (ver == 3 || ver == 6) {
671       return g_strdup_printf ("MACE-%d", ver);
672     } else {
673       GST_WARNING ("Unexpected maceversion in %" GST_PTR_FORMAT, caps);
674       return g_strdup ("MACE");
675     }
676   } else if (strcmp (info->type, "video/x-svq") == 0) {
677     gint ver = 0;
678
679     gst_structure_get_int (s, "svqversion", &ver);
680     if (ver == 1 || ver == 3) {
681       return g_strdup_printf ("Sorensen Video %d", ver);
682     } else {
683       GST_WARNING ("Unexpected svqversion in %" GST_PTR_FORMAT, caps);
684       return g_strdup ("Sorensen Video");
685     }
686   } else if (strcmp (info->type, "video/x-asus") == 0) {
687     gint ver = 0;
688
689     gst_structure_get_int (s, "asusversion", &ver);
690     if (ver == 1 || ver == 2) {
691       return g_strdup_printf ("Asus Video %d", ver);
692     } else {
693       GST_WARNING ("Unexpected asusversion in %" GST_PTR_FORMAT, caps);
694       return g_strdup ("Asus Video");
695     }
696   } else if (strcmp (info->type, "video/x-ati-vcr") == 0) {
697     gint ver = 0;
698
699     gst_structure_get_int (s, "vcrversion", &ver);
700     if (ver == 1 || ver == 2) {
701       return g_strdup_printf ("ATI VCR %d", ver);
702     } else {
703       GST_WARNING ("Unexpected acrversion in %" GST_PTR_FORMAT, caps);
704       return g_strdup ("ATI VCR");
705     }
706   } else if (strcmp (info->type, "audio/x-adpcm") == 0) {
707     const GValue *layout_val;
708
709     layout_val = gst_structure_get_value (s, "layout");
710     if (layout_val != NULL && G_VALUE_HOLDS_STRING (layout_val)) {
711       const gchar *layout;
712
713       if ((layout = g_value_get_string (layout_val))) {
714         gchar *layout_upper, *ret;
715
716         if (strcmp (layout, "swf") == 0)
717           return g_strdup ("Shockwave ADPCM");
718         if (strcmp (layout, "microsoft") == 0)
719           return g_strdup ("Microsoft ADPCM");
720         if (strcmp (layout, "quicktime") == 0)
721           return g_strdup ("Quicktime ADPCM");
722         if (strcmp (layout, "westwood") == 0)
723           return g_strdup ("Westwood ADPCM");
724         if (strcmp (layout, "yamaha") == 0)
725           return g_strdup ("Yamaha ADPCM");
726         /* FIXME: other layouts: sbpro2, sbpro3, sbpro4, ct, g726, ea,
727          * adx, xa, 4xm, smjpeg, dk4, dk3, dvi */
728         layout_upper = g_ascii_strup (layout, -1);
729         ret = g_strdup_printf ("%s ADPCM", layout_upper);
730         g_free (layout_upper);
731         return ret;
732       }
733     }
734     return g_strdup ("ADPCM");
735   } else if (strcmp (info->type, "audio/mpeg") == 0) {
736     gint ver = 0, layer = 0;
737
738     gst_structure_get_int (s, "mpegversion", &ver);
739
740     switch (ver) {
741       case 1:
742         gst_structure_get_int (s, "layer", &layer);
743         switch (layer) {
744           case 1:
745           case 2:
746           case 3:
747             return g_strdup_printf ("MPEG-1 Layer %d (MP%d)", layer, layer);
748           default:
749             break;
750         }
751         GST_WARNING ("Unexpected MPEG-1 layer in %" GST_PTR_FORMAT, caps);
752         return g_strdup ("MPEG-1 Audio");
753       case 2:
754         return g_strdup ("MPEG-2 AAC");
755       case 4:
756         return g_strdup ("MPEG-4 AAC");
757       default:
758         break;
759     }
760     GST_WARNING ("Unexpected audio mpegversion in %" GST_PTR_FORMAT, caps);
761     return g_strdup ("MPEG Audio");
762   } else if (strcmp (info->type, "audio/x-pn-realaudio") == 0) {
763     gint ver = 0;
764
765     gst_structure_get_int (s, "raversion", &ver);
766     switch (ver) {
767       case 1:
768         return g_strdup ("RealAudio 14k4bps");
769       case 2:
770         return g_strdup ("RealAudio 28k8bps");
771       case 8:
772         return g_strdup ("RealAudio G2 (Cook)");
773       default:
774         break;
775     }
776     GST_WARNING ("Unexpected raversion in %" GST_PTR_FORMAT, caps);
777     return g_strdup ("RealAudio");
778   } else if (strcmp (info->type, "video/x-pn-realvideo") == 0) {
779     gint ver = 0;
780
781     gst_structure_get_int (s, "rmversion", &ver);
782     switch (ver) {
783       case 1:
784         return g_strdup ("RealVideo 1.0");
785       case 2:
786         return g_strdup ("RealVideo 2.0");
787       case 3:
788         return g_strdup ("RealVideo 3.0");
789       case 4:
790         return g_strdup ("RealVideo 4.0");
791       default:
792         break;
793     }
794     GST_WARNING ("Unexpected rmversion in %" GST_PTR_FORMAT, caps);
795     return g_strdup ("RealVideo");
796   } else if (strcmp (info->type, "video/mpeg") == 0) {
797     gboolean sysstream;
798     gint ver = 0;
799
800     if (!gst_structure_get_boolean (s, "systemstream", &sysstream)) {
801       GST_WARNING ("Missing systemstream field in mpeg video caps "
802           "%" GST_PTR_FORMAT, caps);
803       sysstream = FALSE;
804     }
805
806     if (gst_structure_get_int (s, "mpegversion", &ver) && ver > 0 && ver <= 4) {
807       if (sysstream) {
808         return g_strdup_printf ("MPEG-%d System Stream", ver);
809       } else {
810         const gchar *profile = gst_structure_get_string (s, "profile");
811         if (profile != NULL) {
812           if (ver == 4)
813             profile = pbutils_desc_get_mpeg4v_profile_name_from_nick (profile);
814           else if (ver == 2)
815             profile = pbutils_desc_get_mpeg2v_profile_name_from_nick (profile);
816           else
817             profile = NULL;
818         }
819         if (profile != NULL)
820           return g_strdup_printf ("MPEG-%d Video (%s Profile)", ver, profile);
821         else
822           return g_strdup_printf ("MPEG-%d Video", ver);
823       }
824     }
825     GST_WARNING ("Missing mpegversion field in mpeg video caps "
826         "%" GST_PTR_FORMAT, caps);
827     return g_strdup ("MPEG Video");
828   } else if (strcmp (info->type, "audio/x-raw") == 0) {
829     gint depth = 0;
830     gboolean is_float;
831     const gchar *str;
832     GstAudioFormat format = GST_AUDIO_FORMAT_UNKNOWN;
833     const GstAudioFormatInfo *finfo;
834
835     str = gst_structure_get_string (s, "format");
836     if (str)
837       format = gst_audio_format_from_string (str);
838     if (format == GST_AUDIO_FORMAT_UNKNOWN)
839       return g_strdup (_("Uncompressed audio"));
840
841     finfo = gst_audio_format_get_info (format);
842     depth = GST_AUDIO_FORMAT_INFO_DEPTH (finfo);
843     is_float = GST_AUDIO_FORMAT_INFO_IS_FLOAT (finfo);
844
845     return g_strdup_printf (_("Raw %d-bit %s audio"), depth,
846         is_float ? "floating-point" : "PCM");
847   } else if (strcmp (info->type, "video/x-tscc") == 0) {
848     gint version;
849     gst_structure_get_int (s, "tsccversion", &version);
850     switch (version) {
851       case 1:
852         return g_strdup ("TechSmith Screen Capture 1");
853       case 2:
854         return g_strdup ("TechSmith Screen Capture 2");
855       default:
856         break;
857     }
858     GST_WARNING ("Unexpected version in %" GST_PTR_FORMAT, caps);
859     return g_strdup ("TechSmith Screen Capture");
860   }
861   return NULL;
862 }
863
864 /* returns format info structure, will return NULL for dynamic media types! */
865 static const FormatInfo *
866 find_format_info (const GstCaps * caps)
867 {
868   const GstStructure *s;
869   const gchar *media_type;
870   guint i;
871
872   s = gst_caps_get_structure (caps, 0);
873   media_type = gst_structure_get_name (s);
874
875   for (i = 0; i < G_N_ELEMENTS (formats); ++i) {
876     if (strcmp (media_type, formats[i].type) == 0) {
877       gboolean is_sys = FALSE;
878
879       if ((formats[i].flags & FLAG_SYSTEMSTREAM) == 0)
880         return &formats[i];
881
882       /* this record should only be matched if the systemstream field is set */
883       if (gst_structure_get_boolean (s, "systemstream", &is_sys) && is_sys)
884         return &formats[i];
885     }
886   }
887
888   return NULL;
889 }
890
891 static gboolean
892 caps_are_rtp_caps (const GstCaps * caps, const gchar * media, gchar ** format)
893 {
894   const GstStructure *s;
895   const gchar *str;
896
897   g_assert (media != NULL && format != NULL);
898
899   s = gst_caps_get_structure (caps, 0);
900   if (!gst_structure_has_name (s, "application/x-rtp"))
901     return FALSE;
902   if (!gst_structure_has_field_typed (s, "media", G_TYPE_STRING))
903     return FALSE;
904   str = gst_structure_get_string (s, "media");
905   if (str == NULL || !g_str_equal (str, media))
906     return FALSE;
907   str = gst_structure_get_string (s, "encoding-name");
908   if (str == NULL || *str == '\0')
909     return FALSE;
910
911   if (strcmp (str, "X-ASF-PF") == 0) {
912     *format = g_strdup ("Windows Media");
913   } else if (g_str_has_prefix (str, "X-")) {
914     *format = g_strdup (str + 2);
915   } else {
916     *format = g_strdup (str);
917   }
918
919   return TRUE;
920 }
921
922 /**
923  * gst_pb_utils_get_source_description:
924  * @protocol: the protocol the source element needs to handle, e.g. "http"
925  *
926  * Returns a localised string describing a source element handling the protocol
927  * specified in @protocol, for use in error dialogs or other messages to be
928  * seen by the user. Should never return NULL unless @protocol is invalid.
929  *
930  * This function is mainly for internal use, applications would typically
931  * use gst_missing_plugin_message_get_description() to get a description of
932  * a missing feature from a missing-plugin message.
933  *
934  * Returns: a newly-allocated description string, or NULL on error. Free
935  *          string with g_free() when not needed any longer.
936  */
937 gchar *
938 gst_pb_utils_get_source_description (const gchar * protocol)
939 {
940   gchar *proto_uc, *ret;
941
942   g_return_val_if_fail (protocol != NULL, NULL);
943
944   gst_pb_utils_init_locale_text_domain ();
945
946   if (strcmp (protocol, "cdda") == 0)
947     return g_strdup (_("Audio CD source"));
948
949   if (strcmp (protocol, "dvd") == 0)
950     return g_strdup (_("DVD source"));
951
952   if (strcmp (protocol, "rtsp") == 0)
953     return g_strdup (_("Real Time Streaming Protocol (RTSP) source"));
954
955   /* TODO: what about mmst, mmsu, mmsh? */
956   if (strcmp (protocol, "mms") == 0)
957     return g_strdup (_("Microsoft Media Server (MMS) protocol source"));
958
959   /* make protocol uppercase */
960   proto_uc = g_ascii_strup (protocol, -1);
961
962   /* TODO: find out how to add a comment for translators to the source code
963    * (and tell them to make the first letter uppercase below if they move
964    * the protocol to the middle or end of the string) */
965   ret = g_strdup_printf (_("%s protocol source"), proto_uc);
966
967   g_free (proto_uc);
968
969   return ret;
970 }
971
972 /**
973  * gst_pb_utils_get_sink_description:
974  * @protocol: the protocol the sink element needs to handle, e.g. "http"
975  *
976  * Returns a localised string describing a sink element handling the protocol
977  * specified in @protocol, for use in error dialogs or other messages to be
978  * seen by the user. Should never return NULL unless @protocol is invalid.
979  *
980  * This function is mainly for internal use, applications would typically
981  * use gst_missing_plugin_message_get_description() to get a description of
982  * a missing feature from a missing-plugin message.
983  *
984  * Returns: a newly-allocated description string, or NULL on error. Free
985  *          string with g_free() when not needed any longer.
986  */
987 gchar *
988 gst_pb_utils_get_sink_description (const gchar * protocol)
989 {
990   gchar *proto_uc, *ret;
991
992   g_return_val_if_fail (protocol != NULL, NULL);
993
994   /* make protocol uppercase */
995   proto_uc = g_ascii_strup (protocol, -1);
996
997   /* TODO: find out how to add a comment for translators to the source code
998    * (and tell them to make the first letter uppercase below if they move
999    * the protocol to the middle or end of the string) */
1000   ret = g_strdup_printf ("%s protocol sink", proto_uc);
1001
1002   g_free (proto_uc);
1003
1004   return ret;
1005 }
1006
1007 /**
1008  * gst_pb_utils_get_decoder_description:
1009  * @caps: the (fixed) #GstCaps for which an decoder description is needed
1010  *
1011  * Returns a localised string describing an decoder for the format specified
1012  * in @caps, for use in error dialogs or other messages to be seen by the user.
1013  * Should never return NULL unless @factory_name or @caps are invalid.
1014  *
1015  * This function is mainly for internal use, applications would typically
1016  * use gst_missing_plugin_message_get_description() to get a description of
1017  * a missing feature from a missing-plugin message.
1018  *
1019  * Returns: a newly-allocated description string, or NULL on error. Free
1020  *          string with g_free() when not needed any longer.
1021  */
1022 gchar *
1023 gst_pb_utils_get_decoder_description (const GstCaps * caps)
1024 {
1025   gchar *str, *ret;
1026   GstCaps *tmp;
1027
1028   g_return_val_if_fail (caps != NULL, NULL);
1029   g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
1030
1031   tmp = copy_and_clean_caps (caps);
1032
1033   g_return_val_if_fail (gst_caps_is_fixed (tmp), NULL);
1034
1035   gst_pb_utils_init_locale_text_domain ();
1036
1037   /* special-case RTP caps */
1038   if (caps_are_rtp_caps (tmp, "video", &str)) {
1039     ret = g_strdup_printf (_("%s video RTP depayloader"), str);
1040   } else if (caps_are_rtp_caps (tmp, "audio", &str)) {
1041     ret = g_strdup_printf (_("%s audio RTP depayloader"), str);
1042   } else if (caps_are_rtp_caps (tmp, "application", &str)) {
1043     ret = g_strdup_printf (_("%s RTP depayloader"), str);
1044   } else {
1045     const FormatInfo *info;
1046
1047     str = gst_pb_utils_get_codec_description (tmp);
1048     info = find_format_info (tmp);
1049     if (info != NULL && (info->flags & FLAG_CONTAINER) != 0) {
1050       ret = g_strdup_printf (_("%s demuxer"), str);
1051     } else {
1052       ret = g_strdup_printf (_("%s decoder"), str);
1053     }
1054   }
1055
1056   g_free (str);
1057   gst_caps_unref (tmp);
1058
1059   return ret;
1060 }
1061
1062 /**
1063  * gst_pb_utils_get_encoder_description:
1064  * @caps: the (fixed) #GstCaps for which an encoder description is needed
1065  *
1066  * Returns a localised string describing an encoder for the format specified
1067  * in @caps, for use in error dialogs or other messages to be seen by the user.
1068  * Should never return NULL unless @factory_name or @caps are invalid.
1069  *
1070  * This function is mainly for internal use, applications would typically
1071  * use gst_missing_plugin_message_get_description() to get a description of
1072  * a missing feature from a missing-plugin message.
1073  *
1074  * Returns: a newly-allocated description string, or NULL on error. Free
1075  *          string with g_free() when not needed any longer.
1076  */
1077 gchar *
1078 gst_pb_utils_get_encoder_description (const GstCaps * caps)
1079 {
1080   gchar *str, *ret;
1081   GstCaps *tmp;
1082
1083   g_return_val_if_fail (caps != NULL, NULL);
1084   g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
1085   tmp = copy_and_clean_caps (caps);
1086   g_return_val_if_fail (gst_caps_is_fixed (tmp), NULL);
1087   gst_pb_utils_init_locale_text_domain ();
1088
1089   /* special-case RTP caps */
1090   if (caps_are_rtp_caps (tmp, "video", &str)) {
1091     ret = g_strdup_printf (_("%s video RTP payloader"), str);
1092   } else if (caps_are_rtp_caps (tmp, "audio", &str)) {
1093     ret = g_strdup_printf (_("%s audio RTP payloader"), str);
1094   } else if (caps_are_rtp_caps (tmp, "application", &str)) {
1095     ret = g_strdup_printf (_("%s RTP payloader"), str);
1096   } else {
1097     const FormatInfo *info;
1098
1099     str = gst_pb_utils_get_codec_description (tmp);
1100     info = find_format_info (tmp);
1101     if (info != NULL && (info->flags & FLAG_CONTAINER) != 0) {
1102       ret = g_strdup_printf (_("%s muxer"), str);
1103     } else {
1104       ret = g_strdup_printf (_("%s encoder"), str);
1105     }
1106   }
1107
1108   g_free (str);
1109   gst_caps_unref (tmp);
1110
1111   return ret;
1112 }
1113
1114 /**
1115  * gst_pb_utils_get_element_description:
1116  * @factory_name: the name of the element, e.g. "giosrc"
1117  *
1118  * Returns a localised string describing the given element, for use in
1119  * error dialogs or other messages to be seen by the user. Should never
1120  * return NULL unless @factory_name is invalid.
1121  *
1122  * This function is mainly for internal use, applications would typically
1123  * use gst_missing_plugin_message_get_description() to get a description of
1124  * a missing feature from a missing-plugin message.
1125  *
1126  * Returns: a newly-allocated description string, or NULL on error. Free
1127  *          string with g_free() when not needed any longer.
1128  */
1129 gchar *
1130 gst_pb_utils_get_element_description (const gchar * factory_name)
1131 {
1132   gchar *ret;
1133
1134   g_return_val_if_fail (factory_name != NULL, NULL);
1135
1136   gst_pb_utils_init_locale_text_domain ();
1137
1138   ret = g_strdup_printf (_("GStreamer element %s"), factory_name);
1139   if (ret && g_str_has_prefix (ret, factory_name))
1140     *ret = g_ascii_toupper (*ret);
1141
1142   return ret;
1143 }
1144
1145 /**
1146  * gst_pb_utils_add_codec_description_to_tag_list:
1147  * @taglist: a #GstTagList
1148  * @codec_tag: (allow-none): a GStreamer codec tag such as #GST_TAG_AUDIO_CODEC,
1149  *             #GST_TAG_VIDEO_CODEC or #GST_TAG_CODEC. If none is specified,
1150  *             the function will attempt to detect the appropriate category.
1151  * @caps: the (fixed) #GstCaps for which a codec tag should be added.
1152  *
1153  * Adds a codec tag describing the format specified by @caps to @taglist.
1154  *
1155  * Returns: TRUE if a codec tag was added, FALSE otherwise.
1156  */
1157 gboolean
1158 gst_pb_utils_add_codec_description_to_tag_list (GstTagList * taglist,
1159     const gchar * codec_tag, const GstCaps * caps)
1160 {
1161   const FormatInfo *info;
1162   gchar *desc;
1163
1164   g_return_val_if_fail (taglist != NULL, FALSE);
1165   g_return_val_if_fail (GST_IS_TAG_LIST (taglist), FALSE);
1166   g_return_val_if_fail (codec_tag == NULL || (gst_tag_exists (codec_tag)
1167           && gst_tag_get_type (codec_tag) == G_TYPE_STRING), FALSE);
1168   g_return_val_if_fail (caps != NULL, FALSE);
1169   g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
1170
1171   info = find_format_info (caps);
1172   if (info == NULL)
1173     return FALSE;
1174
1175   /* Attempt to find tag classification */
1176   if (codec_tag == NULL) {
1177     if (info->flags & FLAG_CONTAINER)
1178       codec_tag = GST_TAG_CONTAINER_FORMAT;
1179     else if (info->flags & FLAG_AUDIO)
1180       codec_tag = GST_TAG_AUDIO_CODEC;
1181     else if (info->flags & FLAG_VIDEO)
1182       codec_tag = GST_TAG_VIDEO_CODEC;
1183     else if (info->flags & FLAG_SUB)
1184       codec_tag = GST_TAG_SUBTITLE_CODEC;
1185     else
1186       codec_tag = GST_TAG_CODEC;
1187   }
1188
1189   desc = format_info_get_desc (info, caps);
1190   gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, codec_tag, desc, NULL);
1191   g_free (desc);
1192
1193   return TRUE;
1194 }
1195
1196 /**
1197  * gst_pb_utils_get_codec_description:
1198  * @caps: the (fixed) #GstCaps for which an format description is needed
1199  *
1200  * Returns a localised (as far as this is possible) string describing the
1201  * media format specified in @caps, for use in error dialogs or other messages
1202  * to be seen by the user. Should never return NULL unless @caps is invalid.
1203  *
1204  * Also see the convenience function
1205  * gst_pb_utils_add_codec_description_to_tag_list().
1206  *
1207  * Returns: a newly-allocated description string, or NULL on error. Free
1208  *          string with g_free() when not needed any longer.
1209  */
1210 gchar *
1211 gst_pb_utils_get_codec_description (const GstCaps * caps)
1212 {
1213   const FormatInfo *info;
1214   gchar *str, *comma;
1215   GstCaps *tmp;
1216
1217   g_return_val_if_fail (caps != NULL, NULL);
1218   g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
1219   tmp = copy_and_clean_caps (caps);
1220   g_return_val_if_fail (gst_caps_is_fixed (tmp), NULL);
1221
1222   info = find_format_info (tmp);
1223
1224   if (info) {
1225     str = format_info_get_desc (info, tmp);
1226   } else {
1227     str = gst_caps_to_string (tmp);
1228
1229     /* cut off everything after the media type, if there is anything */
1230     if ((comma = strchr (str, ','))) {
1231       *comma = '\0';
1232       g_strchomp (str);
1233       /* we could do something more elaborate here, like taking into account
1234        * audio/, video/, image/ and application/ prefixes etc. */
1235     }
1236
1237     GST_WARNING ("No description available for media type: %s", str);
1238   }
1239   gst_caps_unref (tmp);
1240
1241   return str;
1242 }
1243
1244 /* internal helper functions for gst_encoding_profile_get_file_extension() */
1245 const gchar *pb_utils_get_file_extension_from_caps (const GstCaps * caps);
1246 gboolean pb_utils_is_tag (const GstCaps * caps);
1247
1248 const gchar *
1249 pb_utils_get_file_extension_from_caps (const GstCaps * caps)
1250 {
1251   const FormatInfo *info;
1252   const gchar *ext = NULL;
1253   GstCaps *stripped_caps;
1254
1255   g_assert (GST_IS_CAPS (caps));
1256
1257   stripped_caps = copy_and_clean_caps (caps);
1258
1259   g_assert (gst_caps_is_fixed (stripped_caps));
1260
1261   info = find_format_info (stripped_caps);
1262
1263   if (info && info->ext[0] != '\0') {
1264     ext = info->ext;
1265   } else if (info && info->desc == NULL) {
1266     const GstStructure *s;
1267
1268     s = gst_caps_get_structure (stripped_caps, 0);
1269
1270     /* cases where we have to evaluate the caps more closely */
1271     if (strcmp (info->type, "audio/mpeg") == 0) {
1272       int version = 0, layer = 3;
1273
1274       if (gst_structure_get_int (s, "mpegversion", &version)) {
1275         if (version == 2 || version == 4) {
1276           ext = "aac";
1277         } else if (version == 1) {
1278           gst_structure_get_int (s, "layer", &layer);
1279           if (layer == 1)
1280             ext = "mp1";
1281           else if (layer == 2)
1282             ext = "mp2";
1283           else
1284             ext = "mp3";
1285         }
1286       }
1287     }
1288   }
1289
1290   gst_caps_unref (stripped_caps);
1291   return ext;
1292 }
1293
1294 /**
1295  * gst_pb_utils_get_file_extension_from_caps:
1296  * @caps: the (fixed) #GstCaps for which a file extension is needed
1297  *
1298  * Returns a possible file extension for the given caps, if known.
1299  *
1300  * Returns: (nullable): a newly-allocated file extension string, or NULL on error. Free
1301  *          string with g_free() when not needed any longer.
1302  *
1303  * Since: 1.20
1304  */
1305 gchar *
1306 gst_pb_utils_get_file_extension_from_caps (const GstCaps * caps)
1307 {
1308   return g_strdup (pb_utils_get_file_extension_from_caps (caps));
1309 }
1310
1311 /**
1312  * gst_pb_utils_get_caps_description_flags:
1313  * @caps: the (fixed) #GstCaps for which flags are requested
1314  *
1315  * Returns flags that describe the format of the caps if known. No flags are
1316  * set for unknown caps.
1317  *
1318  * Returns: #GstPbUtilsCapsDescriptionFlags that describe @caps, or no flags
1319  *          if the caps are unknown.
1320  *
1321  * Since: 1.20
1322  */
1323 GstPbUtilsCapsDescriptionFlags
1324 gst_pb_utils_get_caps_description_flags (const GstCaps * caps)
1325 {
1326   GstCaps *tmp;
1327   const FormatInfo *info;
1328   GstPbUtilsCapsDescriptionFlags flags = 0;
1329
1330   g_return_val_if_fail (caps != NULL, 0);
1331   g_return_val_if_fail (GST_IS_CAPS (caps), 0);
1332   tmp = copy_and_clean_caps (caps);
1333   g_return_val_if_fail (gst_caps_is_fixed (tmp), 0);
1334
1335   info = find_format_info (tmp);
1336   /* A separate flags type is used because internally more flags are needed
1337    * for filtering purposes, e.g. the SYSTEMSTREAM flag */
1338   if (info) {
1339     if ((info->flags & FLAG_CONTAINER))
1340       flags |= GST_PBUTILS_CAPS_DESCRIPTION_FLAG_CONTAINER;
1341     if ((info->flags & FLAG_AUDIO))
1342       flags |= GST_PBUTILS_CAPS_DESCRIPTION_FLAG_AUDIO;
1343     if ((info->flags & FLAG_VIDEO))
1344       flags |= GST_PBUTILS_CAPS_DESCRIPTION_FLAG_VIDEO;
1345     if ((info->flags & FLAG_IMAGE))
1346       flags |= GST_PBUTILS_CAPS_DESCRIPTION_FLAG_IMAGE;
1347     if ((info->flags & FLAG_SUB))
1348       flags |= GST_PBUTILS_CAPS_DESCRIPTION_FLAG_SUBTITLE;
1349     if ((info->flags & FLAG_TAG))
1350       flags |= GST_PBUTILS_CAPS_DESCRIPTION_FLAG_TAG;
1351     if ((info->flags & FLAG_GENERIC))
1352       flags |= GST_PBUTILS_CAPS_DESCRIPTION_FLAG_GENERIC;
1353     if ((info->flags & FLAG_METADATA))
1354       flags |= GST_PBUTILS_CAPS_DESCRIPTION_FLAG_METADATA;
1355   }
1356
1357   gst_caps_unref (tmp);
1358
1359   return flags;
1360 }
1361
1362 gboolean
1363 pb_utils_is_tag (const GstCaps * caps)
1364 {
1365   const FormatInfo *info;
1366   GstCaps *stripped_caps;
1367   gboolean is_tag = FALSE;
1368
1369   g_assert (GST_IS_CAPS (caps));
1370
1371   stripped_caps = copy_and_clean_caps (caps);
1372
1373   g_assert (gst_caps_is_fixed (stripped_caps));
1374
1375   info = find_format_info (stripped_caps);
1376
1377   if (info) {
1378     is_tag = (info->flags & FLAG_TAG) != 0;
1379   }
1380   gst_caps_unref (stripped_caps);
1381
1382   return is_tag;
1383 }
1384
1385 #if 0
1386 void
1387 gst_pb_utils_list_all (void)
1388 {
1389   gint i;
1390
1391   g_print ("static const gchar *caps_strings[] = { ");
1392
1393   for (i = 0; i < G_N_ELEMENTS (formats); ++i) {
1394     if (formats[i].desc != NULL)
1395       g_print ("  \"%s\", ", formats[i].type);
1396   }
1397   g_print ("\n#if 0\n");
1398   for (i = 0; i < G_N_ELEMENTS (formats); ++i) {
1399     if (formats[i].desc == NULL)
1400       g_print ("  \"%s\", \n", formats[i].type);
1401   }
1402   g_print ("\n#endif\n");
1403 }
1404 #endif