Merge remote-tracking branch 'njsf/for-steve' into njsf-dbus
[sxemacs] / src / media / media.c
1 /* Media functions.
2    Copyright (C) 2006 Sebastian Freundt
3
4 This file is part of SXEmacs
5
6 SXEmacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 SXEmacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
18
19
20 /* Synched up with: Not in FSF. */
21
22 #include <config.h>
23 #include "lisp.h"
24
25 #include "buffer.h"
26
27 #include "media.h"
28 #include "sound.h"
29 #ifdef HAVE_FFMPEG
30 #include "media-ffmpeg.h"
31 #endif
32 #ifdef HAVE_SNDFILE
33 #include "media-sndfile.h"
34 #endif
35 #ifdef HAVE_INTERNAL_MEDIA
36 #include "media-internal.h"
37 #endif
38 #ifdef HAVE_MAD
39 #include "media-mad.h"
40 #endif
41 #ifdef HAVE_GSTREAMER
42 #include "media-gstreamer.h"
43 #endif
44 #ifdef HAVE_SOX
45 #include "media-sox.h"
46 #endif
47 #ifdef HAVE_XINE
48 #include "media-xine.h"
49 #endif
50
51 Lisp_Object Qmedia_streamp;
52 Lisp_Object Qunknown;
53 Lisp_Object Qunavailable;
54 /* media property syms */
55 Lisp_Object Qdemux, Qcodec, Qnchannels, Qsamplerate;
56 Lisp_Object Qbitrate, Qabitrate, Qvbitrate;
57 Lisp_Object Qwidth, Qheight, Qaspect, Qdriver, Qkind, Qfifo, Quri, Qtype;
58 Lisp_Object Qaudio, Qvideo, Qimage;
59
60 static void determine_stream_type(Lisp_Media_Stream *ms, media_driver);
61 static void media_stream_print(Lisp_Object, Lisp_Object, int);
62 static void media_substream_print(media_substream *, Lisp_Object, int);
63 static void media_substream_finalise(void*, int);
64
65 \f
66 static void determine_stream_type(Lisp_Media_Stream *ms, media_driver preferred)
67 {
68 #ifdef HAVE_FFMPEG
69         if ((media_stream_driver(ms) == MDRIVER_UNKNOWN) &&
70             (preferred == MDRIVER_UNKNOWN ||
71              preferred == MDRIVER_FFMPEG)) {
72                 MEDIA_DEBUG("trying ffmpeg.\n");
73                 media_stream_set_meths(ms, media_ffmpeg);
74                 media_stream_meth(ms, open)(ms);
75         }
76 #endif
77 #ifdef HAVE_MAD
78         if ((media_stream_driver(ms) == MDRIVER_UNKNOWN) &&
79             (preferred == MDRIVER_UNKNOWN ||
80              preferred == MDRIVER_MAD)) {
81                 MEDIA_DEBUG("trying mad.\n");
82                 media_stream_set_meths(ms, media_mad);
83                 media_stream_meth(ms, open)(ms);
84         }
85 #endif
86 #ifdef HAVE_SOX
87         if ((media_stream_driver(ms) == MDRIVER_UNKNOWN) &&
88             (preferred == MDRIVER_UNKNOWN ||
89              preferred == MDRIVER_SOX)) {
90                 MEDIA_DEBUG("trying sox.\n");
91                 media_stream_set_meths(ms, media_sox);
92                 media_stream_meth(ms, open)(ms);
93         }
94 #endif
95 #ifdef HAVE_SNDFILE
96         if ((media_stream_driver(ms) == MDRIVER_UNKNOWN) &&
97             (preferred == MDRIVER_UNKNOWN ||
98              preferred == MDRIVER_SNDFILE)) {
99                 MEDIA_DEBUG("trying sndfile.\n");
100                 media_stream_set_meths(ms, media_sndfile);
101                 media_stream_meth(ms, open)(ms);
102         }
103 #endif
104 /* not working stuff here :) */
105 #if 0
106 #ifdef HAVE_GSTREAMER
107         if ((media_stream_driver(ms) == MDRIVER_UNKNOWN) &&
108             (preferred == MDRIVER_UNKNOWN ||
109              preferred == MDRIVER_GSTREAMER)) {
110                 MEDIA_DEBUG("trying gstreamer.\n");
111                 media_gstreamer_analyse_stream(ms);
112         }
113 #endif
114 #endif
115 #ifdef HAVE_XINE
116         if ((media_stream_driver(ms) == MDRIVER_UNKNOWN) &&
117             (preferred == MDRIVER_UNKNOWN ||
118              preferred == MDRIVER_XINE)) {
119                 MEDIA_DEBUG("trying xine.\n");
120                 media_stream_set_meths(ms, media_xine);
121                 media_stream_meth(ms, open)(ms);
122         }
123 #endif
124 #ifdef HAVE_INTERNAL_MEDIA
125         if ((media_stream_driver(ms) == MDRIVER_UNKNOWN) &&
126             (preferred == MDRIVER_UNKNOWN ||
127              preferred == MDRIVER_INTERNAL)) {
128                 MEDIA_DEBUG("trying internal.\n");
129                 media_internal_analyse_stream(ms);
130         }
131 #endif
132         if (media_stream_driver(ms) == MDRIVER_UNKNOWN) {
133                 MEDIA_DEBUG("giving up\n");
134                 media_stream_set_meths(ms, NULL);
135         }
136         return;
137 }
138
139 \f
140 /*****************************************************************/
141 /*                          media streams                        */
142 /*****************************************************************/
143 static Lisp_Object media_stream_mark(Lisp_Object obj)
144 {
145         switch (XMEDIA_STREAM_KIND(obj)) {
146         case MKIND_FILE:
147                 mark_object(XMEDIA_STREAM(obj)->
148                             kind_properties.fprops->filename);
149                 break;
150         case MKIND_STRING:
151         case MKIND_FIFO:
152         case MKIND_STREAM:
153         case MKIND_UNKNOWN:
154         case NUMBER_OF_MEDIA_KINDS:
155         default:
156                 break;
157         }
158
159         if (XMEDIA_STREAM_METHS(obj) &&
160             XMEDIA_STREAM_METH(obj, mark))
161                 XMEDIA_STREAM_METH(obj, mark)(XMEDIA_STREAM_DATA(obj));
162
163         return Qnil;
164 }
165
166 static void
167 media_stream_finalise(void *header, int for_disksave)
168 {
169         Lisp_Media_Stream *ms = (Lisp_Media_Stream*)header;
170         media_substream *mss = NULL;
171
172         if(ms == NULL)
173                 return;
174         if (media_stream_meths(ms) &&
175             media_stream_meth(ms, close))
176                 media_stream_meth(ms, close)(media_stream_data(ms));
177
178         mss = media_stream_first(ms);
179         while (mss) {
180                 media_substream_finalise(mss, for_disksave);
181                 mss = media_substream_next(mss);
182         }
183
184 #if 0
185         switch (media_stream_driver(ms)) {
186         case MDRIVER_XINE:
187 #ifdef HAVE_XINE
188                 if (media_stream_data(ms))
189                         media_xine_close_context(media_stream_data(ms));
190 #endif
191                 break;
192         case MDRIVER_SNDFILE:
193 #ifdef HAVE_SNDFILE
194                 if (media_stream_data(ms))
195                         sf_close(media_stream_data(ms));
196 #endif
197                 break;
198         case MDRIVER_MAD:
199 #ifdef HAVE_MAD
200                 if (media_stream_data(ms))
201                         mad_decoder_finish(media_stream_data(ms));
202 #endif
203                 break;
204         case MDRIVER_SOX:
205 #ifdef HAVE_SOX
206                 if (media_stream_data(ms))
207                         st_close(media_stream_data(ms));
208 #endif
209                 break;
210         default:
211                 break;
212         }
213 #endif
214
215         switch (media_stream_kind(ms)) {
216         case MKIND_FILE: {
217                 mkind_file_properties *mkfp;
218                 mkfp = media_stream_kind_properties(ms).fprops;
219                 if (mkfp) {
220                         xfree(mkfp);
221                 }
222                 break;
223         }
224         case MKIND_STRING: {
225                 mkind_string_properties *mksp;
226                 mksp = media_stream_kind_properties(ms).sprops;
227                 if (mksp) {
228                         if (mksp->name)
229                                 xfree(mksp->name);
230 #if 0
231                         if (mksp->stream_data)
232                                 xfree(mksp->stream_data);
233 #endif
234                         xfree(mksp);
235                 }
236                 break;
237         }
238         case MKIND_UNKNOWN:
239         case MKIND_FIFO:
240         case MKIND_STREAM:
241         case NUMBER_OF_MEDIA_KINDS:
242         default:
243                 break;
244         }
245
246         /* avoid some warning */
247         if (for_disksave || ms == NULL);
248 }
249
250 static void
251 media_stream_print(Lisp_Object obj, Lisp_Object printcharfun, int ef)
252 {
253         Lisp_Media_Stream *ms = XMEDIA_STREAM(obj);
254         media_substream *mss;
255
256         write_c_string("#<media-stream", printcharfun);
257
258         write_c_string(" :kind ", printcharfun);
259
260         switch (media_stream_kind(ms)) {
261         case MKIND_FILE: {
262                 Lisp_Object file =
263                         media_stream_kind_properties(ms).fprops->filename;
264
265                 write_c_string("#<file ", printcharfun);
266                 print_internal(file, printcharfun, ef);
267                 write_c_string(">", printcharfun);
268                 break;
269         }
270         case MKIND_STRING:
271                 write_c_string("#<string>", printcharfun);
272                 break;
273         case MKIND_FIFO:
274                 write_c_string("#<fifo>", printcharfun);
275                 break;
276         case MKIND_STREAM:
277                 write_c_string("#<stream>", printcharfun);
278                 break;
279
280         default:
281         case MKIND_UNKNOWN:
282         case NUMBER_OF_MEDIA_KINDS:
283                 write_c_string("#<unknown>", printcharfun);
284                 break;
285         }
286
287         mss = media_stream_first(ms);
288         while (mss) {
289                 write_c_string(" ", printcharfun);
290                 media_substream_print(mss, printcharfun, ef);
291                 mss = media_substream_next(mss);
292         }
293
294         write_c_string(" driven by ", printcharfun);
295         switch (media_stream_driver(ms)) {
296         case MDRIVER_INTERNAL:
297                 write_c_string("internal", printcharfun);
298                 break;
299         case MDRIVER_FFMPEG:
300                 write_c_string("ffmpeg", printcharfun);
301                 break;
302         case MDRIVER_SNDFILE:
303                 write_c_string("sndfile", printcharfun);
304                 break;
305         case MDRIVER_MAD:
306                 write_c_string("mad", printcharfun);
307                 break;
308         case MDRIVER_SOX:
309                 write_c_string("sox", printcharfun);
310                 break;
311         case MDRIVER_XINE:
312                 write_c_string("xine", printcharfun);
313                 break;
314         case MDRIVER_GSTREAMER:
315                 write_c_string("gstreamer", printcharfun);
316                 break;
317         case MDRIVER_UNKNOWN:
318         case NUMBER_OF_MEDIA_DRIVERS:
319         default:
320                 XMEDIA_STREAM_SET_METHS(obj, NULL);
321                 write_c_string("unknown", printcharfun);
322                 break;
323         }
324
325         if (XMEDIA_STREAM_METHS(obj) &&
326             XMEDIA_STREAM_METH(obj, print)) {
327                 XMEDIA_STREAM_METH(obj, print)(obj, printcharfun, ef);
328         }
329
330         write_c_string(">", printcharfun);
331 }
332
333 static int
334 media_stream_equal(Lisp_Object obj1, Lisp_Object obj2, int depth)
335 {
336         if (XMEDIA_STREAM_DATA(obj1) == XMEDIA_STREAM_DATA(obj2))
337                 return Qt;
338         else
339                 return Qnil;
340
341         /* less warnings */
342         if (depth);
343 }
344
345 static unsigned long
346 media_stream_hash (Lisp_Object obj, int depth)
347 {
348         return (unsigned long)obj;
349
350         /* less warnings */
351         if (depth);
352 }
353
354 static const struct lrecord_description media_stream_description[] = {
355         { XD_LISP_OBJECT, offsetof(Lisp_Media_Stream, first) },
356         { XD_LISP_OBJECT, offsetof(Lisp_Media_Stream, last) },
357         { XD_INT, offsetof(Lisp_Media_Stream, kind) },
358         { XD_INT, offsetof(Lisp_Media_Stream, driver) },
359         { XD_OPAQUE_PTR, offsetof(Lisp_Media_Stream, kind_properties) },
360         { XD_OPAQUE_PTR, offsetof(Lisp_Media_Stream, stream_data) },
361         { XD_END }
362 };
363
364 DEFINE_LRECORD_IMPLEMENTATION("media_stream", media_stream,
365                               media_stream_mark, media_stream_print,
366                               media_stream_finalise,
367                               media_stream_equal, media_stream_hash,
368                               media_stream_description,
369                               Lisp_Media_Stream);
370
371 \f
372 /*****************************************************************/
373 /*                          media substreams                     */
374 /*****************************************************************/
375
376 static void
377 media_substream_finalise(void *header, int for_disksave)
378 {
379         media_substream *mss = (media_substream*)header;
380
381         switch (media_substream_type(mss)) {
382         case MTYPE_AUDIO:
383                 if (media_substream_type_properties(mss).aprops)
384                         xfree(media_substream_type_properties(mss).aprops);
385                 break;
386         case MTYPE_VIDEO:
387                 if (media_substream_type_properties(mss).vprops)
388                         xfree(media_substream_type_properties(mss).vprops);
389                 break;
390         case MTYPE_UNKNOWN:
391         case MTYPE_IMAGE:
392         case NUMBER_OF_MEDIA_TYPES:
393         default:
394                 break;
395         }
396
397 #ifdef HAVE_THREADS
398         pthread_mutex_destroy(&mss->substream_mutex);
399 #endif
400
401         media_substream_data(mss) = NULL;
402
403         /* avoid some warning */
404         if (for_disksave);
405 }
406
407 static void
408 media_substream_print_audio(media_substream *mss, Lisp_Object printcharfun)
409 {
410         mtype_audio_properties *mtap =
411                 media_substream_type_properties(mss).aprops;
412
413         write_c_string("#<audio ", printcharfun);
414         if (mtap->name || mtap->codec_name) {
415                 if (mtap->name && mtap->codec_name)
416                         write_fmt_str(printcharfun, "%s (%s)",
417                                       mtap->name, mtap->codec_name);
418                 else if (mtap->name)
419                         write_fmt_str(printcharfun, "%s [???]", mtap->name);
420                 else if (mtap->codec_name)
421                         write_fmt_str(printcharfun, "??? (%s)", mtap->codec_name);
422         } else
423                 write_c_string("???", printcharfun);
424
425         switch (mtap->channels) {
426         case 1:
427                 write_c_string(", mono", printcharfun);
428                 break;
429         case 2:
430                 write_c_string(", stereo", printcharfun);
431                 break;
432         case 5:
433                 write_c_string(", chn:5", printcharfun);
434                 break;
435         case 6:
436                 write_c_string(", 5.1", printcharfun);
437                 break;
438         default:
439                 write_c_string(", chn:???", printcharfun);
440                 break;
441         }
442
443         if (mtap->samplerate) 
444                 write_fmt_str(printcharfun, ", %d Hz, %d Bit",
445                               mtap->samplerate,
446                               mtap->samplewidth);
447
448         if (mtap->bitrate)
449                 write_fmt_str(printcharfun, ", %d kb/s", mtap->bitrate/1000);
450
451         write_c_string(">", printcharfun);
452 }
453
454 static void
455 media_substream_print_video(media_substream *mss, Lisp_Object printcharfun)
456 {
457         mtype_video_properties *mtvp =
458                 media_substream_type_properties(mss).vprops;
459
460         write_c_string("#<video ", printcharfun);
461         if (mtvp->name || mtvp->codec_name) {
462                 if (mtvp->name && mtvp->codec_name)
463                         write_fmt_str(printcharfun, "%s (%s)",
464                                       mtvp->name, mtvp->codec_name);
465                 else if (mtvp->name)
466                         write_fmt_str(printcharfun, "%s [???]", mtvp->name);
467                 else if (mtvp->codec_name)
468                         write_fmt_str(printcharfun, "??? (%s)", mtvp->codec_name);
469         } else
470                 write_c_string("???", printcharfun);
471
472         if (mtvp->bitrate)
473                 write_fmt_str(printcharfun, ", %d kb/s", mtvp->bitrate);
474
475         if (mtvp->width && mtvp->height) {
476                 if (mtvp->aspect_num > 1 && mtvp->aspect_den >= 1)
477                         write_fmt_str(printcharfun, ", %dx%d (%d/%d)",
478                                       mtvp->width, mtvp->height,
479                                       mtvp->aspect_num, mtvp->aspect_den);
480                 else
481                         write_fmt_str(printcharfun, ", %dx%d (%.2f/1)",
482                                       mtvp->width, mtvp->height,
483                                       (double)mtvp->width/(double)mtvp->height);
484         }
485         write_c_string(">", printcharfun);
486 }
487
488 static void
489 media_substream_print(media_substream *mss,
490                       Lisp_Object printcharfun, int escapeflag)
491 {
492         write_c_string("#<media-substream :type ", printcharfun);
493
494         switch (media_substream_type(mss)) {
495         case MTYPE_AUDIO: {
496                 media_substream_print_audio(mss, printcharfun);
497                 break;
498         }
499         case MTYPE_VIDEO:
500                 media_substream_print_video(mss, printcharfun);
501                 break;
502         case MTYPE_IMAGE:
503                 write_c_string("#<image>", printcharfun);
504                 break;
505         default:
506         case MTYPE_UNKNOWN:
507         case NUMBER_OF_MEDIA_TYPES:
508                 write_c_string("#<unknown>", printcharfun);
509                 break;
510         }
511
512         write_c_string(">", printcharfun);
513 }
514
515 static Lisp_Media_Stream *
516 media_stream_allocate(void)
517 {
518         Lisp_Media_Stream *ms;
519
520         ms = alloc_lcrecord_type(Lisp_Media_Stream, &lrecord_media_stream);
521         return ms;
522 }
523
524 \f
525 Lisp_Object make_media_stream()
526 {
527         Lisp_Media_Stream *ms;
528         Lisp_Object lms;
529
530         ms = media_stream_allocate();
531         media_stream_kind(ms) = MKIND_UNKNOWN;
532         media_stream_driver(ms) = MDRIVER_UNKNOWN;
533         media_stream_data(ms) = NULL;
534
535         /* now set the navigation */
536         media_stream_first(ms) = NULL;
537         media_stream_last(ms) = NULL;
538
539         XSETMEDIA_STREAM(lms, ms);
540
541         return lms;
542 }
543
544 media_substream *make_media_substream(void)
545 {
546 /* this allocates and conses to the back of ms */
547         media_substream *mss;
548
549         mss = xnew_and_zero(media_substream);
550         media_substream_type(mss) = MTYPE_UNKNOWN;
551         media_substream_data(mss) = NULL;
552
553         /* set next/prev */
554         media_substream_next(mss) = NULL;
555         media_substream_prev(mss) = NULL;
556
557 #ifdef HAVE_THREADS
558         pthread_mutex_init(&mss->substream_mutex, NULL);
559 #endif
560
561         return mss;
562 }
563
564 media_substream *make_media_substream_append(Lisp_Media_Stream *ms)
565 {
566         media_substream *mss;
567
568         mss = make_media_substream();
569
570         /* set next/prev */
571         media_substream_next(mss) = NULL;
572         if (!(media_stream_last(ms))) {
573                 media_substream_prev(mss) = NULL;
574                 media_stream_first(ms) = mss;
575         } else {
576                 media_substream_prev(mss) = media_stream_last(ms);
577                 media_substream_next(media_stream_last(ms)) = mss;
578         }
579
580         media_stream_last(ms) = mss;
581         media_substream_up(mss) = ms;
582
583         return mss;
584 }
585
586 media_substream *make_media_substream_prepend(Lisp_Media_Stream *ms)
587 {
588         media_substream *mss;
589
590         mss = make_media_substream();
591
592         /* set next/prev */
593         media_substream_prev(mss) = NULL;
594         if (!(media_stream_first(ms))) {
595                 media_substream_next(mss) = NULL;
596                 media_stream_last(ms) = mss;
597         } else {
598                 media_substream_next(mss) = media_stream_first(ms);
599                 media_substream_prev(media_stream_first(ms)) = mss;
600         }
601
602         media_stream_first(ms) = mss;
603         media_substream_up(mss) = ms;
604
605         return mss;
606 }
607
608 DEFUN("make-media-stream", Fmake_media_stream, 2, 3, 0, /*
609 Create a new media stream from DATA.
610
611 FROM is a keyword and defines how DATA is interpreted:
612 :file - DATA is the name of a file
613 :data - DATA is a string with the stream data
614 :url  - DATA is a url (string) for streamed media contents
615
616 Optional argument DRIVER (a symbol) may be used to force
617 the use of a certain driver instead of automatically
618 detecting a suitable one.  It is one of `ffmpeg', `sndfile',
619 `sox', `mad', `xine', `gstreamer', or `internal'.
620 */
621       (from, data, driver))
622 {
623         Lisp_Object lms;
624         Lisp_Media_Stream *ms;
625         enum whats_data {
626                 DATA_IS_BULLSHIT,
627                 DATA_IS_FILE,
628                 DATA_IS_URL,
629                 DATA_IS_DATA } datatype = DATA_IS_BULLSHIT;
630         media_driver pref = MDRIVER_UNKNOWN;
631
632         if (0);
633         else if (EQ(from, Q_file))
634                 datatype = DATA_IS_FILE;
635         else if (EQ(from, Q_data))
636                 datatype = DATA_IS_DATA;
637         else if (EQ(from, Q_url))
638                 datatype = DATA_IS_URL;
639         else {
640                 datatype = DATA_IS_BULLSHIT;
641                 return Qnil;    /* in this case, why bother? stupid user :) */
642         }
643
644         if (NILP(driver))
645                 pref = MDRIVER_UNKNOWN;
646 #ifdef HAVE_MAD
647         else if (EQ(driver, Qmad))
648                 pref = MDRIVER_MAD;
649 #endif
650 #ifdef HAVE_FFMPEG
651         else if (EQ(driver, Qffmpeg))
652                 pref = MDRIVER_FFMPEG;
653 #endif
654 #ifdef HAVE_SOX
655         else if (EQ(driver, Qsox))
656                 pref = MDRIVER_SOX;
657 #endif
658         else if (EQ(driver, intern("xine")))
659                 pref = MDRIVER_XINE;
660 #ifdef HAVE_SNDFILE
661         else if (EQ(driver, Qsndfile))
662                 pref = MDRIVER_SNDFILE;
663 #endif
664         else if (EQ(driver, intern("internal")))
665                 pref = MDRIVER_INTERNAL;
666         else if (EQ(driver, intern("gstreamer")))
667                 pref = MDRIVER_GSTREAMER;
668         else
669                 pref = MDRIVER_UNKNOWN;
670
671         /* hm, maybe data could be a symbol from the sound-alist?
672          * or a buffer or a network socket?
673          */
674         CHECK_STRING(data);
675
676         lms = make_media_stream();
677         ms = XMEDIA_STREAM(lms);
678
679         switch (datatype) {
680         case DATA_IS_FILE: {
681                 mkind_file_properties *fprops;
682
683                 /* expand-file-name first and check for existence*/
684                 data = Fexpand_file_name(data, Qnil);
685                 if (!NILP(Ffile_directory_p(data)) ||
686                     NILP(Ffile_readable_p(data)))
687                         break;
688
689                 media_stream_kind(ms) = MKIND_FILE;
690
691                 /* initialise a new file properties structure */
692                 fprops = xnew_and_zero(mkind_file_properties);
693
694                 /* copy the filename also as C string */
695                 fprops->filename = data;
696
697                 /* assign the file properties */
698                 media_stream_kind_properties(ms).fprops = fprops;
699
700                 determine_stream_type(ms, pref);
701                 break;
702         }
703         case DATA_IS_DATA: {
704                 mkind_string_properties *sprops;
705                 char *data_ext;
706                 int data_len = 0;
707
708                 media_stream_kind(ms) = MKIND_STRING;
709
710                 /* initialise a new file properties structure */
711                 sprops = xnew_and_zero(mkind_string_properties);
712
713                 /* copy the filename also as C string */
714                 TO_EXTERNAL_FORMAT(LISP_STRING, data,
715                                    MALLOC, (data_ext, data_len),
716                                    Qbinary);
717                 data_ext[data_len] = '\0';
718                 sprops->name = NULL;
719                 sprops->stream_data = data_ext;
720                 sprops->size = data_len;
721
722                 /* assign the file properties */
723                 media_stream_kind_properties(ms).sprops = sprops;
724
725                 determine_stream_type(ms, pref);
726                 break;
727         }
728         case DATA_IS_URL: {
729                 break;
730         }
731         case DATA_IS_BULLSHIT:
732         default:
733                 break;
734         }
735
736         return lms;
737 }
738
739 static Lisp_Object recons(Lisp_Object to, Lisp_Object from)
740 {
741         Lisp_Object result;
742
743         result = to;
744
745         while (!NILP(from)) {
746                 result = Fcons(XCAR(from), result);
747                 from = XCDR(from);
748         }
749         return result;
750 }
751
752 DEFUN("media-stream-p", Fmedia_stream_p, 1, 1, 0, /*
753 Return non-nil if object is a media-stream.
754 */
755       (object))
756 {
757         if (MEDIA_STREAMP(object))
758                 return Qt;
759         else
760                 return Qnil;
761 }
762
763 #if 0
764 DEFUN("audio-substream-p", Faudio_substream_p, 1, 1, 0, /*
765 Return non-nil if object is a media-substream with audio data.
766 */
767       (object))
768 {
769         if (MEDIA_SUBSTREAMP(object) &&
770             XMEDIA_SUBSTREAM_TYPE(object) == MTYPE_AUDIO)
771                 return Qt;
772         else
773                 return Qnil;
774 }
775
776 DEFUN("video-substream-p", Fvideo_substream_p, 1, 1, 0, /*
777 Return non-nil if object is a media-substream with video data.
778 */
779       (object))
780 {
781         if (MEDIA_SUBSTREAMP(object) &&
782             XMEDIA_SUBSTREAM_TYPE(object) == MTYPE_VIDEO)
783                 return Qt;
784         else
785                 return Qnil;
786 }
787 #endif  /* 0 */
788
789 DEFUN("media-available-formats", Fmedia_available_formats, 0, 0, 0, /*
790 Return a list of input formats in the underlying media libraries.
791 */
792       ())
793 {
794         Lisp_Object formats;
795         Lisp_Object temp;
796
797         formats = Qnil;
798         temp = Qnil;
799
800 #ifdef HAVE_FFMPEG
801         temp = media_ffmpeg_available_formats();
802 #endif
803         formats = recons(formats, temp);
804         
805 #ifdef HAVE_SNDFILE
806         temp = Qnil;
807 #endif
808         formats = recons(formats, temp);
809
810         return formats;
811 }
812
813 static inline void __attribute__((always_inline))
814 __add_prop(Lisp_Object *reslist, Lisp_Object key, Lisp_Object val)
815 {
816         *reslist = Fcons(Fcons(key, val), *reslist);
817         return;
818 }
819
820 static void
821 media_substream_props(media_substream *mss, Lisp_Object *reslist)
822 {
823         switch (media_substream_type(mss)) {
824         case MTYPE_AUDIO: {
825                 mtype_audio_properties *mtap =
826                         media_substream_type_properties(mss).aprops;
827
828                 /* add the type property */
829                 __add_prop(reslist, Qtype, Qaudio);
830
831                 if (mtap->name) {
832                         __add_prop(reslist, Qdemux, build_string(mtap->name));
833                 }
834                 if (mtap->codec_name) {
835                         __add_prop(reslist, Qcodec,
836                                    build_string(mtap->codec_name));
837                 }
838
839                 if (mtap->channels) {
840                         __add_prop(reslist, Qnchannels,
841                                    make_int(mtap->channels));
842                 }
843
844                 if (mtap->samplerate) {
845                         __add_prop(reslist, Qsamplerate,
846                                    make_int(mtap->samplerate));
847                 }
848
849                 if (mtap->bitrate) {
850                         __add_prop(reslist, Qabitrate, make_int(mtap->bitrate));
851                 }
852
853                 break;
854         }
855
856         case MTYPE_VIDEO: {
857                 mtype_video_properties *mtvp =
858                         media_substream_type_properties(mss).vprops;
859
860                 /* add the type property */
861                 __add_prop(reslist, Qtype, Qvideo);
862
863                 if (mtvp->name) {
864                         __add_prop(reslist, Qdemux, build_string(mtvp->name));
865                 }
866                 if (mtvp->codec_name) {
867                         __add_prop(reslist, Qcodec,
868                                    build_string(mtvp->codec_name));
869                 }
870
871                 if (mtvp->bitrate) {
872                         __add_prop(reslist, Qvbitrate, make_int(mtvp->bitrate));
873                 }
874
875                 if (mtvp->width) {
876                         __add_prop(reslist, Qwidth, make_int(mtvp->width));
877                 }
878
879                 if (mtvp->height) {
880                         __add_prop(reslist, Qheight, make_int(mtvp->height));
881                 }
882
883 #if defined HAVE_MPQ && defined WITH_GMP && 0
884                 if (mtvp->aspect_num > 1 && mtvp->aspect_den >= 1) {
885                         __add_prop(reslist, Qaspect,
886                                    make_bigq(mtvp->aspect_num,
887                                              mtvp->aspect_den));
888                 }
889 #elif defined HAVE_FPFLOAT
890 /* use float arithmetic */
891                 if (mtvp->aspect_num > 1 && mtvp->aspect_den >= 1) {
892                         __add_prop(reslist, Qaspect,
893                                    make_float((fpfloat)mtvp->aspect_num /
894                                               (fpfloat)mtvp->aspect_den));
895                 } else if (mtvp->width && mtvp->height) {
896                         __add_prop(reslist, Qaspect,
897                                    make_float((fpfloat)mtvp->width /
898                                               (fpfloat)mtvp->height));
899                 }
900 #endif
901
902                 break;
903         }
904         case MTYPE_IMAGE: {
905                 __add_prop(reslist, Qtype, Qimage);
906                 break;
907         }
908         default:
909         case MTYPE_UNKNOWN:
910         case NUMBER_OF_MEDIA_TYPES:
911                 break;
912         }
913 }
914
915 DEFUN("media-properties", Fmedia_properties, 1, 1, 0, /*
916 Return an alist of available properties of media-stream STREAM.
917
918 Depending on the underlying stream this alist may be made of
919 several of the following keys, grouped by media contents.
920
921 general:
922 -------
923 'file (string) the stream's filename
924 'uri (string) the stream's URI
925 'fifo (string) the stream's FIFO socket
926 'driver (symbol) the stream's demuxer/decoder driver
927 'kind (symbol) the stream's kind (file, fifo, string, uri, etc.)
928 'type (symbol) the stream's type (audio, video, image, subtitle, lyrics, etc.)
929
930 audio:
931 -----
932 'codec (string) the suitable audio codec
933 'demux (string) the suitable demuxer
934 'title (string) the title of an audio track
935 'artist (string) the performing artist(s)
936 'album (string) the title of the album
937 'comment (string) an arbitrary comment
938 'genre (string) the genre identifier string
939 'year (integer) the year of release
940 'track (integer) the track number on the album
941 'length (integer) the length of the track in seconds
942 'abitrate (integer) the average bitrate of the track in kb/s
943 'samplerate (integer) the samplerate of the track in Hz
944 'nchannels (integer) the number of distinguished channels
945
946 video:
947 -----
948 'codec (string) the suitable audio codec
949 'demux (string) the suitable demuxer
950 'title (string) the title of a video track
951 'comment (string) an arbitrary comment
952 'year (integer) the year of release
953 'vbitrate (integer) the average bitrate of the track in kb/s
954 'width (integer) the x-resolution in pixels
955 'height (integer) the y-resolution in pixels
956 'aspect (bigq) the aspect quotient
957
958 Keys which do not apply to the underlying stream or are not
959 defined in the tag section of the stream are simply left out
960 in the result alist.
961 */
962       (stream))
963 {
964         Lisp_Media_Stream *ms = XMEDIA_STREAM(stream);
965         media_substream *mss;
966         Lisp_Object resdl = Qnil;
967
968         switch (media_stream_kind(ms)) {
969         case MKIND_FILE: {
970                 Lisp_Object file =
971                         media_stream_kind_properties(ms).fprops->filename;
972
973                 __add_prop(&resdl, Qfile, file);
974                 __add_prop(&resdl, Qkind, Qfile);
975                 break;
976         }
977         case MKIND_FIFO: {
978                 __add_prop(&resdl, Qkind, Qfifo);
979                 break;
980         }
981         case MKIND_STREAM: {
982                 __add_prop(&resdl, Qkind, Quri);
983                 break;
984         }
985         case MKIND_STRING:
986                 __add_prop(&resdl, Qkind, Qstring);
987                 break;
988         case MKIND_UNKNOWN:
989         case NUMBER_OF_MEDIA_KINDS:
990         default:
991                 __add_prop(&resdl, Qkind, Qunknown);
992                 break;
993         }
994
995         __add_prop(&resdl, Qdriver, Qunavailable);
996         switch (media_stream_driver(ms)) {
997         case MDRIVER_INTERNAL:
998                 __add_prop(&resdl, Qdriver, Qinternal);
999                 break;
1000         case MDRIVER_FFMPEG:
1001 #ifdef HAVE_FFMPEG
1002                 __add_prop(&resdl, Qdriver, Qffmpeg);
1003 #if 0
1004                 streaminfo = media_ffmpeg_streaminfo(ms);
1005 #endif
1006 #endif
1007                 break;
1008         case MDRIVER_SNDFILE:
1009 #ifdef HAVE_SNDFILE
1010                 __add_prop(&resdl, Qdriver, Qsndfile);
1011 #endif
1012                 break;
1013         case MDRIVER_MAD:
1014 #ifdef HAVE_MAD
1015                 __add_prop(&resdl, Qdriver, Qmad);
1016 #endif
1017                 break;
1018         case MDRIVER_SOX:
1019 #ifdef HAVE_SOX
1020                 __add_prop(&resdl, Qdriver, Qsox);
1021 #endif
1022                 break;
1023
1024         case MDRIVER_XINE:
1025                 break;
1026
1027         case MDRIVER_GSTREAMER:
1028                 break;
1029
1030         case MDRIVER_UNKNOWN:
1031         case NUMBER_OF_MEDIA_DRIVERS:
1032         default:
1033                 __add_prop(&resdl, Qdriver, Qunknown);
1034                 break;
1035         }
1036
1037 #if 0
1038         if (streaminfo) {
1039                 xfree(streaminfo);
1040         }
1041 #endif
1042
1043         mss = media_stream_first(ms);
1044         while (mss) {
1045                 media_substream_props(mss, &resdl);
1046                 mss = media_substream_next(mss);
1047         }
1048
1049         return resdl;
1050 }
1051
1052 /* convenience functions */
1053 DEFUN("media-stream-aspect", Fmedia_stream_aspect, 1, 3, 0, /*
1054 Return a list of aspect ratios in media stream STREAM.
1055
1056 Optional argument SUBSTREAM names the index of a specific (video)
1057 substream, i.e. only video streams are counted, i.e. the substream 0
1058 names the first video track inside STREAM.
1059 The return value in that case is just a float or quotient, depending
1060 on the optional argument QUOTIENTP.
1061
1062 Optional argument QUOTIENTP indicates that aspect ratios are to be
1063 returned as bigq quotients instead of floats (the default).
1064
1065 By default, the aspect ratios of all the video tracks appear in the list,
1066 the first element in the list belongs to the first video track,
1067 the second element to the second one and so on.
1068 */
1069       (stream, substream, quotientp))
1070 {
1071         Lisp_Media_Stream *ms = XMEDIA_STREAM(stream);
1072         Lisp_Object res = Qnil;
1073         long int idx = INTP(substream) ? XINT(substream) : -1;
1074
1075         for (media_substream *mss = media_stream_first(ms); mss;
1076              (mss = media_substream_next(mss))) {
1077                 mtype_video_properties *mtvp;
1078                 long int num = 0, den = 0;
1079                 Lisp_Object tmp;
1080
1081                 if (LIKELY(media_substream_type(mss) != MTYPE_VIDEO)) {
1082                         continue;
1083                 }
1084
1085                 /* oh, it's a video stream, check if it's the one we wanted */
1086                 if (UNLIKELY(idx--  > 0)) {
1087                         continue;
1088                 }
1089
1090                 mtvp = media_substream_type_properties(mss).vprops;
1091                 if (mtvp->aspect_num > 1 && mtvp->aspect_den >= 1) {
1092                         num = mtvp->aspect_num;
1093                         den = mtvp->aspect_den;
1094                 } else if (mtvp->width && mtvp->height) {
1095                         /* good enough? just an approximation as
1096                          * the aspect ratio may very well differ
1097                          * from the width-by-height ratio */
1098                         num = mtvp->width;
1099                         den = mtvp->height;
1100                 }
1101                         
1102                 if (LIKELY(NILP(quotientp))) {
1103                         tmp = make_float((fpfloat)num / (fpfloat)den);
1104                 } 
1105 #if defined HAVE_MPQ && defined WITH_GMP
1106                 else {
1107                         tmp = make_bigq(num, den);
1108                 }
1109 #else
1110                 else {
1111                   dead_wrong_type_argument(Qfeaturep,quotientp);
1112                 }
1113 #endif
1114                 if (UNLIKELY(INTP(substream))) {
1115                         return tmp;
1116                 }
1117                 /* the very very default case */
1118                 res = Fcons(tmp, res);
1119         }
1120         return res;
1121 }
1122
1123 \f
1124 /* Audio Coercion */
1125 /* SXEmacs works internally with samples in 24bit resolution */
1126
1127 DEFINE_MEDIA_SAMPLE_FORMAT_SIMPLE(sxe_msf_U8);
1128
1129 static void
1130 sxe_msf_U8_up(void *d, void *s, size_t len)
1131 {
1132         /* convert U8 samples to internal format (S24in32) */
1133         int i;
1134         int32_t *dst = d;
1135         uint8_t *src = s;
1136
1137         /* len is the number of samples (== #frame * #channels) */
1138         MEDIA_DEBUG_FMT("upsampling U8->internal: %u samples\n", len);
1139
1140         for (i = len-1; i >= 0; i--)
1141                 dst[i] = (int32_t)(src[i] ^ 0x80) << 16;
1142
1143         return;
1144 }
1145
1146 static void
1147 sxe_msf_U8_down(void *d, void *s, size_t len)
1148 {
1149         /* convert samples from internal format (S24in32) to U8 */
1150         size_t i;
1151         uint8_t *dst = d;
1152         int32_t *src = s;
1153
1154         /* len is the number of samples (== #frame * #channels) */
1155         MEDIA_DEBUG_FMT("downsampling internal->U8: %u samples\n", len);
1156
1157         for (i = 0; i < len; i++)
1158                 dst[i] = (uint8_t)(src[i] >> 16) ^ 0x80;
1159
1160         return;
1161 }
1162
1163 DEFINE_MEDIA_SAMPLE_FORMAT_SIMPLE(sxe_msf_S16);
1164
1165 static void
1166 sxe_msf_S16_up(void *d, void *s, size_t len)
1167 {
1168         /* convert S16 samples to internal format (S24in32) */
1169         int i;
1170         int32_t *dst = d;
1171         int16_t *src = s;
1172
1173         /* len is the number of samples (== #frame * #channels) */
1174         MEDIA_DEBUG_FMT("upsampling S16->internal: %u samples\n", len);
1175
1176         for (i = len-1; i >= 0; i--)
1177                 dst[i] = (int32_t)(src[i]) << 8;
1178         MEDIA_DEBUG_FMT("d00:%d  d01:%d\n", dst[0], dst[1]);
1179
1180         return;
1181 }
1182
1183 static void
1184 sxe_msf_S16_down(void *d, void *s, size_t len)
1185 {
1186         /* convert samples from internal format (S24in32) to S16 */
1187         size_t i;
1188         int16_t *dst = d;
1189         int32_t *src = s;
1190
1191         /* len is the number of samples (== #frame * #channels) */
1192         MEDIA_DEBUG_FMT("downsampling internal->S16: %u samples\n", len);
1193
1194         for (i = 0; i < len; i++)
1195                 dst[i] = (int16_t)(src[i] >> 8);
1196
1197         return;
1198 }
1199
1200 DEFINE_MEDIA_SAMPLE_FORMAT_SIMPLE(sxe_msf_S24); /* format internally used */
1201
1202 static void
1203 sxe_msf_S24_up(void *d, void *s, size_t len)
1204 {
1205         MEDIA_DEBUG_FMT("upsampling S24->internal: %u samples\n", len);
1206
1207         /* S24 _is_ the internal format */
1208         return;
1209 }
1210
1211 static void
1212 sxe_msf_S24_down(void *d, void *s, size_t len)
1213 {
1214         MEDIA_DEBUG_FMT("downsampling internal->S24: %u samples\n", len);
1215
1216         /* S24 _is_ the internal format */
1217         return;
1218 }
1219
1220 DEFINE_MEDIA_SAMPLE_FORMAT_SIMPLE(sxe_msf_S32);
1221
1222 static void
1223 sxe_msf_S32_up(void *d, void *s, size_t len)
1224 {
1225         /* convert S32 samples to internal format (S24in32) */
1226         size_t i;
1227         int32_t *dst = d;
1228         int32_t *src = s;
1229
1230         /* len is the number of samples (== #frame * #channels) */
1231         MEDIA_DEBUG_FMT("upsampling S32->internal: %u samples\n", len);
1232
1233         for (i = 0; i < len; i++)
1234                 dst[i] = src[i] >> 8;
1235
1236         return;
1237 }
1238
1239 static void
1240 sxe_msf_S32_down(void *d, void *s, size_t len)
1241 {
1242         /* convert samples from internal format (S24in32) to S32 */
1243         size_t i;
1244         int32_t *dst = d;
1245         int32_t *src = s;
1246
1247         /* len is the number of samples (== #frame * #channels) */
1248         MEDIA_DEBUG_FMT("downsampling internal->S32: %u samples\n", len);
1249
1250         for (i = 0; i < len; i++)
1251                 dst[i] = src[i] << 8;
1252
1253         return;
1254 }
1255
1256 DEFINE_MEDIA_SAMPLE_FORMAT_SIMPLE(sxe_msf_FLT);
1257
1258 static void
1259 sxe_msf_FLT_up(void *d, void *s, size_t len)
1260 {
1261         /* convert float samples to internal format (S24in32) */
1262         size_t i;
1263         int32_t *dst = d;
1264         float *src = s;
1265
1266         /* len is the number of samples (== #frame * #channels) */
1267         MEDIA_DEBUG_FMT("upsampling FLT->internal: %u samples\n", len);
1268
1269         for (i = 0; i < len; i++) {
1270                 dst[i] = (int32_t)(src[i] * SXE_MAX_S24);
1271         }
1272         MEDIA_DEBUG_FMT("s00:%f d00:%d  s01:%f d01:%d\n",
1273                         src[0], dst[0], src[1], dst[1]);
1274
1275         return;
1276 }
1277
1278 static void
1279 sxe_msf_FLT_down(void *d, void *s, size_t len)
1280 {
1281         /* convert samples from internal format (S24in32) to float */
1282         int i;
1283         float *dst = d;
1284         int32_t *src = s;
1285
1286         /* len is the number of samples (== #frame * #channels) */
1287         MEDIA_DEBUG_FMT("downsampling internal->FLT: %u samples\n", len);
1288
1289         for (i = len-1; i >= 0; i--) {
1290                 dst[i] = (float)(src[i]) / SXE_MAX_S24;
1291         }
1292         MEDIA_DEBUG_FMT("d00:%f  d01:%f\n", dst[0], dst[1]);
1293
1294         return;
1295 }
1296
1297 /* `effects' */
1298 DEFINE_MEDIA_SAMPLE_EFFECT(sxe_mse_1ch_to_2ch, _sxe_mse_1ch_to_2ch);
1299
1300 static size_t
1301 _sxe_mse_1ch_to_2ch(sxe_media_sample_t *dst, sxe_media_sample_t *src,
1302                     size_t len, void *ignored)
1303 {
1304         /* mono to stereo converter */
1305         int i;
1306
1307         MEDIA_DEBUG_COE("mono->stereo: %u samples\n", len);
1308
1309         /* len is the number of samples */
1310         for (i = len-1; i >= 0; i--) {
1311                 dst[2*i] = src[i];
1312                 dst[2*i+1] = src[i];
1313         }
1314
1315         return len * 2;
1316 }
1317
1318 DEFINE_MEDIA_SAMPLE_EFFECT(sxe_mse_2ch_to_1ch, _sxe_mse_2ch_to_1ch);
1319
1320 static size_t
1321 _sxe_mse_2ch_to_1ch(sxe_media_sample_t *dst, sxe_media_sample_t *src,
1322                     size_t len, void *args)
1323 {
1324         /* stereo to mono converter */
1325         size_t i;
1326         sxe_mse_2ch_to_1ch_args *_args = args;
1327         int c = 0;
1328
1329         MEDIA_DEBUG_COE("stereo->mono: %u samples\n", len);
1330
1331         if (_args) {
1332                 c = _args->chan;
1333         }
1334
1335         /* len is the number of samples */
1336         for (i = 0; i < len/2; i++) {
1337                 dst[i] = src[2*i+c];
1338         }
1339
1340         return len / 2;
1341 }
1342
1343 DEFINE_MEDIA_SAMPLE_EFFECT(sxe_mse_5ch_to_2ch, _sxe_mse_5ch_to_2ch);
1344
1345 static size_t
1346 _sxe_mse_5ch_to_2ch(sxe_media_sample_t *dst, sxe_media_sample_t *src,
1347                     size_t len, void *args)
1348 {
1349         /* 5 channel to stereo converter */
1350         size_t i;
1351         sxe_mse_5ch_to_2ch_args *_args = args;
1352         int c1 = 0, c2 = 1;
1353
1354         MEDIA_DEBUG_COE("5ch->stereo: %u samples\n", len);
1355
1356         if (_args) {
1357                 c1 = _args->chan1;
1358                 c2 = _args->chan1;
1359         }
1360
1361         /* len is the number of samples */
1362         for (i = 0; i < len/5; i++) {
1363                 dst[2*i] = src[5*i+c1];
1364                 dst[2*i+1] = src[5*i+c2];
1365         }
1366
1367         return len * 2 / 5;
1368 }
1369
1370 DEFINE_MEDIA_SAMPLE_EFFECT(sxe_mse_volume, _sxe_mse_volume);
1371
1372 static size_t
1373 _sxe_mse_volume(sxe_media_sample_t *dst, sxe_media_sample_t *src,
1374                 size_t len, void *args)
1375 {
1376         /* stereo to mono converter */
1377         size_t i, j;
1378         sxe_mse_volume_args *_args = args;
1379
1380         MEDIA_DEBUG_COE("volume: %u samples\n", len);
1381
1382         /* len is the number of samples */
1383         for (i = 0; i < len; i+=_args->num_channels) {
1384                 for (j = 0; j < (size_t)_args->num_channels; j++) {
1385                         uint8_t vol = _args->volume[j];
1386                         dst[i+j] = (src[i+j] * vol) >> 7;
1387                 }
1388         }
1389
1390         return len;
1391 }
1392
1393 DEFINE_MEDIA_SAMPLE_EFFECT(sxe_mse_rerate, _sxe_mse_rerate);
1394
1395 static size_t
1396 _sxe_mse_rerate(sxe_media_sample_t *dst, sxe_media_sample_t *src,
1397                 size_t len, void *args)
1398 {
1399         /* rate converter */
1400         int i;
1401         sxe_mse_rerate_args *_args = args;
1402         float trafo = (float)_args->srcrate / (float)_args->tgtrate
1403                 * _args->tweak;
1404         int chans = _args->num_channels;
1405         int bound = len/chans/trafo;
1406
1407         MEDIA_DEBUG_COE("rerate: %u samples, final trafo: %f, bound is: %d\n",
1408                         len, trafo, bound);
1409
1410         if (trafo < 1.0) {
1411                 for (i = bound-1; i >= 0; i--) {
1412                         int frame = i * trafo;
1413                         dst[chans*i] = (src[chans*frame]);
1414                         dst[chans*i+1] = (src[chans*frame+1]);
1415                 }
1416         } else if (trafo > 1.0) {
1417                 for (i = 0; i < bound-1; i++) {
1418                         int frame = i * trafo;
1419                         dst[chans*i] = (src[chans*frame]);
1420                         dst[chans*i+1] = (src[chans*frame+1]);
1421                 }
1422         }
1423
1424         return bound * chans;
1425 }
1426
1427 \f
1428 void syms_of_media(void)
1429 {
1430         INIT_LRECORD_IMPLEMENTATION(media_stream);
1431
1432         defsymbol(&Qmedia_streamp, "media-stream-p");
1433
1434         DEFSYMBOL(Qtype);
1435         DEFSYMBOL(Qdemux);
1436         DEFSYMBOL(Qcodec);
1437         DEFSYMBOL(Qnchannels);
1438         DEFSYMBOL(Qsamplerate);
1439         DEFSYMBOL(Qbitrate);
1440         DEFSYMBOL(Qabitrate);
1441         DEFSYMBOL(Qvbitrate);
1442         DEFSYMBOL(Qwidth);
1443         DEFSYMBOL(Qheight);
1444         DEFSYMBOL(Qaspect);
1445         DEFSYMBOL(Qaudio);
1446         DEFSYMBOL(Qvideo);
1447         DEFSYMBOL(Qimage);
1448         DEFSYMBOL(Qdriver);
1449         DEFSYMBOL(Qkind);
1450         DEFSYMBOL(Qfifo);
1451         DEFSYMBOL(Quri);
1452
1453         DEFSUBR(Fmake_media_stream);
1454         DEFSUBR(Fmedia_stream_p);
1455 #if 0
1456         DEFSUBR(Faudio_substream_p);
1457         DEFSUBR(Fvideo_substream_p);
1458 #endif
1459
1460         DEFSUBR(Fmedia_available_formats);
1461         DEFSUBR(Fmedia_properties);
1462         DEFSUBR(Fmedia_stream_aspect);
1463
1464 #ifdef HAVE_FFMPEG
1465         defsymbol(&Qffmpeg, "ffmpeg");
1466 #endif
1467 #ifdef HAVE_MAD
1468         defsymbol(&Qmad, "mad");
1469 #endif
1470 #ifdef HAVE_SOX
1471         defsymbol(&Qsox, "sox");
1472 #endif
1473 #ifdef HAVE_SNDFILE
1474         defsymbol(&Qsndfile, "sndfile");
1475 #endif
1476         defsymbol(&Qunknown, "unknown");
1477         defsymbol(&Qunavailable, "unavailable");
1478 }
1479
1480 void vars_of_media(void)
1481 {
1482         Fprovide(intern("media"));
1483
1484 #ifdef HAVE_FFMPEG
1485         Fprovide(intern("media-ffmpeg"));
1486 #endif
1487 #ifdef HAVE_SNDFILE
1488         Fprovide(intern("media-sndfile"));
1489 #endif
1490 #ifdef HAVE_MAD
1491         Fprovide(intern("media-mad"));
1492 #endif
1493 #ifdef HAVE_SOX
1494         Fprovide(intern("media-sox"));
1495 #endif
1496 #ifdef HAVE_XINE
1497         Fprovide(intern("media-xine"));
1498 #endif
1499 #ifdef HAVE_GSTREAMER
1500         Fprovide(intern("media-gstreamer"));
1501 #endif
1502 #ifdef HAVE_INTERNAL_MEDIA
1503         Fprovide(intern("media-internal"));
1504 #endif
1505 }
1506
1507 /* media.c ends here */