Changeset 7110
- Timestamp:
- 01/27/10 06:19:40 (7 weeks ago)
- Location:
- trunk/liquidsoap
- Files:
-
- 1 added
- 1 removed
- 16 modified
- 1 copied
- 2 moved
-
configure.ac (modified) (2 diffs)
-
src/Makefile (modified) (7 diffs)
-
src/configure.mli (modified) (1 diff)
-
src/decoder (added)
-
src/decoder.ml (modified) (4 diffs)
-
src/decoder.mli (modified) (2 diffs)
-
src/decoder/Makefile (copied) (copied from trunk/liquidsoap/src/formats/Makefile) (1 diff)
-
src/decoder/mp3.ml (moved) (moved from trunk/liquidsoap/src/formats/mp3.ml) (4 diffs)
-
src/decoder/ogg_decoder.ml (moved) (moved from trunk/liquidsoap/src/formats/oggformat.ml) (3 diffs)
-
src/lang/lang_builtins.ml (modified) (2 diffs)
-
src/playlists/playlist_basic.ml (modified) (2 diffs)
-
src/request.ml (modified) (5 diffs)
-
src/request.mli (modified) (3 diffs)
-
src/sources/generated.ml (modified) (2 diffs)
-
src/sources/harbor_input.ml (modified) (10 diffs)
-
src/sources/http_source.ml (modified) (12 diffs)
-
src/sources/lastfm_input.ml (modified) (5 diffs)
-
src/stream/generator.ml (modified) (2 diffs)
-
src/stream/generator.mli (modified) (2 diffs)
-
src/tools/file_decoder.ml (deleted)
-
src/tools/harbor.ml (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/liquidsoap/configure.ac
r7102 r7110 1175 1175 w_MAGIC="no (requires ocaml-magic)" 1176 1176 cat >> src/configure.ml <<EOCONF 1177 let file_mime s= None1178 let data_mime ?len s= None1177 let file_mime = None 1178 let data_mime = None 1179 1179 EOCONF 1180 1180 else … … 1182 1182 cat >> src/configure.ml <<EOCONF 1183 1183 let magic_cookie = Magic.create ~flags:[[Magic.Mime; Magic.Symlink]] [[]] 1184 let file_mime s = Some (Magic.file magic_cookie s)1185 let data_mime ?len s = Some (Magic.buffer magic_cookie ?len s)1184 let file_mime = Some (Magic.file magic_cookie) 1185 let data_mime = Some (Magic.buffer magic_cookie) 1186 1186 EOCONF 1187 1187 fi -
trunk/liquidsoap/src/Makefile
r7106 r7110 1 1 2 SUBDIRS= tools formatsplaylists encoder protocols plugins stream lang \2 SUBDIRS= tools decoder playlists encoder protocols plugins stream lang \ 3 3 ogg_formats video_converters audio_converters \ 4 4 operators sources conversions outputs io visualization analyze synth … … 32 32 $(if $(W_GAVL),video_converters/gavl_converter.ml) 33 33 34 # TODO move all formats to encoders 34 35 formats = \ 35 $(if $(W_OGG),formats/oggformat.ml) \36 36 $(if $(W_VORBIS),formats/vorbisduration.ml) \ 37 37 $(if $(W_TAGLIB),formats/taglib_plug.ml) \ … … 39 39 formats/externalformat.ml \ 40 40 formats/midiformat.ml \ 41 $(if $(W_MP3),formats/mp3.ml)42 41 # $(if $(W_FAAD),formats/aacformat.ml) \ 42 43 decoders = \ 44 $(if $(W_OGG),decoder/ogg_decoder.ml) \ 45 $(if $(W_MP3),decoder/mp3.ml) 43 46 44 47 playlists = \ … … 57 60 sources/req_queue.ml sources/req_equeue.ml \ 58 61 sources/generated.ml sources/http_source.ml \ 59 $(if $(W_MP3),sources/http_mp3.ml) \60 $(if $(W_OGG),sources/http_ogg.ml) \61 $(if $(W_FAAD),sources/http_aac.ml) \62 62 $(if $(W_ALSA),sources/alsa_in.ml) \ 63 63 $(if $(W_BJACK),sources/bjack_in.ml) \ … … 110 110 $(if $(W_GRAPHICS),outputs/graphics_out.ml) \ 111 111 $(if $(W_ALSA),outputs/alsa_out.ml) \ 112 $(if $(W_BJACK),outputs/bjack_out.ml) \ 113 # outputs/external_encoded.ml \ 112 $(if $(W_BJACK),outputs/bjack_out.ml) 114 113 115 114 io = \ … … 157 156 lang/lang_types.ml lang/lang_values.ml \ 158 157 lang/lang_lexer.ml lang/lang_parser.ml lang/lang_pp.ml lang/lang.ml \ 159 tools/ioRing.ml tools/ file_decoder.ml tools/mutils.ml \158 tools/ioRing.ml tools/mutils.ml \ 160 159 $(video_converters) $(audio_converters) $(ogg_demuxer) $(protocols) \ 161 160 $(sources) $(operators) $(conversions) $(outputs) \ 162 161 $(encoders) $(ogg_muxer) $(io) \ 163 $(analyze) $(playlists) $(visualization) $(synth) $( formats) \162 $(analyze) $(playlists) $(visualization) $(synth) $(decoders) \ 164 163 shebang.ml \ 165 164 lang/lang_builtins.ml \ … … 227 226 tools/harbor.ml $(video_converters) $(audio_converters) \ 228 227 $(ogg_utils) $(protocols) $(sources) $(operators) $(outputs) $(io) \ 229 $(analyze) $(playlists) $( formats) \228 $(analyze) $(playlists) $(decoders) \ 230 229 lang/lang_builtins.ml 231 230 libliq_objects=$(libliq_sources:.ml=.$(o)) -
trunk/liquidsoap/src/configure.mli
r6172 r7110 21 21 22 22 (** Magic mime detection *) 23 val file_mime : string -> stringoption24 val data_mime : ?len:int -> string -> stringoption23 val file_mime : (string -> string) option 24 val data_mime : (?len:int -> string -> string) option 25 25 26 26 val requests_table_size : int -
trunk/liquidsoap/src/decoder.ml
r6925 r7110 2 2 3 3 Liquidsoap, a programmable audio stream generator. 4 Copyright 2003-20 09Savonet team4 Copyright 2003-2010 Savonet team 5 5 6 6 This program is free software; you can redistribute it and/or modify … … 21 21 *****************************************************************************) 22 22 23 (** Plug for file decoding, in which [src/formats] plugins come. *) 23 (** Media decoding infrastructure. 24 * 25 * We treat files and streams. 26 * We separate detection from the actual decoding. 27 * For files, the decoder detection function is passed a filename and 28 * an expected content kind. 29 * For streams, it is passed a MIME type and a content kind. 30 * 31 * In practice, most file decoders will be based on stream decoders, 32 * with a specific (more precise) detection function. Although 33 * we cannot force it at this point, we provide some infrastructure 34 * to help. 35 * 36 * In the short term, the plug infrastructure should provide 37 * a way to ban / prioritize 38 * plugins. For example: 39 * - choose ogg_demuxer when extension = ogg 40 * - choose mad when extension = mp3 41 * - choose mad when mime-type = audio/mp3 42 * 43 * A few comments about the changes from the old decoding infrastructure: 44 * 45 * We need to change the closing policy (necessary to release resources 46 * immediately). In particular stream decoders shouldn't be in charge 47 * of closing the stream (sink) since they didn't open it: this trivializes 48 * the stream interface (sink) to an input function and a THREAD-SAFE 49 * generator. 50 * 51 * In MAD, we used openfile and openstream, but unifying doesn't change 52 * the performance. 53 * 54 * Estimating the remaining time can be done externally, based on the 55 * file description. This is equivalent to what is done currently, 56 * except in WAV. 57 * 58 * The WAV decoder doesn't fit the approx duration computation. 59 * The MIDI decoder doesn't use a buffer. TODO look at this carefully. *) 24 60 25 61 open Dtools … … 27 63 let log = Log.make ["decoder"] 28 64 65 (** A local file is simply identified by its filename. *) 66 type file = string 67 68 (** A stream is identified by a MIME type. *) 69 type stream = string 70 71 type 'a decoder = Decoder of ('a -> unit) 72 73 type input = int -> string * int 74 75 (** A stream decoder does not "own" any file descriptor, 76 * and is generally assumed to not allocate resources (in the sense 77 * of things that should be explicitly managed, not just garbage collected). 78 * Hence it does not need a close function. *) 79 type stream_decoder = input -> Generator.From_audio_video.t decoder 80 29 81 (** A decoder is a filling function and a closing function, 30 * called as soon as a filling fails, i.e. is partial. *) 31 type decoder = { 82 * called at least when filling fails, i.e. the frame is partial. 83 * The closing function can be called earlier e.g. if the user skips. 84 * In most cases, file decoders are wrapped stream decoders. *) 85 type file_decoder = { 32 86 fill : Frame.t -> int ; (* Return remaining ticks. *) 33 87 close : unit -> unit ; 34 88 } 35 89 36 (** Plugins are given filenames and return a decoder, if possible. *) 37 let formats : (string -> Frame.content_kind -> decoder option) Plug.plug = 90 (** Plugins might define various decoders. In order to be accessed, 91 * they should also register methods for choosing decoders. *) 92 93 (** For a given file, once a decoder is chosen it can be used several 94 * times. This is at least useful to separate the actual opening of 95 * the file from checking that it is a valid media file. *) 96 let file_decoders : 97 (file -> Frame.content_kind -> (unit -> file_decoder) option) 98 Plug.plug = 38 99 Plug.create 39 ~doc:"Method to read audio files." ~insensitive:true "audio file formats" 100 ~doc:"File decoding methods." ~insensitive:true "file decoding" 101 102 let stream_decoders : 103 (stream -> Frame.content_kind -> stream_decoder option) Plug.plug = 104 Plug.create 105 ~doc:"Stream decoding methods." ~insensitive:true "stream decoding" 40 106 41 107 let dummy = … … 45 111 close = (fun _ -> ()) } 46 112 47 exception Exit of (string * (string -> Frame.content_kind -> decoder option)) 48 (** Get a valid decoder creator for [filename]. 49 * The validity is not based on file extension but only on success of the 50 * decoder instantiation. 51 * Being based on file extension is weak, and troublesome when accessing a 52 * remote file -- that would force us to create a local temporary file with the 53 * same extension. *) 54 let search_valid filename kind : (string*(unit -> decoder)) option = 113 exception Exit of (string * (unit -> file_decoder)) 114 115 (** Get a valid decoder creator for [filename]. *) 116 let get_file_decoder filename kind : (unit -> file_decoder) option = 55 117 try 56 formats#iter ~rev:true 57 (fun format decoder -> 58 log#f 4 "Trying %s decoder for %S..." format filename ; 59 match decoder filename kind with 60 | Some d -> 61 d.close () ; 62 log#f 3 "Decoder %s chosen for %S." format filename ; 63 raise (Exit (format, decoder)) 64 | None -> ()) ; 118 file_decoders#iter ~rev:true 119 (fun name decoder -> 120 log#f 4 "Trying method %S for %S..." name filename ; 121 match try decoder filename kind with _ -> None with 122 | Some f -> 123 log#f 3 "Method %S accepted %S." name filename ; 124 raise (Exit (name,f)) 125 | None -> ()) ; 65 126 log#f 3 "Unable to decode %S!" filename ; 66 127 None 67 128 with 68 | Exit (format,d) -> 69 Some (format,fun () -> 70 match d filename kind with 71 | None -> 72 log#f 2 "Decoder %s betrayed us on %S!" format filename ; 73 dummy 74 | Some d -> d) 129 | Exit (name,f) -> 130 Some (fun () -> 131 try f () with _ -> 132 log#f 2 "Decoder %S betrayed us on %S!" name filename ; 133 dummy) 134 135 exception Exit of stream_decoder 136 137 let get_stream_decoder mime kind = 138 try 139 stream_decoders#iter 140 (fun name decoder -> 141 log#f 4 "Trying method %S for %S..." name mime ; 142 match try decoder mime kind with _ -> None with 143 | Some f -> 144 log#f 3 "Method %S accepted %S." name mime ; 145 raise (Exit f) 146 | None -> ()) ; 147 log#f 3 "Unable to decode stream of type %S!" mime ; 148 None 149 with 150 | Exit f -> Some f 151 152 (** {1 Helpers for defining decoders} *) 153 154 module Buffered(Generator:Generator.S) = 155 struct 156 157 let file_decoder filename kind create_decoder gen = 158 let frame_size = Lazy.force Frame.size in 159 let file_size = (Unix.stat filename).Unix.st_size in 160 let fd = Unix.openfile filename [Unix.O_RDONLY] 0 in 161 let input len = 162 try 163 let s = String.create len in 164 let i = Unix.read fd s 0 len in 165 s, i 166 with _ -> "", 0 167 in 168 let Decoder decoder = create_decoder input in 169 let out_ticks = ref 0 in 170 let fill frame = 171 begin try 172 while Generator.length gen < frame_size do 173 decoder gen 174 done 175 with _ -> () 176 end ; 177 178 let offset = Frame.position frame in 179 let old_breaks = Frame.breaks frame in 180 Generator.fill gen frame ; 181 if 182 (* Check that we got only one chunk of data, 183 * and that it has a correct type. *) 184 let end_pos,content = Frame.content frame offset in 185 not (end_pos = Frame.position frame && 186 Frame.type_has_kind (Frame.type_of_content content) kind) 187 then begin 188 log#f 2 "Decoder of %S didn't respect its content type!" filename ; 189 (* Pretend nothing happened, and end decoding. 190 * We first restore a content layer with a valid type, so that 191 * the code which reads that frame doesn't see the anomaly. 192 * Then we reset breaks to indicate that there's no more data. *) 193 let _ = 194 Frame.content_of_type frame offset (Frame.type_of_kind kind) 195 in 196 Frame.set_breaks frame old_breaks ; 197 Frame.add_break frame offset ; 198 0 199 end else 200 let in_bytes = Unix.lseek fd 0 Unix.SEEK_CUR in 201 let gen_len = Generator.length gen in 202 out_ticks := !out_ticks + Frame.position frame - offset ; 203 (* Compute an estimated number of remaining ticks. *) 204 if in_bytes = 0 then -1 else 205 let compression = 206 (float (!out_ticks+gen_len)) /. (float in_bytes) 207 in 208 let remaining_ticks = 209 (float gen_len) +. 210 (float (file_size - in_bytes)) *. compression 211 in 212 int_of_float remaining_ticks 213 in 214 { fill = fill ; 215 close = fun () -> Unix.close fd } 216 217 end -
trunk/liquidsoap/src/decoder.mli
r6925 r7110 2 2 3 3 Liquidsoap, a programmable audio stream generator. 4 Copyright 2003-20 09Savonet team4 Copyright 2003-2010 Savonet team 5 5 6 6 This program is free software; you can redistribute it and/or modify … … 21 21 *****************************************************************************) 22 22 23 type decoder = { fill : Frame.t -> int; close : unit -> unit; } 23 val log : Dtools.Log.t 24 24 25 val formats : (string -> Frame.content_kind -> decoder option) Plug.plug 25 type file = string 26 type stream = string 26 27 27 val search_valid : 28 string -> Frame.content_kind -> (string * (unit -> decoder)) option 28 type input = int -> string * int 29 30 type 'a decoder = Decoder of ('a -> unit) 31 type stream_decoder = input -> Generator.From_audio_video.t decoder 32 type file_decoder = { fill : Frame.t -> int; close : unit -> unit; } 33 34 val file_decoders : 35 (file -> Frame.content_kind -> (unit -> file_decoder) option) 36 Plug.plug 37 val stream_decoders : 38 (stream -> Frame.content_kind -> stream_decoder option) Plug.plug 39 40 val get_file_decoder : 41 file -> Frame.content_kind -> (unit -> file_decoder) option 42 val get_stream_decoder : 43 file -> Frame.content_kind -> stream_decoder option 44 45 module Buffered : 46 functor (Generator : Generator.S) -> 47 sig 48 val file_decoder : 49 string -> 50 Frame.content_kind -> 51 (input -> Generator.t decoder) -> 52 Generator.t -> 53 file_decoder 54 end -
trunk/liquidsoap/src/decoder/Makefile
r2703 r7110 4 4 top_srcdir= ../.. 5 5 include $(top_srcdir)/Makefile.rules 6 7 id3tag_stubs.o: id3tag_stubs.c8 $(CC) $(CFLAGS) -c -g -Wall -I$(shell $(OCAMLC) -where) $<9 10 test_id3tag: id3tag_stubs.o id3tag.mli id3tag.ml test_id3tag.ml11 ocamlc id3tag.mli12 ocamlopt -c id3tag.ml13 ocamlopt -cclib "$(LDFLAGS)" unix.cmxa id3tag.cmx id3tag_stubs.o test_id3tag.ml -cclib -lid3tag -o test_id3tag -
trunk/liquidsoap/src/decoder/mp3.ml
r6927 r7110 2 2 3 3 Liquidsoap, a programmable audio stream generator. 4 Copyright 2003-20 09Savonet team4 Copyright 2003-2010 Savonet team 5 5 6 6 This program is free software; you can redistribute it and/or modify … … 21 21 *****************************************************************************) 22 22 23 (** Decode and read metadata s ofmp3 files. *)23 (** Decode and read metadata from mp3 files. *) 24 24 25 25 open Dtools 26 26 27 27 module Generator = Generator.From_audio_video 28 module Buffered = Decoder.Buffered(Generator) 28 29 29 30 let log = Log.make ["format";"mp3"] 31 32 let create_decoder input = 33 let resampler = Rutils.create_audio () in 34 let fd = Mad.openstream input in 35 Decoder.Decoder (fun gen -> 36 let data = Mad.decode_frame_float fd in 37 let sample_freq,channels,_ = Mad.get_output_format fd in 38 let content,length = 39 resampler ~audio_src_rate:(float sample_freq) data 40 in 41 let content = 42 if Random.int 100 = 0 then [|content.(0)|] else content 43 in 44 Generator.put_audio gen content 0 (Array.length content.(0))) 45 46 let create_file_decoder filename kind = 47 let generator = Generator.create Generator.Audio in 48 Buffered.file_decoder filename kind create_decoder generator 30 49 31 50 let conf_mad = 32 51 Conf.void ~p:(Configure.conf#plug "mad") 33 52 "Mad (mp3) decoding options." 34 let conf_mime =35 Conf.bool ~p:(conf_mad#plug "check_mime") ~d:true36 "Enable magic mime test, if compiled."37 53 let conf_mime_types = 38 54 Conf.list ~p:(conf_mad#plug "mime_types") … … 40 56 ~d:["audio/mpeg";"application/octet-stream";"video/x-unknown"] 41 57 42 let check file = 43 if conf_mime#get then 44 match Configure.file_mime file with 45 (* libmagic does not always detect mp3 as audio/mpeg 46 * (Repeat after me: mp3 formats sucks, mp3 format.... ) 47 * So it might return other types. 48 * We hope other formats like jpeg, gif, etc.. 49 * will most likely be correctly detected *) 50 | Some s when List.mem s conf_mime_types#get -> true 51 | None -> true 52 | Some s -> 53 log#f 2 "Mime type for %s is not valid: %s" file s; 54 false 55 else 56 (** Mime check disabled.. *) 57 true 58 (* Get the number of channels of audio in an MP3 file. 59 * This is done by decoding a first chunk of data, thus checking 60 * that libmad can actually open the file -- which doesn't mean much. *) 61 let get_type filename = 62 let fd = Mad.openfile filename in 63 ignore(Mad.decode_frame_float fd); 64 let rate,channels,_ = Mad.get_output_format fd in 65 log#f 4 66 "Libmad recognizes %S as MP3 (%dHz,%d channels)." 67 filename rate channels ; 68 Mad.close fd ; 69 { Frame. 70 audio = channels ; 71 video = 0 ; 72 midi = 0 } 58 73 59 let decoder = 60 { 61 File_decoder. 62 log = log; 63 openfile = 64 (fun file -> 65 if not (check file) then 66 assert false; 67 Mad.openfile file, 68 Rutils.create_audio ()); 69 get_kind = 70 (fun (fd,_) -> 71 (* Decode some data *) 72 ignore(Mad.decode_frame_float fd); 73 (* Get number of channels *) 74 let _,channels,_ = Mad.get_output_format fd in 75 { Frame. 76 audio = Frame.mul_of_int channels ; 77 video = Frame.mul_of_int 0 ; 78 midi = Frame.mul_of_int 0 }); 79 decode = 80 (fun (fd,converter) buffer -> 81 let data = Mad.decode_frame_float fd in 82 let sample_freq,_,_ = Mad.get_output_format fd in 83 let content,length = 84 converter ~audio_src_rate:(float sample_freq) 85 data 86 in 87 Generator.put_audio buffer content 0 length); 88 position = (fun (fd,_) -> Mad.get_current_position fd); 89 close = (fun (fd,_) -> Mad.close fd) 90 } 74 let () = 75 match Configure.file_mime with 76 | None -> 77 Decoder.file_decoders#register 78 "MP3/libmad" 79 ~sdoc:"Use libmad to decode MP3, if it works." 80 (fun filename kind -> 81 if Frame.type_has_kind (get_type filename) kind then 82 Some (fun () -> create_file_decoder filename kind) 83 else begin 84 None 85 end) 86 | Some mime_type -> 87 Decoder.file_decoders#register 88 "MP3/libmad/mime" 89 ~sdoc:"Use libmad to decode MP3 if MIME type is appropriate." 90 (fun filename kind -> 91 let mime = mime_type filename in 92 if not (List.mem mime conf_mime_types#get) then begin 93 log#f 3 "Invalid MIME type for %s: %s!" filename mime ; 94 None 95 end else 96 if kind.Frame.audio = Frame.Variable || 97 kind.Frame.audio = Frame.Succ Frame.Variable || 98 (* libmad always respects the first two kinds *) 99 Frame.type_has_kind (get_type filename) kind 100 then 101 Some (fun () -> create_file_decoder filename kind) 102 else 103 None) 104 105 let () = 106 Decoder.stream_decoders#register 107 "MP3/libmad" 108 ~sdoc:"Use libmad to decode any stream with an appropriate MIME type." 109 (fun mime kind -> 110 let (<:) a b = Frame.mul_sub_mul a b in 111 if List.mem mime conf_mime_types#get && 112 kind.Frame.video <: Frame.Zero && 113 kind.Frame.midi <: Frame.Zero && 114 kind.Frame.audio <> Frame.Zero 115 then 116 (* In fact we can't be sure that we'll satisfy the content 117 * kind, because the MP3 stream might be mono or stereo. 118 * For now, we let this problem result in an error at 119 * decoding-time. Failing early would only be an advantage 120 * if there was possibly another plugin for decoding 121 * correctly the stream (e.g. by performing conversions). *) 122 Some create_decoder 123 else 124 None) 125 126 let check filename = 127 match Configure.file_mime with 128 | Some f -> List.mem (f filename) conf_mime_types#get 129 | None -> (try ignore (get_type filename) ; true with _ -> false) 91 130 92 131 let duration file = 93 if not (check file) then 94 raise Not_found; 132 if not (check file) then raise Not_found ; 95 133 let ans = Mad.duration file in 96 134 match ans with … … 98 136 | _ -> ans 99 137 100 let () = Decoder.formats#register "MP3" (File_decoder.decode decoder);101 Request.dresolvers#register "MP3" duration138 let () = 139 Request.dresolvers#register "MP3" duration -
trunk/liquidsoap/src/decoder/ogg_decoder.ml
r7048 r7110 2 2 3 3 Liquidsoap, a programmable audio stream generator. 4 Copyright 2003-20 09Savonet team4 Copyright 2003-2010 Savonet team 5 5 6 6 This program is free software; you can redistribute it and/or modify … … 23 23 (** Decode and read ogg files. *) 24 24 25 module Generator = Generator.From_audio_video 26 module Buffered = Decoder.Buffered(Generator) 27 25 28 let log = Dtools.Log.make ["format";"ogg"] 26 27 module Generator = Generator.From_audio_video28 29 29 30 exception Channels of int … … 109 110 end 110 111 111 type state = { 112 decoder : Ogg_demuxer.t ; 113 fd : Unix.file_descr ; 114 video_convert : Ogg_demuxer.video -> RGB.t ; 115 audio_resample : ?audio_src_rate:float -> Float_pcm.pcm -> Float_pcm.pcm*int ; 116 video_resample : in_freq:int -> out_freq:int -> 117 RGB.t array -> int -> int -> RGB.t array 118 } 119 120 let decoder = 121 { 122 File_decoder. 123 log = log; 124 openfile = 125 (fun file -> 126 let sync,fd = Ogg.Sync.create_from_file file in 127 begin 128 try 129 { 130 decoder = Ogg_demuxer.init sync ; 131 fd = fd ; 132 video_convert = video_convert () ; 133 audio_resample = Rutils.create_audio () ; 134 video_resample = video_resample () 135 } 136 with 137 | e -> (try Unix.close fd with _ -> ()); raise e 138 end); 139 get_kind = 140 (fun {decoder=decoder} -> 141 let feed ((buf,_),_) = 142 raise (Channels (Array.length buf)) 112 (* TODO this mimicks the old code, but in the near future decoding should 113 * take into account the target content kind, e.g. for dropping channels *) 114 let create_decoder input = 115 let decoder = 116 let sync = Ogg.Sync.create input in 117 Ogg_demuxer.init sync 118 in 119 let video_convert = video_convert () in 120 let audio_resample = Rutils.create_audio () in 121 let video_resample = video_resample () in 122 Decoder.Decoder (fun buffer -> 123 if Ogg_demuxer.eos decoder then 124 raise Ogg_demuxer.End_of_stream; 125 let feed ((buf,sample_freq),_) = 126 let audio_src_rate = float sample_freq in 127 let content,length = 128 audio_resample ~audio_src_rate buf 143 129 in 144 let audio = 145 try 146 Ogg_demuxer.decode_audio_rec decoder feed; 147 raise Not_found 148 with 149 | Channels x -> x 150 | Not_found -> 0 151 in 152 let feed (buf,_) = 153 raise (Channels 1) 154 in 155 let video = 156 try 157 Ogg_demuxer.decode_video_rec decoder feed; 158 raise Not_found 159 with 160 | Channels x -> x 161 | Not_found -> 0 162 in 163 { Frame. 164 audio = Frame.mul_of_int audio ; 165 video = Frame.mul_of_int video ; 166 midi = Frame.mul_of_int 0 }); 167 decode = 168 (fun s buffer -> 169 if Ogg_demuxer.eos s.decoder then 170 raise Ogg_demuxer.End_of_stream; 171 let feed ((buf,sample_freq),_) = 172 let audio_src_rate = float sample_freq in 173 let content,length = 174 s.audio_resample ~audio_src_rate buf 175 in 176 Generator.put_audio buffer content 0 length 177 in 178 let got_audio = 179 try 180 Ogg_demuxer.decode_audio s.decoder feed ; 181 true 182 with 183 | Not_found 184 | Ogg.Not_enough_data -> false 185 in 186 let feed (buf,_) = 187 let in_freq = int_of_float buf.Ogg_demuxer.fps in 188 let out_freq = Lazy.force Frame.video_rate in 189 let rgb = s.video_convert buf in 190 let stream = s.video_resample ~in_freq ~out_freq [|rgb|] 0 1 in 191 Generator.put_video buffer [|stream|] 0 (Array.length stream) 192 in 193 (* Try to decode video. *) 194 let got_video = 195 try 196 Ogg_demuxer.decode_video s.decoder feed ; 197 true 198 with 199 | Not_found 200 | Ogg.Not_enough_data -> false 201 in 202 if not got_audio && not got_video then 203 Ogg_demuxer.feed s.decoder); 204 position = (fun s -> Unix.lseek s.fd 0 Unix.SEEK_CUR); 205 close = (fun s -> 206 try 207 Unix.close s.fd 208 with _ -> ()) 209 } 210 211 let () = Decoder.formats#register "OGG" (File_decoder.decode decoder) 130 Generator.put_audio buffer content 0 length 131 in 132 let got_audio = 133 try 134 Ogg_demuxer.decode_audio decoder feed ; 135 true 136 with 137 | Not_found 138 | Ogg.Not_enough_data -> false 139 in 140 let feed (buf,_) = 141 let in_freq = int_of_float buf.Ogg_demuxer.fps in 142 let out_freq = Lazy.force Frame.video_rate in 143 let rgb = video_convert buf in 144 let stream = video_resample ~in_freq ~out_freq [|rgb|] 0 1 in 145 Generator.put_video buffer [|stream|] 0 (Array.length stream) 146 in 147 (* Try to decode video. *) 148 let got_video = 149 try 150 Ogg_demuxer.decode_video decoder feed ; 151 true 152 with 153 | Not_found 154 | Ogg.Not_enough_data -> false 155 in 156 if not got_audio && not got_video then 157 Ogg_demuxer.feed decoder) 158 159 let create_file_decoder filename content_type kind = 160 let mode = 161 match content_type.Frame.video, content_type.Frame.audio with 162 | 0, _ -> Generator.Audio 163 | _, 0 -> Generator.Video 164 | _, _ -> Generator.Both 165 in 166 let generator = Generator.create mode in 167 Buffered.file_decoder filename kind create_decoder generator 168 169 let get_type filename = 170 let sync,fd = Ogg.Sync.create_from_file filename in 171 let decoder = Ogg_demuxer.init sync in 172 let feed ((buf,_),_) = 173 raise (Channels (Array.length buf)) 174 in 175 let audio = 176 try 177 Ogg_demuxer.decode_audio_rec decoder feed; 178 raise Not_found 179 with 180 | Channels x -> x 181 | Not_found -> 0 182 in 183 let feed (buf,_) = 184 raise (Channels 1) 185 in 186 let video = 187 try 188 Ogg_demuxer.decode_video_rec decoder feed; 189 raise Not_found 190 with 191 | Channels x -> x 192 | Not_found -> 0 193 in 194 let c_type = 195 { Frame. 196 audio = audio ; 197 video = video ; 198 midi = 0 } 199 in 200 Unix.close fd ; 201 c_type 202 203 let () = 204 Decoder.file_decoders#register "OGG" 205 ~sdoc:"Decode a file as OGG provided that libogg accepts it." 206 (fun filename kind -> 207 let content_type = get_type filename in 208 if Frame.type_has_kind content_type kind then 209 Some (fun () -> create_file_decoder filename content_type kind) 210 else 211 None) 212 212 213 213 exception Metadata of (string*string) list 214 214 215 let get_tags ~format file = 216 (* Fail if file is not decoded using the OGG demuxer. *) 217 if format <> "OGG" then 218 raise Not_found; 215 let get_tags file = 219 216 let sync,fd = Ogg.Sync.create_from_file file in 220 217 let close () = -
trunk/liquidsoap/src/lang/lang_builtins.ml
r7057 r7110 157 157 Lang.unit) 158 158 159 (* TODO restore the following code when Wav and External decoding are back 160 159 161 let () = 160 162 let process_t = … … 216 218 Externalformat.register_external_metadata_resolver format resolver; 217 219 Lang.unit) 220 *) 218 221 219 222 let () = -
trunk/liquidsoap/src/playlists/playlist_basic.ml
r6608 r7110 2 2 3 3 Liquidsoap, a programmable audio stream generator. 4 Copyright 2003-20 09Savonet team4 Copyright 2003-2010 Savonet team 5 5 6 6 This program is free software; you can redistribute it and/or modify … … 29 29 30 30 let test_text s = 31 match Configure.data_mime swith31 match Configure.data_mime with 32 32 | None -> () 33 | Some e when Pcre.pmatch ~pat:"text/.*" e -> () 34 | Some e -> 35 begin 36 log#f 3 "Wrong mime type %s for playlist" e ; 37 assert false 38 end 33 | Some get_mime -> 34 let mime = get_mime s in 35 if not (Pcre.pmatch ~pat:"text/.*" mime) then begin 36 log#f 3 "Wrong mime type %s for playlist!" mime ; 37 (* TODO this shouldn't be an assert false, it can happen *) 38 assert false 39 end 39 40 40 41 (* This parser cannot detect the format !! *) -
trunk/liquidsoap/src/request.ml
r7057 r7110 2 2 3 3 Liquidsoap, a programmable audio stream generator. 4 Copyright 2003-20 09Savonet team4 Copyright 2003-2010 Savonet team 5 5 6 6 This program is free software; you can redistribute it and/or modify … … 154 154 155 155 (** Requests. 156 * The purpose of a request is to get a valid file. The file can be audio,156 * The purpose of a request is to get a valid file. The file can contain media 157 157 * in which case validity implies finding a working decoder, or can be 158 158 * something arbitrary, like a playlist. … … 217 217 mutable root_metadata : metadata ; 218 218 mutable indicators : indicator list list ; 219 mutable decoder : (unit -> Decoder. decoder) option ;219 mutable decoder : (unit -> Decoder.file_decoder) option ; 220 220 } 221 221 … … 317 317 let indicator = peek_indicator t in 318 318 let name = indicator.string in 319 match Decoder. search_validname kind with320 | Some (format,f)->319 match Decoder.get_file_decoder name kind with 320 | Some f -> 321 321 t.decoder <- Some f ; 322 322 mresolvers#iter 323 323 (fun _ resolver -> 324 324 try 325 let ans = resolver ~formatname in325 let ans = resolver name in 326 326 List.iter 327 327 (fun (k,v) -> … … 378 378 try 379 379 dresolvers#iter 380 (fun formatresolver ->380 (fun name resolver -> 381 381 try 382 382 let ans = resolver file in -
trunk/liquidsoap/src/request.mli
r7057 r7110 2 2 3 3 Liquidsoap, a programmable audio stream generator. 4 Copyright 2003-20 09Savonet team4 Copyright 2003-2010 Savonet team 5 5 6 6 This program is free software; you can redistribute it and/or modify … … 163 163 (** Return a decoder if the file has been resolved, guaranteed to have 164 164 * available data to deliver. *) 165 val get_decoder : t -> Decoder. decoder option165 val get_decoder : t -> Decoder.file_decoder option 166 166 167 167 (** {1 Plugs} … … 173 173 174 174 val dresolvers : (string -> float) Plug.plug 175 val mresolvers : ( format:string ->string -> (string*string) list) Plug.plug175 val mresolvers : (string -> (string*string) list) Plug.plug 176 176 val protocols : protocol Plug.plug -
trunk/liquidsoap/src/sources/generated.ml
r7029 r7110 2 2 3 3 Liquidsoap, a programmable audio stream generator. 4 Copyright 2003-20 09Savonet team4 Copyright 2003-2010 Savonet team 5 5 6 6 This program is free software; you can redistribute it and/or modify … … 21 21 *****************************************************************************) 22 22 23 module type Generator_t = 24 sig 25 type t 26 val length : t -> int (* ticks *) 27 val remaining : t -> int (* ticks *) 28 val clear : t -> unit 29 val fill : t -> Frame.t -> unit 30 val add_metadata : t -> Frame.metadata -> unit 31 end 32 33 module Make (Generator:Generator_t) = 23 module Make (Generator:Generator.S) = 34 24 struct 35 25 -
trunk/liquidsoap/src/sources/harbor_input.ml
r7052 r7110 2 2 3 3 Liquidsoap, a programmable audio stream generator. 4 Copyright 2003-20 09Savonet team4 Copyright 2003-2010 Savonet team 5 5 6 6 This program is free software; you can redistribute it and/or modify … … 24 24 open Http_source 25 25 26 27 26 (* {1 Input handling} *) 28 29 exception NoDecoder30 27 31 28 class http_input_server ~kind ~dumpfile ~logfile … … 33 30 ~on_connect ~on_disconnect 34 31 ~login ~debug = 35 let abg_max_len = Frame.audio_of_seconds max in 32 (* let abg_max_len = Frame.audio_of_seconds max in 33 TODO handle buffer overflow, cf. input.http *) 36 34 object (self) 37 35 inherit Source.source kind … … 43 41 val mutable relaying = false 44 42 val mutable ns = [] 45 val mutable decoder = fun _ -> raise NoDecoder46 val mutable stype = None43 val mutable create_decoder = fun _ -> assert false 44 val mutable mime_type = None 47 45 48 46 val mutable dump = None … … 65 63 Generator.add_metadata generator m 66 64 67 method put sample_freq data = 68 if not relaying then failwith "relaying stopped" ; 69 Mutex.lock lock ; 70 (* TODO There must be two ways of handling overfull generator: 71 * (1) when streaming, one should just stop the decoder for a while; 72 * (2) when not streaming, one should throw some data. 73 * Doing 1 instead of 2 can lead to deconnections. 74 * Doing 2 instead of 1 leads to ugly sound. 75 * Here, we drop data since we want to remain 76 * connected to the client. *) 77 if Generator.length generator >= abg_max_len then 78 begin 79 Mutex.unlock lock ; 80 Thread.delay (max /. 3.) ; 81 Mutex.lock lock ; 82 if Generator.length generator >= abg_max_len then 83 (* Here, we drop some data after the maximun buffer has been filled. 84 * Delaying the function can lead to deconnection/connection cycles 85 * when the source is not pulled. *) 86 Generator.remove generator (Generator.length generator - abg_max_len) 87 end ; 88 (* TODO: convert sample rate *) 89 Generator.put_audio generator data 0 (Array.length data.(0)); 90 Mutex.unlock lock 91 92 method register_decoder s = 93 match 94 Http_source.stream_decoders#get s 95 with 96 | Some d -> decoder <- d ; stype <- Some s 97 | None -> raise Harbor.Unknown_codec 98 99 method get_type = stype 100 101 method feed socket = 65 (* TODO There must be two ways of handling overfull generator: 66 * (1) when streaming, one should just stop the decoder for a while; 67 * (2) when not streaming, one should throw some data. 68 * Doing 1 instead of 2 can lead to deconnections. 69 * Doing 2 instead of 1 leads to ugly sound. 70 * Here, we USED TO drop data since we want to remain 71 * connected to the client. 72 * TODO Restore the old behavior. *) 73 74 method get_mime_type = mime_type 75 76 method feed socket = 102 77 self#log#f 3 "Decoding..." ; 103 let close () = () in104 78 let t0 = Unix.gettimeofday () in 105 79 let read len = … … 112 86 if float n >= Harbor.conf_timeout#get then 113 87 begin 114 self#log#f 4 " network activity timeout! disconnecting source" ;88 self#log#f 4 "Network activity timeout! Disconnecting source." ; 115 89 self#disconnect 116 90 end … … 122 96 let input = Unix.read socket buf 0 len in 123 97 if input<=0 then raise End_of_file ; 124 let s = String.sub buf 0 input in125 98 begin match dump with 126 | Some b -> output_string b s99 | Some b -> output_string b (String.sub buf 0 input) 127 100 | None -> () 128 101 end ; … … 133 106 | None -> () 134 107 end ; 135 s108 buf,input 136 109 in 137 let sink = 138 { put = self#put ; read = read ; 139 insert_metadata = self#insert_metadata ; close = close } 140 in 141 begin 142 try decoder sink with 143 | e -> self#log#f 2 "Feeding stopped: %s" (Printexc.to_string e) ; 144 if debug then raise e 145 end; 146 self#disconnect ; 147 try 148 Unix.close socket 149 with 150 | _ -> () 110 let Decoder.Decoder decoder = create_decoder read in 111 try 112 while true do 113 if not relaying then failwith "relaying stopped" ; 114 decoder generator 115 done 116 with 117 | e -> 118 self#log#f 2 "Feeding stopped: %s" (Printexc.to_string e) ; 119 if debug then raise e ; 120 self#disconnect ; 121 begin try Unix.close socket with _ -> () end 151 122 152 123 method private wake_up _ = … … 176 147 method private sleep = 177 148 if relaying then self#disconnect 149 150 method register_decoder mime = 151 match 152 Decoder.get_stream_decoder mime kind 153 with 154 | Some d -> create_decoder <- d ; mime_type <- Some mime 155 | None -> raise Harbor.Unknown_codec 178 156 179 157 method relay (headers:(string*string) list) socket = … … 220 198 221 199 let () = 222 (* TODO: handle video too! *)223 200 let kind = Lang.kind_type_of_kind_format ~fresh:1 Lang.audio_any in 224 201 Lang.add_operator "input.harbor" -
trunk/liquidsoap/src/sources/http_source.ml
r6974 r7110 2 2 3 3 Liquidsoap, a programmable audio stream generator. 4 Copyright 2003-20 09Savonet team4 Copyright 2003-2010 Savonet team 5 5 6 6 This program is free software; you can redistribute it and/or modify … … 42 42 ] 43 43 44 type sink = {45 read : int -> string ;46 put : int -> float array array -> unit ;47 insert_metadata : Frame.metadata -> unit ;48 close : unit -> unit49 }50 51 44 (** Types for playlist handling *) 52 45 type playlist_mode = Random | First | Randomize | Normal 53 46 54 55 let stream_decoders : (sink -> unit) Plug.plug = 56 Plug.create ~doc:"Methods for decoding audio streams." "stream formats" 57 58 (** Utilities for reading icy metadata *) 59 47 (** Utility for reading icy metadata *) 60 48 let read_metadata () = let old_chunk = ref "" in fun socket -> 61 49 let size = … … 128 116 let chunkbuf = ref "" in 129 117 let read buf offs len = 130 if chunked then 131 ( 132 if String.length !chunkbuf = 0 then chunkbuf := read_chunk socket; 133 let n = min len (String.length !chunkbuf) in 134 String.blit !chunkbuf 0 buf offs n; 135 chunkbuf := String.sub !chunkbuf n (String.length !chunkbuf - n); 136 n 137 ) 138 else 118 if chunked then begin 119 if String.length !chunkbuf = 0 then chunkbuf := read_chunk socket; 120 let n = min len (String.length !chunkbuf) in 121 String.blit !chunkbuf 0 buf offs n; 122 chunkbuf := String.sub !chunkbuf n (String.length !chunkbuf - n); 123 n 124 end else 139 125 Unix.read socket buf 0 len 140 126 in … … 144 130 let b = String.create len in 145 131 let r = read b 0 len in 146 if r < 0 then "" else String.sub b 0r132 if r < 0 then "",0 else b,r 147 133 | Some metaint -> 148 134 let readcnt = ref 0 in … … 151 137 let b = String.create len in 152 138 let r = read b 0 len in 153 if r < 0 then "" else begin139 if r < 0 then "",0 else begin 154 140 readcnt := !readcnt + r; 155 141 if !readcnt = metaint then begin … … 159 145 | None -> () 160 146 end ; 161 String.sub b 0r147 b,r 162 148 end 163 149 164 (** Generic httpinput *)150 (** HTTP input *) 165 151 166 152 let url_expr = Str.regexp "^http://\\([^/]+\\)\\(/.*\\)?$" … … 202 188 ~debug ?(logfile=None) 203 189 ~user_agent url = 204 let max_len = 190 let _ = 191 (* TODO 192 * We used to deal with overfull generators in the [put] method, 193 * passed to the stream decoders in the [sink]. Now, the 194 * interface is simpler, but we need to put this back somewhere: 195 * add overfull management to Generators? This can be done 196 * for a specific kind of generator, that also needs to be 197 * thread safe: 198 * 199 * The old "sink" system was used to: 200 * - control concurrency on the generator 201 * - log 202 * - conversions (samplerate) 203 * - control (fail to stop feeding on source/output stop) 204 * Most of it was adapted by passing directly a generator to the 205 * decoder, but we still need to handle multi-threading, 206 * by ensuring that read/writing in the generator don't occur 207 * at the same time. *) 205 208 Frame.audio_of_seconds (Pervasives.max max bufferize) 206 209 in … … 225 228 val mutable logf = None 226 229 227 (* Frame content converter *)228 (* TODO: VIDEO !*)229 val converter = Rutils.create_audio ()230 231 230 val mutable connected = false 232 231 val mutable relaying = autostart … … 241 240 if track_on_meta then Generator.add_break generator ; 242 241 243 (* Feed the buffer generator 244 * TODO this is too restrictive, data could be of any [kind] *) 245 method private put sample_freq data = 246 if not relaying then failwith "relaying stopped" ; 247 if poll_should_stop then failwith "source stopped" ; 248 Mutex.lock lock ; 249 (* Drop data when buffer is full. This is the only way 250 * to make it work with switches, when input.http is not 251 * pulled for some time. 252 * It can also be necessary if liquidsoap is slower than the stream. *) 253 if Generator.length generator >= max_len then begin 254 (* TODO Inserting a break in the generator could be a good idea, 255 * for notifying about the gap/drop. 256 * It would require to stop the current #put, since multiple 257 * breaks are not allowed. *) 258 self#log#f 5 "Overfull buffer: dropping data." ; 259 Generator.remove generator (Generator.length generator - max_len) 260 end; 261 let audio_src_rate = float sample_freq in 262 let content,length = 263 converter ~audio_src_rate data 264 in 265 Generator.put_audio generator content 0 (Frame.master_of_audio length) ; 266 Mutex.unlock lock 267 268 method feeding ?(newstream=true) dec socket chunked metaint = 242 method feeding ?(newstream=true) create_decoder socket chunked metaint = 269 243 connected <- true ; 270 let close () = Http.disconnect socket in271 244 let read = read_stream socket chunked metaint self#insert_metadata in 272 245 let read = … … 276 249 let t0 = Unix.gettimeofday () in 277 250 fun len -> 278 let ret : string= read len in251 let ret = read len in 279 252 let time = (Unix.gettimeofday () -. t0) /. 60. in 280 253 Printf.fprintf f "%f %d\n%!" time self#length ; 281 254 ret 282 255 in 283 let sink = 284 { put = self#put ; read = read ; 285 insert_metadata = self#insert_metadata ; close = close } 286 in 256 let Decoder.Decoder decoder = create_decoder read in 287 257 try 288 (* Starting decoding: adding a break here *) 289 Generator.add_break generator ; 290 dec sink 258 while true do 259 if should_fail then failwith "end of track" ; 260 if poll_should_stop then failwith "source stopped" ; 261 decoder generator 262 done 291 263 with 292 264 | e -> … … 431 403 let dec = 432 404 match 433 stream_decoders#get content_type405 Decoder.get_stream_decoder content_type kind 434 406 with 435 407 | Some d -> d … … 491 463 ~descr:"Get the buffer's length, in seconds." 492 464 (fun _ -> Printf.sprintf "%.2f" (Frame.seconds_of_audio self#length)) 493 494 465 495 466 method sleep = poll_should_stop <- true -
trunk/liquidsoap/src/sources/lastfm_input.ml
r6993 r7110 2 2 3 3 Liquidsoap, a programmable audio stream generator. 4 Copyright 2003-20 09Savonet team4 Copyright 2003-2010 Savonet team 5 5 6 6 This program is free software; you can redistribute it and/or modify … … 80 80 session <- None ; 81 81 self#log#f 4 82 "Could not get file from lastfm: %s" (Lastfm.Radio.string_of_error e) 82 "Could not get file from lastfm: %s" 83 (Lastfm.Radio.string_of_error e) 83 84 | e -> self#log#f 4 "Lastfm connection failed: %s" (Printexc.to_string e) 84 85 85 val mutable abort_stream = false 86 (* TODO abort streaming on #abort_track, 87 * setting relaying <- false is too radical, it would completely 88 * stop. *) 86 89 87 method put sample_freq data = 88 if abort_stream then begin 89 abort_stream <- false ; 90 failwith "streaming was aborted" 91 end ; 92 http#put sample_freq data 93 94 method abort_track = 95 abort_stream <- true 96 97 method get ab = 98 http#get ab ; 90 method private get_frame ab = 91 http#get_frame ab ; 99 92 if Frame.is_partial ab then 100 93 begin … … 108 101 | None -> () 109 102 end 103 110 104 end 111 105 … … 115 109 ~kind:(Lang.Unconstrained k) 116 110 ~category:Lang.Input 117 ~descr: ("Forwards the given lastfm stream. The relay can be "^118 "paused/resumed using the start/stop telnet commands.")111 ~descr:"Forwards the given lastfm stream. The relay can be \ 112 paused/resumed using the start/stop telnet commands." 119 113 [ "autostart", Lang.bool_t, Some (Lang.bool true), 120 114 Some "Initially start relaying or not." ; … … 122 116 Some "Duration of the pre-buffered data." ; 123 117 "bind_address", Lang.string_t, Some (Lang.string ""), 124 Some ("address to bind on the local machine. This option can be useful if " ^ 125 "your machine is bound to multiple IPs. \"\" means no bind address.") ; 118 Some "Address to bind on the local machine. \ 119 This option can be useful if your machine is bound to \ 120 multiple IPs. \"\" means no bind address." ; 126 121 "timeout", Lang.float_t, Some (Lang.float 10.), 127 122 Some "Timeout for HTTP connections." ; -
trunk/liquidsoap/src/stream/generator.ml
r7065 r7110 2 2 3 3 Liquidsoap, a programmable audio stream generator. 4 Copyright 2003-20 09Savonet team4 Copyright 2003-2010 Savonet team 5 5 6 6 This program is free software; you can redistribute it and/or modify … … 21 21 *****************************************************************************) 22 22 23 (** The base module doesn't even know what it is buffering. *) 23 module type S = 24 sig 25 type t 26 val length : t -> int (* ticks *) 27 val remaining : t -> int (* ticks *) 28 val clear : t -> unit 29 val fill : t -> Frame.t -> unit 30 val add_metadata : t -> Frame.metadata -> unit 31 end 32 33 (** The base module doesn't even know what kind of data it is buffering. *) 24 34 module Generator = 25 35 struct -
trunk/liquidsoap/src/stream/generator.mli
r6935 r7110 2 2 3 3 Liquidsoap, a programmable audio stream generator. 4 Copyright 2003-20 09Savonet team4 Copyright 2003-2010 Savonet team 5 5 6 6 This program is free software; you can redistribute it and/or modify … … 20 20 21 21 *****************************************************************************) 22 23 module type S = 24 sig 25 type t 26 val length : t -> int (* ticks *) 27 val remaining : t -> int (* ticks *) 28 val clear : t -> unit 29 val fill : t -> Frame.t -> unit 30 val add_metadata : t -> Frame.metadata -> unit 31 end 22 32 23 33 module Generator : -
trunk/liquidsoap/src/tools/harbor.ml
r6994 r7110 2 2 3 3 Liquidsoap, a programmable audio stream generator. 4 Copyright 2003-20 09Savonet team4 Copyright 2003-2010 Savonet team 5 5 6 6 This program is free software; you can redistribute it and/or modify … … 71 71 method virtual is_taken : bool 72 72 method virtual register_decoder : string -> unit 73 method virtual get_ type : string option73 method virtual get_mime_type : string option 74 74 75 75 end … … 267 267 | _ -> 268 268 s#register_decoder stype ; 269 log#f 3 "Adding source on mountpoint '%s' with type '%s'." uri stype ;269 log#f 3 "Adding source on mountpoint %S with type %S." uri stype ; 270 270 if not icy then write_answer ~keep:true c "HTTP/1.0 200 OK\r\n\r\n" ; 271 271 s#relay headers c … … 366 366 "Source is not mp3") 367 367 in 368 if not (List.mem (Utils.get_some s#get_ type)368 if not (List.mem (Utils.get_some s#get_mime_type) 369 369 conf_icy_metadata#get) 370 370 then
