2 Copyright (C) 2006 Sebastian Freundt
4 This file is part of SXEmacs
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.
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.
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/>. */
20 /* Synched up with: Not in FSF. */
30 #include "media-ffmpeg.h"
33 #include "media-sndfile.h"
35 #ifdef HAVE_INTERNAL_MEDIA
36 #include "media-internal.h"
39 #include "media-mad.h"
42 #include "media-gstreamer.h"
45 #include "media-sox.h"
48 #include "media-xine.h"
51 Lisp_Object Qmedia_streamp;
53 /* media property syms */
54 Lisp_Object Qdemux, Qcodec, Qnchannels, Qsamplerate;
55 Lisp_Object Qbitrate, Qabitrate, Qvbitrate;
56 Lisp_Object Qwidth, Qheight, Qaspect, Qdriver, Qkind, Qfifo, Quri, Qtype;
57 Lisp_Object Qaudio, Qvideo, Qimage;
59 static void determine_stream_type(Lisp_Media_Stream *ms, media_driver);
60 static void media_stream_print(Lisp_Object, Lisp_Object, int);
61 static void media_substream_print(media_substream *, Lisp_Object, int);
62 static void media_substream_finalise(void*, int);
65 static void determine_stream_type(Lisp_Media_Stream *ms, media_driver preferred)
68 if ((media_stream_driver(ms) == MDRIVER_UNKNOWN) &&
69 (preferred == MDRIVER_UNKNOWN ||
70 preferred == MDRIVER_FFMPEG)) {
71 MEDIA_DEBUG("trying ffmpeg.\n");
72 media_stream_set_meths(ms, media_ffmpeg);
73 media_stream_meth(ms, open)(ms);
77 if ((media_stream_driver(ms) == MDRIVER_UNKNOWN) &&
78 (preferred == MDRIVER_UNKNOWN ||
79 preferred == MDRIVER_MAD)) {
80 MEDIA_DEBUG("trying mad.\n");
81 media_stream_set_meths(ms, media_mad);
82 media_stream_meth(ms, open)(ms);
86 if ((media_stream_driver(ms) == MDRIVER_UNKNOWN) &&
87 (preferred == MDRIVER_UNKNOWN ||
88 preferred == MDRIVER_SOX)) {
89 MEDIA_DEBUG("trying sox.\n");
90 media_stream_set_meths(ms, media_sox);
91 media_stream_meth(ms, open)(ms);
95 if ((media_stream_driver(ms) == MDRIVER_UNKNOWN) &&
96 (preferred == MDRIVER_UNKNOWN ||
97 preferred == MDRIVER_SNDFILE)) {
98 MEDIA_DEBUG("trying sndfile.\n");
99 media_stream_set_meths(ms, media_sndfile);
100 media_stream_meth(ms, open)(ms);
103 /* not working stuff here :) */
105 #ifdef HAVE_GSTREAMER
106 if ((media_stream_driver(ms) == MDRIVER_UNKNOWN) &&
107 (preferred == MDRIVER_UNKNOWN ||
108 preferred == MDRIVER_GSTREAMER)) {
109 MEDIA_DEBUG("trying gstreamer.\n");
110 media_gstreamer_analyse_stream(ms);
115 if ((media_stream_driver(ms) == MDRIVER_UNKNOWN) &&
116 (preferred == MDRIVER_UNKNOWN ||
117 preferred == MDRIVER_XINE)) {
118 MEDIA_DEBUG("trying xine.\n");
119 media_stream_set_meths(ms, media_xine);
120 media_stream_meth(ms, open)(ms);
123 #ifdef HAVE_INTERNAL_MEDIA
124 if ((media_stream_driver(ms) == MDRIVER_UNKNOWN) &&
125 (preferred == MDRIVER_UNKNOWN ||
126 preferred == MDRIVER_INTERNAL)) {
127 MEDIA_DEBUG("trying internal.\n");
128 media_internal_analyse_stream(ms);
131 if (media_stream_driver(ms) == MDRIVER_UNKNOWN) {
132 MEDIA_DEBUG("giving up\n");
133 media_stream_set_meths(ms, NULL);
139 /*****************************************************************/
141 /*****************************************************************/
142 static Lisp_Object media_stream_mark(Lisp_Object obj)
144 switch (XMEDIA_STREAM_KIND(obj)) {
146 mark_object(XMEDIA_STREAM(obj)->
147 kind_properties.fprops->filename);
153 case NUMBER_OF_MEDIA_KINDS:
158 if (XMEDIA_STREAM_METHS(obj) &&
159 XMEDIA_STREAM_METH(obj, mark))
160 XMEDIA_STREAM_METH(obj, mark)(XMEDIA_STREAM_DATA(obj));
166 media_stream_finalise(void *header, int for_disksave)
168 Lisp_Media_Stream *ms = (Lisp_Media_Stream*)header;
169 media_substream *mss = NULL;
171 if (media_stream_meths(ms) &&
172 media_stream_meth(ms, close))
173 media_stream_meth(ms, close)(media_stream_data(ms));
175 mss = media_stream_first(ms);
177 media_substream_finalise(mss, for_disksave);
178 mss = media_substream_next(mss);
182 switch (media_stream_driver(ms)) {
185 if (media_stream_data(ms))
186 media_xine_close_context(media_stream_data(ms));
189 case MDRIVER_SNDFILE:
191 if (media_stream_data(ms))
192 sf_close(media_stream_data(ms));
197 if (media_stream_data(ms))
198 mad_decoder_finish(media_stream_data(ms));
203 if (media_stream_data(ms))
204 st_close(media_stream_data(ms));
212 switch (media_stream_kind(ms)) {
214 mkind_file_properties *mkfp;
215 mkfp = media_stream_kind_properties(ms).fprops;
222 mkind_string_properties *mksp;
223 mksp = media_stream_kind_properties(ms).sprops;
228 if (mksp->stream_data)
229 xfree(mksp->stream_data);
238 case NUMBER_OF_MEDIA_KINDS:
243 /* avoid some warning */
244 if (for_disksave || ms == NULL);
248 media_stream_print(Lisp_Object obj, Lisp_Object printcharfun, int ef)
250 Lisp_Media_Stream *ms = XMEDIA_STREAM(obj);
251 media_substream *mss;
253 write_c_string("#<media-stream", printcharfun);
255 write_c_string(" :kind ", printcharfun);
257 switch (media_stream_kind(ms)) {
260 media_stream_kind_properties(ms).fprops->filename;
262 write_c_string("#<file ", printcharfun);
263 print_internal(file, printcharfun, ef);
264 write_c_string(">", printcharfun);
268 write_c_string("#<string>", printcharfun);
271 write_c_string("#<fifo>", printcharfun);
274 write_c_string("#<stream>", printcharfun);
279 case NUMBER_OF_MEDIA_KINDS:
280 write_c_string("#<unknown>", printcharfun);
284 mss = media_stream_first(ms);
286 write_c_string(" ", printcharfun);
287 media_substream_print(mss, printcharfun, ef);
288 mss = media_substream_next(mss);
291 write_c_string(" driven by ", printcharfun);
292 switch (media_stream_driver(ms)) {
293 case MDRIVER_INTERNAL:
294 write_c_string("internal", printcharfun);
297 write_c_string("ffmpeg", printcharfun);
299 case MDRIVER_SNDFILE:
300 write_c_string("sndfile", printcharfun);
303 write_c_string("mad", printcharfun);
306 write_c_string("sox", printcharfun);
309 write_c_string("xine", printcharfun);
311 case MDRIVER_GSTREAMER:
312 write_c_string("gstreamer", printcharfun);
314 case MDRIVER_UNKNOWN:
315 case NUMBER_OF_MEDIA_DRIVERS:
317 XMEDIA_STREAM_SET_METHS(obj, NULL);
318 write_c_string("unknown", printcharfun);
322 if (XMEDIA_STREAM_METHS(obj) &&
323 XMEDIA_STREAM_METH(obj, print)) {
324 XMEDIA_STREAM_METH(obj, print)(obj, printcharfun, ef);
327 write_c_string(">", printcharfun);
331 media_stream_equal(Lisp_Object obj1, Lisp_Object obj2, int depth)
333 if (XMEDIA_STREAM_DATA(obj1) == XMEDIA_STREAM_DATA(obj2))
343 media_stream_hash (Lisp_Object obj, int depth)
345 return (unsigned long)obj;
351 static const struct lrecord_description media_stream_description[] = {
352 { XD_LISP_OBJECT, offsetof(Lisp_Media_Stream, first) },
353 { XD_LISP_OBJECT, offsetof(Lisp_Media_Stream, last) },
354 { XD_INT, offsetof(Lisp_Media_Stream, kind) },
355 { XD_INT, offsetof(Lisp_Media_Stream, driver) },
356 { XD_OPAQUE_PTR, offsetof(Lisp_Media_Stream, kind_properties) },
357 { XD_OPAQUE_PTR, offsetof(Lisp_Media_Stream, stream_data) },
361 DEFINE_LRECORD_IMPLEMENTATION("media_stream", media_stream,
362 media_stream_mark, media_stream_print,
363 media_stream_finalise,
364 media_stream_equal, media_stream_hash,
365 media_stream_description,
369 /*****************************************************************/
370 /* media substreams */
371 /*****************************************************************/
374 media_substream_finalise(void *header, int for_disksave)
376 media_substream *mss = (media_substream*)header;
378 switch (media_substream_type(mss)) {
380 if (media_substream_type_properties(mss).aprops)
381 xfree(media_substream_type_properties(mss).aprops);
384 if (media_substream_type_properties(mss).vprops)
385 xfree(media_substream_type_properties(mss).vprops);
389 case NUMBER_OF_MEDIA_TYPES:
395 pthread_mutex_destroy(&mss->substream_mutex);
398 media_substream_data(mss) = NULL;
400 /* avoid some warning */
405 media_substream_print_audio(media_substream *mss, Lisp_Object printcharfun)
407 mtype_audio_properties *mtap =
408 media_substream_type_properties(mss).aprops;
410 write_c_string("#<audio ", printcharfun);
411 if (mtap->name || mtap->codec_name) {
412 char *buf = alloca(64);
413 if (mtap->name && mtap->codec_name)
414 snprintf(buf, 63, "%s (%s)",
415 mtap->name, mtap->codec_name);
417 snprintf(buf, 63, "%s [???]", mtap->name);
418 else if (mtap->codec_name)
419 snprintf(buf, 63, "??? (%s)", mtap->codec_name);
421 write_c_string(buf, printcharfun);
423 write_c_string("???", printcharfun);
425 switch (mtap->channels) {
427 write_c_string(", mono", printcharfun);
430 write_c_string(", stereo", printcharfun);
433 write_c_string(", chn:5", printcharfun);
436 write_c_string(", 5.1", printcharfun);
439 write_c_string(", chn:???", printcharfun);
443 if (mtap->samplerate) {
446 snprintf(buf, 47, ", %d Hz, %d Bit",
449 write_c_string(buf, printcharfun);
455 snprintf(buf, 23, ", %d kb/s", mtap->bitrate/1000);
456 write_c_string(buf, printcharfun);
459 write_c_string(">", printcharfun);
463 media_substream_print_video(media_substream *mss, Lisp_Object printcharfun)
465 mtype_video_properties *mtvp =
466 media_substream_type_properties(mss).vprops;
468 write_c_string("#<video ", printcharfun);
469 if (mtvp->name || mtvp->codec_name) {
470 char *buf = alloca(64);
471 if (mtvp->name && mtvp->codec_name)
472 snprintf(buf, 63, "%s (%s)",
473 mtvp->name, mtvp->codec_name);
475 snprintf(buf, 63, "%s [???]", mtvp->name);
476 else if (mtvp->codec_name)
477 snprintf(buf, 63, "??? (%s)", mtvp->codec_name);
479 write_c_string(buf, printcharfun);
481 write_c_string("???", printcharfun);
484 char *buf = alloca(24);
485 snprintf(buf, 23, ", %d kb/s", mtvp->bitrate);
486 write_c_string(buf, printcharfun);
489 if (mtvp->width && mtvp->height) {
490 char *buf = alloca(48);
491 if (mtvp->aspect_num > 1 && mtvp->aspect_den >= 1)
492 snprintf(buf, 47, ", %dx%d (%d/%d)",
493 mtvp->width, mtvp->height,
494 mtvp->aspect_num, mtvp->aspect_den);
496 snprintf(buf, 47, ", %dx%d (%.2f/1)",
497 mtvp->width, mtvp->height,
498 (double)mtvp->width/(double)mtvp->height);
499 write_c_string(buf, printcharfun);
501 write_c_string(">", printcharfun);
505 media_substream_print(media_substream *mss,
506 Lisp_Object printcharfun, int escapeflag)
508 write_c_string("#<media-substream :type ", printcharfun);
510 switch (media_substream_type(mss)) {
512 media_substream_print_audio(mss, printcharfun);
516 media_substream_print_video(mss, printcharfun);
519 write_c_string("#<image>", printcharfun);
523 case NUMBER_OF_MEDIA_TYPES:
524 write_c_string("#<unknown>", printcharfun);
528 write_c_string(">", printcharfun);
531 static Lisp_Media_Stream *
532 media_stream_allocate(void)
534 Lisp_Media_Stream *ms;
536 ms = alloc_lcrecord_type(Lisp_Media_Stream, &lrecord_media_stream);
541 Lisp_Object make_media_stream()
543 Lisp_Media_Stream *ms;
546 ms = media_stream_allocate();
547 media_stream_kind(ms) = MKIND_UNKNOWN;
548 media_stream_driver(ms) = MDRIVER_UNKNOWN;
549 media_stream_data(ms) = NULL;
551 /* now set the navigation */
552 media_stream_first(ms) = NULL;
553 media_stream_last(ms) = NULL;
555 XSETMEDIA_STREAM(lms, ms);
560 media_substream *make_media_substream(void)
562 /* this allocates and conses to the back of ms */
563 media_substream *mss;
565 mss = xnew_and_zero(media_substream);
566 media_substream_type(mss) = MTYPE_UNKNOWN;
567 media_substream_data(mss) = NULL;
570 media_substream_next(mss) = NULL;
571 media_substream_prev(mss) = NULL;
574 pthread_mutex_init(&mss->substream_mutex, NULL);
580 media_substream *make_media_substream_append(Lisp_Media_Stream *ms)
582 media_substream *mss;
584 mss = make_media_substream();
587 media_substream_next(mss) = NULL;
588 if (!(media_stream_last(ms))) {
589 media_substream_prev(mss) = NULL;
590 media_stream_first(ms) = mss;
592 media_substream_prev(mss) = media_stream_last(ms);
593 media_substream_next(media_stream_last(ms)) = mss;
596 media_stream_last(ms) = mss;
597 media_substream_up(mss) = ms;
602 media_substream *make_media_substream_prepend(Lisp_Media_Stream *ms)
604 media_substream *mss;
606 mss = make_media_substream();
609 media_substream_prev(mss) = NULL;
610 if (!(media_stream_first(ms))) {
611 media_substream_next(mss) = NULL;
612 media_stream_last(ms) = mss;
614 media_substream_next(mss) = media_stream_first(ms);
615 media_substream_prev(media_stream_first(ms)) = mss;
618 media_stream_first(ms) = mss;
619 media_substream_up(mss) = ms;
624 DEFUN("make-media-stream", Fmake_media_stream, 2, 3, 0, /*
625 Create a new media stream from DATA.
627 FROM is a keyword and defines how DATA is interpreted:
628 :file - DATA is the name of a file
629 :data - DATA is a string with the stream data
630 :url - DATA is a url (string) for streamed media contents
632 Optional argument DRIVER (a symbol) may be used to force
633 the use of a certain driver instead of automatically
634 detecting a suitable one. It is one of `ffmpeg', `sndfile',
635 `sox', `mad', `xine', `gstreamer', or `internal'.
637 (from, data, driver))
640 Lisp_Media_Stream *ms;
645 DATA_IS_DATA } datatype = DATA_IS_BULLSHIT;
646 media_driver pref = MDRIVER_UNKNOWN;
649 else if (EQ(from, Q_file))
650 datatype = DATA_IS_FILE;
651 else if (EQ(from, Q_data))
652 datatype = DATA_IS_DATA;
653 else if (EQ(from, Q_url))
654 datatype = DATA_IS_URL;
656 datatype = DATA_IS_BULLSHIT;
657 return Qnil; /* in this case, why bother? stupid user :) */
661 pref = MDRIVER_UNKNOWN;
663 else if (EQ(driver, Qmad))
667 else if (EQ(driver, Qffmpeg))
668 pref = MDRIVER_FFMPEG;
671 else if (EQ(driver, Qsox))
674 else if (EQ(driver, intern("xine")))
677 else if (EQ(driver, Qsndfile))
678 pref = MDRIVER_SNDFILE;
680 else if (EQ(driver, intern("internal")))
681 pref = MDRIVER_INTERNAL;
682 else if (EQ(driver, intern("gstreamer")))
683 pref = MDRIVER_GSTREAMER;
685 pref = MDRIVER_UNKNOWN;
687 /* hm, maybe data could be a symbol from the sound-alist?
688 * or a buffer or a network socket?
692 lms = make_media_stream();
693 ms = XMEDIA_STREAM(lms);
697 mkind_file_properties *fprops;
699 /* expand-file-name first and check for existence*/
700 data = Fexpand_file_name(data, Qnil);
701 if (!NILP(Ffile_directory_p(data)) ||
702 NILP(Ffile_readable_p(data)))
705 media_stream_kind(ms) = MKIND_FILE;
707 /* initialise a new file properties structure */
708 fprops = xnew_and_zero(mkind_file_properties);
710 /* copy the filename also as C string */
711 fprops->filename = data;
713 /* assign the file properties */
714 media_stream_kind_properties(ms).fprops = fprops;
716 determine_stream_type(ms, pref);
720 mkind_string_properties *sprops;
724 media_stream_kind(ms) = MKIND_STRING;
726 /* initialise a new file properties structure */
727 sprops = xnew_and_zero(mkind_string_properties);
729 /* copy the filename also as C string */
730 TO_EXTERNAL_FORMAT(LISP_STRING, data,
731 MALLOC, (data_ext, data_len),
733 data_ext[data_len] = '\0';
735 sprops->stream_data = data_ext;
736 sprops->size = data_len;
738 /* assign the file properties */
739 media_stream_kind_properties(ms).sprops = sprops;
741 determine_stream_type(ms, pref);
747 case DATA_IS_BULLSHIT:
755 static Lisp_Object recons(Lisp_Object to, Lisp_Object from)
761 while (!NILP(from)) {
762 result = Fcons(XCAR(from), result);
768 DEFUN("media-stream-p", Fmedia_stream_p, 1, 1, 0, /*
769 Return non-nil if object is a media-stream.
773 if (MEDIA_STREAMP(object))
780 DEFUN("audio-substream-p", Faudio_substream_p, 1, 1, 0, /*
781 Return non-nil if object is a media-substream with audio data.
785 if (MEDIA_SUBSTREAMP(object) &&
786 XMEDIA_SUBSTREAM_TYPE(object) == MTYPE_AUDIO)
792 DEFUN("video-substream-p", Fvideo_substream_p, 1, 1, 0, /*
793 Return non-nil if object is a media-substream with video data.
797 if (MEDIA_SUBSTREAMP(object) &&
798 XMEDIA_SUBSTREAM_TYPE(object) == MTYPE_VIDEO)
805 DEFUN("media-available-formats", Fmedia_available_formats, 0, 0, 0, /*
806 Return a list of input formats in the underlying media libraries.
817 temp = media_ffmpeg_available_formats();
819 formats = recons(formats, temp);
824 formats = recons(formats, temp);
829 static inline void __attribute__((always_inline))
830 __add_prop(Lisp_Object *reslist, Lisp_Object key, Lisp_Object val)
832 *reslist = Fcons(Fcons(key, val), *reslist);
837 media_substream_props(media_substream *mss, Lisp_Object *reslist)
839 switch (media_substream_type(mss)) {
841 mtype_audio_properties *mtap =
842 media_substream_type_properties(mss).aprops;
844 /* add the type property */
845 __add_prop(reslist, Qtype, Qaudio);
848 __add_prop(reslist, Qdemux, build_string(mtap->name));
850 if (mtap->codec_name) {
851 __add_prop(reslist, Qcodec,
852 build_string(mtap->codec_name));
855 if (mtap->channels) {
856 __add_prop(reslist, Qnchannels,
857 make_int(mtap->channels));
860 if (mtap->samplerate) {
861 __add_prop(reslist, Qsamplerate,
862 make_int(mtap->samplerate));
866 __add_prop(reslist, Qabitrate, make_int(mtap->bitrate));
873 mtype_video_properties *mtvp =
874 media_substream_type_properties(mss).vprops;
876 /* add the type property */
877 __add_prop(reslist, Qtype, Qvideo);
880 __add_prop(reslist, Qdemux, build_string(mtvp->name));
882 if (mtvp->codec_name) {
883 __add_prop(reslist, Qcodec,
884 build_string(mtvp->codec_name));
888 __add_prop(reslist, Qvbitrate, make_int(mtvp->bitrate));
892 __add_prop(reslist, Qwidth, make_int(mtvp->width));
896 __add_prop(reslist, Qheight, make_int(mtvp->height));
899 #if defined HAVE_MPQ && defined WITH_GMP && 0
900 if (mtvp->aspect_num > 1 && mtvp->aspect_den >= 1) {
901 __add_prop(reslist, Qaspect,
902 make_bigq(mtvp->aspect_num,
905 #elif defined HAVE_FPFLOAT
906 /* use float arithmetic */
907 if (mtvp->aspect_num > 1 && mtvp->aspect_den >= 1) {
908 __add_prop(reslist, Qaspect,
909 make_float((fpfloat)mtvp->aspect_num /
910 (fpfloat)mtvp->aspect_den));
911 } else if (mtvp->width && mtvp->height) {
912 __add_prop(reslist, Qaspect,
913 make_float((fpfloat)mtvp->width /
914 (fpfloat)mtvp->height));
921 __add_prop(reslist, Qtype, Qimage);
926 case NUMBER_OF_MEDIA_TYPES:
931 DEFUN("media-properties", Fmedia_properties, 1, 1, 0, /*
932 Return an alist of available properties of media-stream STREAM.
934 Depending on the underlying stream this alist may be made of
935 several of the following keys, grouped by media contents.
939 'file (string) the stream's filename
940 'uri (string) the stream's URI
941 'fifo (string) the stream's FIFO socket
942 'driver (symbol) the stream's demuxer/decoder driver
943 'kind (symbol) the stream's kind (file, fifo, string, uri, etc.)
944 'type (symbol) the stream's type (audio, video, image, subtitle, lyrics, etc.)
948 'codec (string) the suitable audio codec
949 'demux (string) the suitable demuxer
950 'title (string) the title of an audio track
951 'artist (string) the performing artist(s)
952 'album (string) the title of the album
953 'comment (string) an arbitrary comment
954 'genre (string) the genre identifier string
955 'year (integer) the year of release
956 'track (integer) the track number on the album
957 'length (integer) the length of the track in seconds
958 'abitrate (integer) the average bitrate of the track in kb/s
959 'samplerate (integer) the samplerate of the track in Hz
960 'nchannels (integer) the number of distinguished channels
964 'codec (string) the suitable audio codec
965 'demux (string) the suitable demuxer
966 'title (string) the title of a video track
967 'comment (string) an arbitrary comment
968 'year (integer) the year of release
969 'vbitrate (integer) the average bitrate of the track in kb/s
970 'width (integer) the x-resolution in pixels
971 'height (integer) the y-resolution in pixels
972 'aspect (bigq) the aspect quotient
974 Keys which do not apply to the underlying stream or are not
975 defined in the tag section of the stream are simply left out
980 Lisp_Media_Stream *ms = XMEDIA_STREAM(stream);
981 media_substream *mss;
982 Lisp_Object resdl = Qnil;
984 switch (media_stream_kind(ms)) {
987 media_stream_kind_properties(ms).fprops->filename;
989 __add_prop(&resdl, Qfile, file);
990 __add_prop(&resdl, Qkind, Qfile);
994 __add_prop(&resdl, Qkind, Qfifo);
998 __add_prop(&resdl, Qkind, Quri);
1002 __add_prop(&resdl, Qkind, Qstring);
1005 case NUMBER_OF_MEDIA_KINDS:
1007 __add_prop(&resdl, Qkind, Qunknown);
1011 switch (media_stream_driver(ms)) {
1012 case MDRIVER_INTERNAL:
1013 __add_prop(&resdl, Qdriver, Qinternal);
1015 case MDRIVER_FFMPEG:
1017 __add_prop(&resdl, Qdriver, Qffmpeg);
1019 streaminfo = media_ffmpeg_streaminfo(ms);
1023 case MDRIVER_SNDFILE:
1025 __add_prop(&resdl, Qdriver, Qsndfile);
1030 __add_prop(&resdl, Qdriver, Qmad);
1035 __add_prop(&resdl, Qdriver, Qsox);
1039 case MDRIVER_UNKNOWN:
1040 case NUMBER_OF_MEDIA_DRIVERS:
1042 __add_prop(&resdl, Qdriver, Qunknown);
1052 mss = media_stream_first(ms);
1054 media_substream_props(mss, &resdl);
1055 mss = media_substream_next(mss);
1061 /* convenience functions */
1062 DEFUN("media-stream-aspect", Fmedia_stream_aspect, 1, 3, 0, /*
1063 Return a list of aspect ratios in media stream STREAM.
1065 Optional argument SUBSTREAM names the index of a specific (video)
1066 substream, i.e. only video streams are counted, i.e. the substream 0
1067 names the first video track inside STREAM.
1068 The return value in that case is just a float or quotient, depending
1069 on the optional argument QUOTIENTP.
1071 Optional argument QUOTIENTP indicates that aspect ratios are to be
1072 returned as bigq quotients instead of floats (the default).
1074 By default, the aspect ratios of all the video tracks appear in the list,
1075 the first element in the list belongs to the first video track,
1076 the second element to the second one and so on.
1078 (stream, substream, quotientp))
1080 Lisp_Media_Stream *ms = XMEDIA_STREAM(stream);
1081 Lisp_Object res = Qnil;
1082 long int idx = INTP(substream) ? XINT(substream) : -1;
1084 for (media_substream *mss = media_stream_first(ms); mss;
1085 (mss = media_substream_next(mss))) {
1086 mtype_video_properties *mtvp;
1087 long int num = 0, den = 0;
1090 if (LIKELY(media_substream_type(mss) != MTYPE_VIDEO)) {
1094 /* oh, it's a video stream, check if it's the one we wanted */
1095 if (UNLIKELY(idx-- > 0)) {
1099 mtvp = media_substream_type_properties(mss).vprops;
1100 if (mtvp->aspect_num > 1 && mtvp->aspect_den >= 1) {
1101 num = mtvp->aspect_num;
1102 den = mtvp->aspect_den;
1103 } else if (mtvp->width && mtvp->height) {
1104 /* good enough? just an approximation as
1105 * the aspect ratio may very well differ
1106 * from the width-by-height ratio */
1111 if (LIKELY(NILP(quotientp))) {
1112 tmp = make_float((fpfloat)num / (fpfloat)den);
1114 #if defined HAVE_MPQ && defined WITH_GMP
1116 tmp = make_bigq(num, den);
1120 dead_wrong_type_argument(Qfeaturep,quotientp);
1123 if (UNLIKELY(INTP(substream))) {
1126 /* the very very default case */
1127 res = Fcons(tmp, res);
1133 /* Audio Coercion */
1134 /* SXEmacs works internally with samples in 24bit resolution */
1136 DEFINE_MEDIA_SAMPLE_FORMAT_SIMPLE(sxe_msf_U8);
1139 sxe_msf_U8_up(void *d, void *s, size_t len)
1141 /* convert U8 samples to internal format (S24in32) */
1146 /* len is the number of samples (== #frame * #channels) */
1147 MEDIA_DEBUG_FMT("upsampling U8->internal: %u samples\n", len);
1149 for (i = len-1; i >= 0; i--)
1150 dst[i] = (int32_t)(src[i] ^ 0x80) << 16;
1156 sxe_msf_U8_down(void *d, void *s, size_t len)
1158 /* convert samples from internal format (S24in32) to U8 */
1163 /* len is the number of samples (== #frame * #channels) */
1164 MEDIA_DEBUG_FMT("downsampling internal->U8: %u samples\n", len);
1166 for (i = 0; i < len; i++)
1167 dst[i] = (uint8_t)(src[i] >> 16) ^ 0x80;
1172 DEFINE_MEDIA_SAMPLE_FORMAT_SIMPLE(sxe_msf_S16);
1175 sxe_msf_S16_up(void *d, void *s, size_t len)
1177 /* convert S16 samples to internal format (S24in32) */
1182 /* len is the number of samples (== #frame * #channels) */
1183 MEDIA_DEBUG_FMT("upsampling S16->internal: %u samples\n", len);
1185 for (i = len-1; i >= 0; i--)
1186 dst[i] = (int32_t)(src[i]) << 8;
1187 MEDIA_DEBUG_FMT("d00:%d d01:%d\n", dst[0], dst[1]);
1193 sxe_msf_S16_down(void *d, void *s, size_t len)
1195 /* convert samples from internal format (S24in32) to S16 */
1200 /* len is the number of samples (== #frame * #channels) */
1201 MEDIA_DEBUG_FMT("downsampling internal->S16: %u samples\n", len);
1203 for (i = 0; i < len; i++)
1204 dst[i] = (int16_t)(src[i] >> 8);
1209 DEFINE_MEDIA_SAMPLE_FORMAT_SIMPLE(sxe_msf_S24); /* format internally used */
1212 sxe_msf_S24_up(void *d, void *s, size_t len)
1214 MEDIA_DEBUG_FMT("upsampling S24->internal: %u samples\n", len);
1216 /* S24 _is_ the internal format */
1221 sxe_msf_S24_down(void *d, void *s, size_t len)
1223 MEDIA_DEBUG_FMT("downsampling internal->S24: %u samples\n", len);
1225 /* S24 _is_ the internal format */
1229 DEFINE_MEDIA_SAMPLE_FORMAT_SIMPLE(sxe_msf_S32);
1232 sxe_msf_S32_up(void *d, void *s, size_t len)
1234 /* convert S32 samples to internal format (S24in32) */
1239 /* len is the number of samples (== #frame * #channels) */
1240 MEDIA_DEBUG_FMT("upsampling S32->internal: %u samples\n", len);
1242 for (i = 0; i < len; i++)
1243 dst[i] = src[i] >> 8;
1249 sxe_msf_S32_down(void *d, void *s, size_t len)
1251 /* convert samples from internal format (S24in32) to S32 */
1256 /* len is the number of samples (== #frame * #channels) */
1257 MEDIA_DEBUG_FMT("downsampling internal->S32: %u samples\n", len);
1259 for (i = 0; i < len; i++)
1260 dst[i] = src[i] << 8;
1265 DEFINE_MEDIA_SAMPLE_FORMAT_SIMPLE(sxe_msf_FLT);
1268 sxe_msf_FLT_up(void *d, void *s, size_t len)
1270 /* convert float samples to internal format (S24in32) */
1275 /* len is the number of samples (== #frame * #channels) */
1276 MEDIA_DEBUG_FMT("upsampling FLT->internal: %u samples\n", len);
1278 for (i = 0; i < len; i++) {
1279 dst[i] = (int32_t)(src[i] * SXE_MAX_S24);
1281 MEDIA_DEBUG_FMT("s00:%f d00:%d s01:%f d01:%d\n",
1282 src[0], dst[0], src[1], dst[1]);
1288 sxe_msf_FLT_down(void *d, void *s, size_t len)
1290 /* convert samples from internal format (S24in32) to float */
1295 /* len is the number of samples (== #frame * #channels) */
1296 MEDIA_DEBUG_FMT("downsampling internal->FLT: %u samples\n", len);
1298 for (i = len-1; i >= 0; i--) {
1299 dst[i] = (float)(src[i]) / SXE_MAX_S24;
1301 MEDIA_DEBUG_FMT("d00:%f d01:%f\n", dst[0], dst[1]);
1307 DEFINE_MEDIA_SAMPLE_EFFECT(sxe_mse_1ch_to_2ch, _sxe_mse_1ch_to_2ch);
1310 _sxe_mse_1ch_to_2ch(sxe_media_sample_t *dst, sxe_media_sample_t *src,
1311 size_t len, void *ignored)
1313 /* mono to stereo converter */
1316 MEDIA_DEBUG_COE("mono->stereo: %u samples\n", len);
1318 /* len is the number of samples */
1319 for (i = len-1; i >= 0; i--) {
1321 dst[2*i+1] = src[i];
1327 DEFINE_MEDIA_SAMPLE_EFFECT(sxe_mse_2ch_to_1ch, _sxe_mse_2ch_to_1ch);
1330 _sxe_mse_2ch_to_1ch(sxe_media_sample_t *dst, sxe_media_sample_t *src,
1331 size_t len, void *args)
1333 /* stereo to mono converter */
1335 sxe_mse_2ch_to_1ch_args *_args = args;
1338 MEDIA_DEBUG_COE("stereo->mono: %u samples\n", len);
1344 /* len is the number of samples */
1345 for (i = 0; i < len/2; i++) {
1346 dst[i] = src[2*i+c];
1352 DEFINE_MEDIA_SAMPLE_EFFECT(sxe_mse_5ch_to_2ch, _sxe_mse_5ch_to_2ch);
1355 _sxe_mse_5ch_to_2ch(sxe_media_sample_t *dst, sxe_media_sample_t *src,
1356 size_t len, void *args)
1358 /* 5 channel to stereo converter */
1360 sxe_mse_5ch_to_2ch_args *_args = args;
1363 MEDIA_DEBUG_COE("5ch->stereo: %u samples\n", len);
1370 /* len is the number of samples */
1371 for (i = 0; i < len/5; i++) {
1372 dst[2*i] = src[5*i+c1];
1373 dst[2*i+1] = src[5*i+c2];
1379 DEFINE_MEDIA_SAMPLE_EFFECT(sxe_mse_volume, _sxe_mse_volume);
1382 _sxe_mse_volume(sxe_media_sample_t *dst, sxe_media_sample_t *src,
1383 size_t len, void *args)
1385 /* stereo to mono converter */
1387 sxe_mse_volume_args *_args = args;
1389 MEDIA_DEBUG_COE("volume: %u samples\n", len);
1391 /* len is the number of samples */
1392 for (i = 0; i < len; i+=_args->num_channels) {
1393 for (j = 0; j < (size_t)_args->num_channels; j++) {
1394 uint8_t vol = _args->volume[j];
1395 dst[i+j] = (src[i+j] * vol) >> 7;
1402 DEFINE_MEDIA_SAMPLE_EFFECT(sxe_mse_rerate, _sxe_mse_rerate);
1405 _sxe_mse_rerate(sxe_media_sample_t *dst, sxe_media_sample_t *src,
1406 size_t len, void *args)
1408 /* rate converter */
1410 sxe_mse_rerate_args *_args = args;
1411 float trafo = (float)_args->srcrate / (float)_args->tgtrate
1413 int chans = _args->num_channels;
1414 int bound = len/chans/trafo;
1416 MEDIA_DEBUG_COE("rerate: %u samples, final trafo: %f, bound is: %d\n",
1420 for (i = bound-1; i >= 0; i--) {
1421 int frame = i * trafo;
1422 dst[chans*i] = (src[chans*frame]);
1423 dst[chans*i+1] = (src[chans*frame+1]);
1425 } else if (trafo > 1.0) {
1426 for (i = 0; i < bound-1; i++) {
1427 int frame = i * trafo;
1428 dst[chans*i] = (src[chans*frame]);
1429 dst[chans*i+1] = (src[chans*frame+1]);
1433 return bound * chans;
1437 void syms_of_media(void)
1439 INIT_LRECORD_IMPLEMENTATION(media_stream);
1441 defsymbol(&Qmedia_streamp, "media-stream-p");
1446 DEFSYMBOL(Qnchannels);
1447 DEFSYMBOL(Qsamplerate);
1448 DEFSYMBOL(Qbitrate);
1449 DEFSYMBOL(Qabitrate);
1450 DEFSYMBOL(Qvbitrate);
1462 DEFSUBR(Fmake_media_stream);
1463 DEFSUBR(Fmedia_stream_p);
1465 DEFSUBR(Faudio_substream_p);
1466 DEFSUBR(Fvideo_substream_p);
1469 DEFSUBR(Fmedia_available_formats);
1470 DEFSUBR(Fmedia_properties);
1471 DEFSUBR(Fmedia_stream_aspect);
1474 defsymbol(&Qffmpeg, "ffmpeg");
1477 defsymbol(&Qmad, "mad");
1480 defsymbol(&Qsox, "sox");
1483 defsymbol(&Qsndfile, "sndfile");
1485 defsymbol(&Qunknown, "unknown");
1488 void vars_of_media(void)
1490 Fprovide(intern("media"));
1493 Fprovide(intern("media-ffmpeg"));
1496 Fprovide(intern("media-sndfile"));
1499 Fprovide(intern("media-mad"));
1502 Fprovide(intern("media-sox"));
1505 Fprovide(intern("media-xine"));
1507 #ifdef HAVE_GSTREAMER
1508 Fprovide(intern("media-gstreamer"));
1510 #ifdef HAVE_INTERNAL_MEDIA
1511 Fprovide(intern("media-internal"));
1515 /* media.c ends here */