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