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