Build Fix -- compatibility issue with newer autoconf
[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 #include <ent/ent.h>
25
26 #include "buffer.h"
27
28 #include "media.h"
29 #include "sound.h"
30 #ifdef HAVE_FFMPEG
31 #include "media-ffmpeg.h"
32 #endif
33 #ifdef HAVE_SNDFILE
34 #include "media-sndfile.h"
35 #endif
36 #ifdef HAVE_INTERNAL_MEDIA
37 #include "media-internal.h"
38 #endif
39 #ifdef HAVE_MAD
40 #include "media-mad.h"
41 #endif
42 #ifdef HAVE_GSTREAMER
43 #include "media-gstreamer.h"
44 #endif
45 #ifdef HAVE_SOX
46 #include "media-sox.h"
47 #endif
48 #ifdef HAVE_XINE
49 #include "media-xine.h"
50 #endif
51
52 Lisp_Object Qmedia_streamp;
53 Lisp_Object Qunavailable;
54 /* media property syms */
55 Lisp_Object Qdemux, Qcodec, Qnchannels, Qsamplerate;
56 Lisp_Object Qbitrate, Qabitrate, Qvbitrate;
57 Lisp_Object Qaspect, Qdriver, Qkind, Qfifo, Quri;
58 Lisp_Object Qaudio, Qvideo;
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 = NULL;
706                 int data_len = 0;
707
708                 /* copy the filename also as C string */
709                 TO_EXTERNAL_FORMAT(LISP_STRING, data,
710                                    MALLOC, (data_ext, data_len),
711                                    Qbinary);
712
713                 if (data_ext != NULL) {
714                         media_stream_kind(ms) = MKIND_STRING;
715
716                         /* initialise a new file properties structure */
717                         sprops = xnew_and_zero(mkind_string_properties);
718
719                         data_ext[data_len] = '\0';
720                         sprops->name = NULL;
721                         sprops->stream_data = data_ext;
722                         sprops->size = data_len;
723
724                         /* assign the file properties */
725                         media_stream_kind_properties(ms).sprops = sprops;
726
727                         determine_stream_type(ms, pref);
728                 }
729                 break;
730         }
731         case DATA_IS_URL: {
732                 break;
733         }
734         case DATA_IS_BULLSHIT:
735         default:
736                 break;
737         }
738
739         return lms;
740 }
741
742 static Lisp_Object recons(Lisp_Object to, Lisp_Object from)
743 {
744         Lisp_Object result;
745
746         result = to;
747
748         while (!NILP(from)) {
749                 result = Fcons(XCAR(from), result);
750                 from = XCDR(from);
751         }
752         return result;
753 }
754
755 DEFUN("media-stream-p", Fmedia_stream_p, 1, 1, 0, /*
756 Return non-nil if object is a media-stream.
757 */
758       (object))
759 {
760         if (MEDIA_STREAMP(object))
761                 return Qt;
762         else
763                 return Qnil;
764 }
765
766 #if 0
767 DEFUN("audio-substream-p", Faudio_substream_p, 1, 1, 0, /*
768 Return non-nil if object is a media-substream with audio data.
769 */
770       (object))
771 {
772         if (MEDIA_SUBSTREAMP(object) &&
773             XMEDIA_SUBSTREAM_TYPE(object) == MTYPE_AUDIO)
774                 return Qt;
775         else
776                 return Qnil;
777 }
778
779 DEFUN("video-substream-p", Fvideo_substream_p, 1, 1, 0, /*
780 Return non-nil if object is a media-substream with video data.
781 */
782       (object))
783 {
784         if (MEDIA_SUBSTREAMP(object) &&
785             XMEDIA_SUBSTREAM_TYPE(object) == MTYPE_VIDEO)
786                 return Qt;
787         else
788                 return Qnil;
789 }
790 #endif  /* 0 */
791
792 DEFUN("media-available-formats", Fmedia_available_formats, 0, 0, 0, /*
793 Return a list of input formats in the underlying media libraries.
794 */
795       ())
796 {
797         Lisp_Object formats;
798         Lisp_Object temp;
799
800         formats = Qnil;
801         temp = Qnil;
802
803 #ifdef HAVE_FFMPEG
804         temp = media_ffmpeg_available_formats();
805 #endif
806         formats = recons(formats, temp);
807
808 #ifdef HAVE_SNDFILE
809         temp = Qnil;
810 #endif
811         formats = recons(formats, temp);
812
813         return formats;
814 }
815
816 static inline void __attribute__((always_inline))
817 __add_prop(Lisp_Object *reslist, Lisp_Object key, Lisp_Object val)
818 {
819         *reslist = Fcons(Fcons(key, val), *reslist);
820         return;
821 }
822
823 static void
824 media_substream_props(media_substream *mss, Lisp_Object *reslist)
825 {
826         switch (media_substream_type(mss)) {
827         case MTYPE_AUDIO: {
828                 mtype_audio_properties *mtap =
829                         media_substream_type_properties(mss).aprops;
830
831                 /* add the type property */
832                 __add_prop(reslist, Qtype, Qaudio);
833
834                 if (mtap->name) {
835                         __add_prop(reslist, Qdemux, build_string(mtap->name));
836                 }
837                 if (mtap->codec_name) {
838                         __add_prop(reslist, Qcodec,
839                                    build_string(mtap->codec_name));
840                 }
841
842                 if (mtap->channels) {
843                         __add_prop(reslist, Qnchannels,
844                                    make_int(mtap->channels));
845                 }
846
847                 if (mtap->samplerate) {
848                         __add_prop(reslist, Qsamplerate,
849                                    make_int(mtap->samplerate));
850                 }
851
852                 if (mtap->bitrate) {
853                         __add_prop(reslist, Qabitrate, make_int(mtap->bitrate));
854                 }
855
856                 break;
857         }
858
859         case MTYPE_VIDEO: {
860                 mtype_video_properties *mtvp =
861                         media_substream_type_properties(mss).vprops;
862
863                 /* add the type property */
864                 __add_prop(reslist, Qtype, Qvideo);
865
866                 if (mtvp->name) {
867                         __add_prop(reslist, Qdemux, build_string(mtvp->name));
868                 }
869                 if (mtvp->codec_name) {
870                         __add_prop(reslist, Qcodec,
871                                    build_string(mtvp->codec_name));
872                 }
873
874                 if (mtvp->bitrate) {
875                         __add_prop(reslist, Qvbitrate, make_int(mtvp->bitrate));
876                 }
877
878                 if (mtvp->width) {
879                         __add_prop(reslist, Qwidth, make_int(mtvp->width));
880                 }
881
882                 if (mtvp->height) {
883                         __add_prop(reslist, Qheight, make_int(mtvp->height));
884                 }
885
886 #if defined HAVE_MPQ && defined WITH_GMP && 0
887                 if (mtvp->aspect_num > 1 && mtvp->aspect_den >= 1) {
888                         __add_prop(reslist, Qaspect,
889                                    make_bigq(mtvp->aspect_num,
890                                              mtvp->aspect_den));
891                 }
892 #elif defined HAVE_FPFLOAT
893 /* use float arithmetic */
894                 if (mtvp->aspect_num > 1 && mtvp->aspect_den >= 1) {
895                         __add_prop(reslist, Qaspect,
896                                    make_float((fpfloat)mtvp->aspect_num /
897                                               (fpfloat)mtvp->aspect_den));
898                 } else if (mtvp->width && mtvp->height) {
899                         __add_prop(reslist, Qaspect,
900                                    make_float((fpfloat)mtvp->width /
901                                               (fpfloat)mtvp->height));
902                 }
903 #endif
904
905                 break;
906         }
907         case MTYPE_IMAGE: {
908                 __add_prop(reslist, Qtype, Qimage);
909                 break;
910         }
911         default:
912         case MTYPE_UNKNOWN:
913         case NUMBER_OF_MEDIA_TYPES:
914                 break;
915         }
916 }
917
918 DEFUN("media-properties", Fmedia_properties, 1, 1, 0, /*
919 Return an alist of available properties of media-stream STREAM.
920
921 Depending on the underlying stream this alist may be made of
922 several of the following keys, grouped by media contents.
923
924 general:
925 -------
926 'file (string) the stream's filename
927 'uri (string) the stream's URI
928 'fifo (string) the stream's FIFO socket
929 'driver (symbol) the stream's demuxer/decoder driver
930 'kind (symbol) the stream's kind (file, fifo, string, uri, etc.)
931 'type (symbol) the stream's type (audio, video, image, subtitle, lyrics, etc.)
932
933 audio:
934 -----
935 'codec (string) the suitable audio codec
936 'demux (string) the suitable demuxer
937 'title (string) the title of an audio track
938 'artist (string) the performing artist(s)
939 'album (string) the title of the album
940 'comment (string) an arbitrary comment
941 'genre (string) the genre identifier string
942 'year (integer) the year of release
943 'track (integer) the track number on the album
944 'length (integer) the length of the track in seconds
945 'abitrate (integer) the average bitrate of the track in kb/s
946 'samplerate (integer) the samplerate of the track in Hz
947 'nchannels (integer) the number of distinguished channels
948
949 video:
950 -----
951 'codec (string) the suitable audio codec
952 'demux (string) the suitable demuxer
953 'title (string) the title of a video track
954 'comment (string) an arbitrary comment
955 'year (integer) the year of release
956 'vbitrate (integer) the average bitrate of the track in kb/s
957 'width (integer) the x-resolution in pixels
958 'height (integer) the y-resolution in pixels
959 'aspect (bigq) the aspect quotient
960
961 Keys which do not apply to the underlying stream or are not
962 defined in the tag section of the stream are simply left out
963 in the result alist.
964 */
965       (stream))
966 {
967         Lisp_Media_Stream *ms = XMEDIA_STREAM(stream);
968         media_substream *mss;
969         Lisp_Object resdl = Qnil;
970
971         switch (media_stream_kind(ms)) {
972         case MKIND_FILE: {
973                 Lisp_Object file =
974                         media_stream_kind_properties(ms).fprops->filename;
975
976                 __add_prop(&resdl, Qfile, file);
977                 __add_prop(&resdl, Qkind, Qfile);
978                 break;
979         }
980         case MKIND_FIFO: {
981                 __add_prop(&resdl, Qkind, Qfifo);
982                 break;
983         }
984         case MKIND_STREAM: {
985                 __add_prop(&resdl, Qkind, Quri);
986                 break;
987         }
988         case MKIND_STRING:
989                 __add_prop(&resdl, Qkind, Qstring);
990                 break;
991         case MKIND_UNKNOWN:
992         case NUMBER_OF_MEDIA_KINDS:
993         default:
994                 __add_prop(&resdl, Qkind, Qunknown);
995                 break;
996         }
997
998         __add_prop(&resdl, Qdriver, Qunavailable);
999         switch (media_stream_driver(ms)) {
1000         case MDRIVER_INTERNAL:
1001                 __add_prop(&resdl, Qdriver, Qinternal);
1002                 break;
1003         case MDRIVER_FFMPEG:
1004 #ifdef HAVE_FFMPEG
1005                 __add_prop(&resdl, Qdriver, Qffmpeg);
1006 #if 0
1007                 streaminfo = media_ffmpeg_streaminfo(ms);
1008 #endif
1009 #endif
1010                 break;
1011         case MDRIVER_SNDFILE:
1012 #ifdef HAVE_SNDFILE
1013                 __add_prop(&resdl, Qdriver, Qsndfile);
1014 #endif
1015                 break;
1016         case MDRIVER_MAD:
1017 #ifdef HAVE_MAD
1018                 __add_prop(&resdl, Qdriver, Qmad);
1019 #endif
1020                 break;
1021         case MDRIVER_SOX:
1022 #ifdef HAVE_SOX
1023                 __add_prop(&resdl, Qdriver, Qsox);
1024 #endif
1025                 break;
1026
1027         case MDRIVER_XINE:
1028                 break;
1029
1030         case MDRIVER_GSTREAMER:
1031                 break;
1032
1033         case MDRIVER_UNKNOWN:
1034         case NUMBER_OF_MEDIA_DRIVERS:
1035         default:
1036                 __add_prop(&resdl, Qdriver, Qunknown);
1037                 break;
1038         }
1039
1040 #if 0
1041         if (streaminfo) {
1042                 xfree(streaminfo);
1043         }
1044 #endif
1045
1046         mss = media_stream_first(ms);
1047         while (mss) {
1048                 media_substream_props(mss, &resdl);
1049                 mss = media_substream_next(mss);
1050         }
1051
1052         return resdl;
1053 }
1054
1055 /* convenience functions */
1056 DEFUN("media-stream-aspect", Fmedia_stream_aspect, 1, 3, 0, /*
1057 Return a list of aspect ratios in media stream STREAM.
1058
1059 Optional argument SUBSTREAM names the index of a specific (video)
1060 substream, i.e. only video streams are counted, i.e. the substream 0
1061 names the first video track inside STREAM.
1062 The return value in that case is just a float or quotient, depending
1063 on the optional argument QUOTIENTP.
1064
1065 Optional argument QUOTIENTP indicates that aspect ratios are to be
1066 returned as bigq quotients instead of floats (the default).
1067
1068 By default, the aspect ratios of all the video tracks appear in the list,
1069 the first element in the list belongs to the first video track,
1070 the second element to the second one and so on.
1071 */
1072       (stream, substream, quotientp))
1073 {
1074         Lisp_Media_Stream *ms = XMEDIA_STREAM(stream);
1075         Lisp_Object res = Qnil;
1076         long int idx = INTP(substream) ? XINT(substream) : -1;
1077
1078         for (media_substream *mss = media_stream_first(ms); mss;
1079              (mss = media_substream_next(mss))) {
1080                 mtype_video_properties *mtvp;
1081                 long int num = 0, den = 0;
1082                 Lisp_Object tmp = Qnil;
1083
1084                 if (LIKELY(media_substream_type(mss) != MTYPE_VIDEO)) {
1085                         continue;
1086                 }
1087
1088                 /* oh, it's a video stream, check if it's the one we wanted */
1089                 if (UNLIKELY(idx--  > 0)) {
1090                         continue;
1091                 }
1092
1093                 mtvp = media_substream_type_properties(mss).vprops;
1094                 if (mtvp->aspect_num > 1 && mtvp->aspect_den >= 1) {
1095                         num = mtvp->aspect_num;
1096                         den = mtvp->aspect_den;
1097                 } else if (mtvp->width && mtvp->height) {
1098                         /* good enough? just an approximation as
1099                          * the aspect ratio may very well differ
1100                          * from the width-by-height ratio */
1101                         num = mtvp->width;
1102                         den = mtvp->height;
1103                 }
1104
1105                 if (LIKELY(NILP(quotientp))) {
1106                         assert(den != 0);
1107                         if( den != 0)
1108                                 tmp = make_float((fpfloat)num / (fpfloat)den);
1109                 }
1110 #if defined HAVE_MPQ && defined WITH_GMP
1111                 else {
1112                         tmp = make_bigq(num, den);
1113                 }
1114 #else
1115                 else {
1116                   dead_wrong_type_argument(Qfeaturep,quotientp);
1117                 }
1118 #endif
1119                 if (UNLIKELY(INTP(substream))) {
1120                         return tmp;
1121                 }
1122                 /* the very very default case */
1123                 res = Fcons(tmp, res);
1124         }
1125         return res;
1126 }
1127
1128 \f
1129 /* Audio Coercion */
1130 /* SXEmacs works internally with samples in 24bit resolution */
1131
1132 DEFINE_MEDIA_SAMPLE_FORMAT_SIMPLE(sxe_msf_U8);
1133
1134 static void
1135 sxe_msf_U8_up(void *d, void *s, size_t len)
1136 {
1137         /* convert U8 samples to internal format (S24in32) */
1138         int i;
1139         int32_t *dst = d;
1140         uint8_t *src = s;
1141
1142         /* len is the number of samples (== #frame * #channels) */
1143         MEDIA_DEBUG_FMT("upsampling U8->internal: %u samples\n", len);
1144
1145         for (i = len-1; i >= 0; i--)
1146                 dst[i] = (int32_t)(src[i] ^ 0x80) << 16;
1147
1148         return;
1149 }
1150
1151 static void
1152 sxe_msf_U8_down(void *d, void *s, size_t len)
1153 {
1154         /* convert samples from internal format (S24in32) to U8 */
1155         size_t i;
1156         uint8_t *dst = d;
1157         int32_t *src = s;
1158
1159         /* len is the number of samples (== #frame * #channels) */
1160         MEDIA_DEBUG_FMT("downsampling internal->U8: %u samples\n", len);
1161
1162         for (i = 0; i < len; i++)
1163                 dst[i] = (uint8_t)(src[i] >> 16) ^ 0x80;
1164
1165         return;
1166 }
1167
1168 DEFINE_MEDIA_SAMPLE_FORMAT_SIMPLE(sxe_msf_S16);
1169
1170 static void
1171 sxe_msf_S16_up(void *d, void *s, size_t len)
1172 {
1173         /* convert S16 samples to internal format (S24in32) */
1174         int i;
1175         int32_t *dst = d;
1176         int16_t *src = s;
1177
1178         /* len is the number of samples (== #frame * #channels) */
1179         MEDIA_DEBUG_FMT("upsampling S16->internal: %u samples\n", len);
1180
1181         for (i = len-1; i >= 0; i--)
1182                 dst[i] = (int32_t)(src[i]) << 8;
1183         MEDIA_DEBUG_FMT("d00:%d  d01:%d\n", dst[0], dst[1]);
1184
1185         return;
1186 }
1187
1188 static void
1189 sxe_msf_S16_down(void *d, void *s, size_t len)
1190 {
1191         /* convert samples from internal format (S24in32) to S16 */
1192         size_t i;
1193         int16_t *dst = d;
1194         int32_t *src = s;
1195
1196         /* len is the number of samples (== #frame * #channels) */
1197         MEDIA_DEBUG_FMT("downsampling internal->S16: %u samples\n", len);
1198
1199         for (i = 0; i < len; i++)
1200                 dst[i] = (int16_t)(src[i] >> 8);
1201
1202         return;
1203 }
1204
1205 DEFINE_MEDIA_SAMPLE_FORMAT_SIMPLE(sxe_msf_S24); /* format internally used */
1206
1207 static void
1208 sxe_msf_S24_up(void *d, void *s, size_t len)
1209 {
1210         MEDIA_DEBUG_FMT("upsampling S24->internal: %u samples\n", len);
1211
1212         /* S24 _is_ the internal format */
1213         return;
1214 }
1215
1216 static void
1217 sxe_msf_S24_down(void *d, void *s, size_t len)
1218 {
1219         MEDIA_DEBUG_FMT("downsampling internal->S24: %u samples\n", len);
1220
1221         /* S24 _is_ the internal format */
1222         return;
1223 }
1224
1225 DEFINE_MEDIA_SAMPLE_FORMAT_SIMPLE(sxe_msf_S32);
1226
1227 static void
1228 sxe_msf_S32_up(void *d, void *s, size_t len)
1229 {
1230         /* convert S32 samples to internal format (S24in32) */
1231         size_t i;
1232         int32_t *dst = d;
1233         int32_t *src = s;
1234
1235         /* len is the number of samples (== #frame * #channels) */
1236         MEDIA_DEBUG_FMT("upsampling S32->internal: %u samples\n", len);
1237
1238         for (i = 0; i < len; i++)
1239                 dst[i] = src[i] >> 8;
1240
1241         return;
1242 }
1243
1244 static void
1245 sxe_msf_S32_down(void *d, void *s, size_t len)
1246 {
1247         /* convert samples from internal format (S24in32) to S32 */
1248         size_t i;
1249         int32_t *dst = d;
1250         int32_t *src = s;
1251
1252         /* len is the number of samples (== #frame * #channels) */
1253         MEDIA_DEBUG_FMT("downsampling internal->S32: %u samples\n", len);
1254
1255         for (i = 0; i < len; i++)
1256                 dst[i] = src[i] << 8;
1257
1258         return;
1259 }
1260
1261 DEFINE_MEDIA_SAMPLE_FORMAT_SIMPLE(sxe_msf_FLT);
1262
1263 static void
1264 sxe_msf_FLT_up(void *d, void *s, size_t len)
1265 {
1266         /* convert float samples to internal format (S24in32) */
1267         size_t i;
1268         int32_t *dst = d;
1269         float *src = s;
1270
1271         /* len is the number of samples (== #frame * #channels) */
1272         MEDIA_DEBUG_FMT("upsampling FLT->internal: %u samples\n", len);
1273
1274         for (i = 0; i < len; i++) {
1275                 dst[i] = (int32_t)(src[i] * SXE_MAX_S24);
1276         }
1277         MEDIA_DEBUG_FMT("s00:%f d00:%d  s01:%f d01:%d\n",
1278                         src[0], dst[0], src[1], dst[1]);
1279
1280         return;
1281 }
1282
1283 static void
1284 sxe_msf_FLT_down(void *d, void *s, size_t len)
1285 {
1286         /* convert samples from internal format (S24in32) to float */
1287         int i;
1288         float *dst = d;
1289         int32_t *src = s;
1290
1291         /* len is the number of samples (== #frame * #channels) */
1292         MEDIA_DEBUG_FMT("downsampling internal->FLT: %u samples\n", len);
1293
1294         for (i = len-1; i >= 0; i--) {
1295                 dst[i] = (float)(src[i]) / SXE_MAX_S24;
1296         }
1297         MEDIA_DEBUG_FMT("d00:%f  d01:%f\n", dst[0], dst[1]);
1298
1299         return;
1300 }
1301
1302 DEFINE_MEDIA_SAMPLE_FORMAT_SIMPLE(sxe_msf_DBL);
1303
1304 static void
1305 sxe_msf_DBL_up(void *d, void *s, size_t len)
1306 {
1307         /* convert double samples to internal format (S24in32) */
1308         size_t i;
1309         int32_t *dst = d;
1310         double *src = s;
1311
1312         /* len is the number of samples (== #frame * #channels) */
1313         MEDIA_DEBUG_FMT("upsampling DBL->internal: %u samples\n", len);
1314
1315         for (i = 0; i < len; i++) {
1316                 dst[i] = (int32_t)(src[i] * SXE_MAX_S24);
1317         }
1318         MEDIA_DEBUG_FMT("s00:%f d00:%d  s01:%f d01:%d\n",
1319                         src[0], dst[0], src[1], dst[1]);
1320
1321         return;
1322 }
1323
1324 static void
1325 sxe_msf_DBL_down(void *d, void *s, size_t len)
1326 {
1327         /* convert samples from internal format (S24in32) to double */
1328         int i;
1329         float *dst = d;
1330         int32_t *src = s;
1331
1332         /* len is the number of samples (== #frame * #channels) */
1333         MEDIA_DEBUG_FMT("downsampling internal->DBL: %u samples\n", len);
1334
1335         for (i = len-1; i >= 0; i--) {
1336                 dst[i] = (double)(src[i]) / SXE_MAX_S24;
1337         }
1338         MEDIA_DEBUG_FMT("d00:%f  d01:%f\n", dst[0], dst[1]);
1339
1340         return;
1341 }
1342
1343 /* `effects' */
1344 DEFINE_MEDIA_SAMPLE_EFFECT(sxe_mse_1ch_to_2ch, _sxe_mse_1ch_to_2ch);
1345
1346 static size_t
1347 _sxe_mse_1ch_to_2ch(sxe_media_sample_t *dst, sxe_media_sample_t *src,
1348                     size_t len, void *ignored)
1349 {
1350         /* mono to stereo converter */
1351         int i;
1352
1353         MEDIA_DEBUG_COE("mono->stereo: %u samples\n", len);
1354
1355         /* len is the number of samples */
1356         for (i = len-1; i >= 0; i--) {
1357                 dst[2*i] = src[i];
1358                 dst[2*i+1] = src[i];
1359         }
1360
1361         return len * 2;
1362 }
1363
1364 DEFINE_MEDIA_SAMPLE_EFFECT(sxe_mse_2ch_to_1ch, _sxe_mse_2ch_to_1ch);
1365
1366 static size_t
1367 _sxe_mse_2ch_to_1ch(sxe_media_sample_t *dst, sxe_media_sample_t *src,
1368                     size_t len, void *args)
1369 {
1370         /* stereo to mono converter */
1371         size_t i;
1372         sxe_mse_2ch_to_1ch_args *_args = args;
1373         int c = 0;
1374
1375         MEDIA_DEBUG_COE("stereo->mono: %u samples\n", len);
1376
1377         if (_args) {
1378                 c = _args->chan;
1379         }
1380
1381         /* len is the number of samples */
1382         for (i = 0; i < len/2; i++) {
1383                 dst[i] = src[2*i+c];
1384         }
1385
1386         return len / 2;
1387 }
1388
1389 DEFINE_MEDIA_SAMPLE_EFFECT(sxe_mse_5ch_to_2ch, _sxe_mse_5ch_to_2ch);
1390
1391 static size_t
1392 _sxe_mse_5ch_to_2ch(sxe_media_sample_t *dst, sxe_media_sample_t *src,
1393                     size_t len, void *args)
1394 {
1395         /* 5 channel to stereo converter */
1396         size_t i;
1397         sxe_mse_5ch_to_2ch_args *_args = args;
1398         int c1 = 0, c2 = 1;
1399
1400         MEDIA_DEBUG_COE("5ch->stereo: %u samples\n", len);
1401
1402         if (_args) {
1403                 c1 = _args->chan1;
1404                 c2 = _args->chan1;
1405         }
1406
1407         /* len is the number of samples */
1408         for (i = 0; i < len/5; i++) {
1409                 dst[2*i] = src[5*i+c1];
1410                 dst[2*i+1] = src[5*i+c2];
1411         }
1412
1413         return len * 2 / 5;
1414 }
1415
1416 DEFINE_MEDIA_SAMPLE_EFFECT(sxe_mse_volume, _sxe_mse_volume);
1417
1418 static size_t
1419 _sxe_mse_volume(sxe_media_sample_t *dst, sxe_media_sample_t *src,
1420                 size_t len, void *args)
1421 {
1422         /* stereo to mono converter */
1423         size_t i, j;
1424         sxe_mse_volume_args *_args = args;
1425
1426         MEDIA_DEBUG_COE("volume: %u samples\n", len);
1427
1428         /* len is the number of samples */
1429         for (i = 0; i < len; i+=_args->num_channels) {
1430                 for (j = 0; j < (size_t)_args->num_channels; j++) {
1431                         uint8_t vol = _args->volume[j];
1432                         dst[i+j] = (src[i+j] * vol) >> 7;
1433                 }
1434         }
1435
1436         return len;
1437 }
1438
1439 DEFINE_MEDIA_SAMPLE_EFFECT(sxe_mse_rerate, _sxe_mse_rerate);
1440
1441 static size_t
1442 _sxe_mse_rerate(sxe_media_sample_t *dst, sxe_media_sample_t *src,
1443                 size_t len, void *args)
1444 {
1445         /* rate converter */
1446         int i;
1447         sxe_mse_rerate_args *_args = args;
1448         float trafo = (float)_args->srcrate / (float)_args->tgtrate
1449                 * _args->tweak;
1450         int chans = _args->num_channels;
1451         int bound = len/chans/trafo;
1452
1453         MEDIA_DEBUG_COE("rerate: %u samples, final trafo: %f, bound is: %d\n",
1454                         len, trafo, bound);
1455
1456         if (trafo < 1.0) {
1457                 for (i = bound-1; i >= 0; i--) {
1458                         int frame = i * trafo;
1459                         dst[chans*i] = (src[chans*frame]);
1460                         dst[chans*i+1] = (src[chans*frame+1]);
1461                 }
1462         } else if (trafo > 1.0) {
1463                 for (i = 0; i < bound-1; i++) {
1464                         int frame = i * trafo;
1465                         dst[chans*i] = (src[chans*frame]);
1466                         dst[chans*i+1] = (src[chans*frame+1]);
1467                 }
1468         }
1469
1470         return bound * chans;
1471 }
1472
1473 \f
1474 void syms_of_media(void)
1475 {
1476         INIT_LRECORD_IMPLEMENTATION(media_stream);
1477
1478         defsymbol(&Qmedia_streamp, "media-stream-p");
1479
1480         DEFSYMBOL(Qtype);
1481         DEFSYMBOL(Qdemux);
1482         DEFSYMBOL(Qcodec);
1483         DEFSYMBOL(Qnchannels);
1484         DEFSYMBOL(Qsamplerate);
1485         DEFSYMBOL(Qbitrate);
1486         DEFSYMBOL(Qabitrate);
1487         DEFSYMBOL(Qvbitrate);
1488         DEFSYMBOL(Qwidth);
1489         DEFSYMBOL(Qheight);
1490         DEFSYMBOL(Qaspect);
1491         DEFSYMBOL(Qaudio);
1492         DEFSYMBOL(Qvideo);
1493         DEFSYMBOL(Qimage);
1494         DEFSYMBOL(Qdriver);
1495         DEFSYMBOL(Qkind);
1496         DEFSYMBOL(Qfifo);
1497         DEFSYMBOL(Quri);
1498
1499         DEFSUBR(Fmake_media_stream);
1500         DEFSUBR(Fmedia_stream_p);
1501 #if 0
1502         DEFSUBR(Faudio_substream_p);
1503         DEFSUBR(Fvideo_substream_p);
1504 #endif
1505
1506         DEFSUBR(Fmedia_available_formats);
1507         DEFSUBR(Fmedia_properties);
1508         DEFSUBR(Fmedia_stream_aspect);
1509
1510 #ifdef HAVE_FFMPEG
1511         defsymbol(&Qffmpeg, "ffmpeg");
1512 #endif
1513 #ifdef HAVE_MAD
1514         defsymbol(&Qmad, "mad");
1515 #endif
1516 #ifdef HAVE_SOX
1517         defsymbol(&Qsox, "sox");
1518 #endif
1519 #ifdef HAVE_SNDFILE
1520         defsymbol(&Qsndfile, "sndfile");
1521 #endif
1522         defsymbol(&Qunknown, "unknown");
1523         defsymbol(&Qunavailable, "unavailable");
1524 }
1525
1526 void vars_of_media(void)
1527 {
1528         Fprovide(intern("media"));
1529
1530 #ifdef HAVE_FFMPEG
1531         Fprovide(intern("media-ffmpeg"));
1532 #endif
1533 #ifdef HAVE_SNDFILE
1534         Fprovide(intern("media-sndfile"));
1535 #endif
1536 #ifdef HAVE_MAD
1537         Fprovide(intern("media-mad"));
1538 #endif
1539 #ifdef HAVE_SOX
1540         Fprovide(intern("media-sox"));
1541 #endif
1542 #ifdef HAVE_XINE
1543         Fprovide(intern("media-xine"));
1544 #endif
1545 #ifdef HAVE_GSTREAMER
1546         Fprovide(intern("media-gstreamer"));
1547 #endif
1548 #ifdef HAVE_INTERNAL_MEDIA
1549         Fprovide(intern("media-internal"));
1550 #endif
1551 }
1552
1553 /* media.c ends here */