Changeset 7110

Show
Ignore:
Timestamp:
01/27/10 06:19:40 (7 weeks ago)
Author:
dbaelde
Message:

Major change to the decoding infrastructure, really integrating the
new content type system. The main differences are:

  • decoding files and streams is unified
  • as a result decoding and detecting whether a decoder fits a file or a stream are clearly separated
  • the old architecture forced a kind upon a decoder, whereas a decoder should be able to adapt itself to a kind by changing its output type
  • the base code in Decoder checks that file decoders respect the expected content kind, which was already needed with libmad, and should prove valuable when we start playing more complex games there
  • the old coding style proved very error prone, no more records

By the way, I moved all decoding-related stuff to a new directory, and
we should remove the old stuff quickly as we finish this migration.
I have only adapted MP3 and OGG for now, and didn't enable OGG stream
decoding. I have adapted intput.http/harbor/lastfm to use the new
infrastructure -- although their (Lang) types don't reflect it --
but we need a thread-safe generator there for stable use.
Also changed the mime-related API in Configure.

Location:
trunk/liquidsoap
Files:
1 added
1 removed
16 modified
1 copied
2 moved

Legend:

Unmodified
Added
Removed
  • trunk/liquidsoap/configure.ac

    r7102 r7110  
    11751175    w_MAGIC="no (requires ocaml-magic)" 
    11761176    cat >> src/configure.ml <<EOCONF 
    1177 let file_mime s = None 
    1178 let data_mime ?len s = None 
     1177let file_mime = None 
     1178let data_mime = None 
    11791179EOCONF 
    11801180else 
     
    11821182    cat >> src/configure.ml <<EOCONF 
    11831183let 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) 
     1184let file_mime = Some (Magic.file magic_cookie) 
     1185let data_mime = Some (Magic.buffer magic_cookie) 
    11861186EOCONF 
    11871187fi 
  • trunk/liquidsoap/src/Makefile

    r7106 r7110  
    11 
    2 SUBDIRS= tools formats playlists encoder protocols plugins stream lang \ 
     2SUBDIRS= tools decoder playlists encoder protocols plugins stream lang \ 
    33                 ogg_formats video_converters audio_converters \ 
    44                 operators sources conversions outputs io visualization analyze synth 
     
    3232        $(if $(W_GAVL),video_converters/gavl_converter.ml) 
    3333 
     34# TODO move all formats to encoders 
    3435formats = \ 
    35         $(if $(W_OGG),formats/oggformat.ml) \ 
    3636        $(if $(W_VORBIS),formats/vorbisduration.ml) \ 
    3737        $(if $(W_TAGLIB),formats/taglib_plug.ml) \ 
     
    3939        formats/externalformat.ml \ 
    4040        formats/midiformat.ml \ 
    41         $(if $(W_MP3),formats/mp3.ml) 
    4241        # $(if $(W_FAAD),formats/aacformat.ml) \ 
     42 
     43decoders = \ 
     44        $(if $(W_OGG),decoder/ogg_decoder.ml) \ 
     45        $(if $(W_MP3),decoder/mp3.ml) 
    4346 
    4447playlists = \ 
     
    5760        sources/req_queue.ml sources/req_equeue.ml \ 
    5861        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) \ 
    6262        $(if $(W_ALSA),sources/alsa_in.ml) \ 
    6363        $(if $(W_BJACK),sources/bjack_in.ml) \ 
     
    110110        $(if $(W_GRAPHICS),outputs/graphics_out.ml) \ 
    111111        $(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) 
    114113 
    115114io = \ 
     
    157156        lang/lang_types.ml lang/lang_values.ml \ 
    158157        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 \ 
    160159        $(video_converters) $(audio_converters) $(ogg_demuxer) $(protocols) \ 
    161160        $(sources) $(operators) $(conversions) $(outputs) \ 
    162161        $(encoders) $(ogg_muxer) $(io) \ 
    163         $(analyze) $(playlists) $(visualization) $(synth) $(formats) \ 
     162        $(analyze) $(playlists) $(visualization) $(synth) $(decoders) \ 
    164163        shebang.ml \ 
    165164        lang/lang_builtins.ml \ 
     
    227226        tools/harbor.ml $(video_converters) $(audio_converters) \ 
    228227        $(ogg_utils) $(protocols) $(sources) $(operators) $(outputs) $(io) \ 
    229         $(analyze) $(playlists) $(formats) \ 
     228        $(analyze) $(playlists) $(decoders) \ 
    230229        lang/lang_builtins.ml 
    231230libliq_objects=$(libliq_sources:.ml=.$(o)) 
  • trunk/liquidsoap/src/configure.mli

    r6172 r7110  
    2121 
    2222(** Magic mime detection *) 
    23 val file_mime : string -> string option 
    24 val data_mime : ?len:int -> string -> string option 
     23val file_mime : (string -> string) option 
     24val data_mime : (?len:int -> string -> string) option 
    2525 
    2626val requests_table_size : int 
  • trunk/liquidsoap/src/decoder.ml

    r6925 r7110  
    22 
    33  Liquidsoap, a programmable audio stream generator. 
    4   Copyright 2003-2009 Savonet team 
     4  Copyright 2003-2010 Savonet team 
    55 
    66  This program is free software; you can redistribute it and/or modify 
     
    2121 *****************************************************************************) 
    2222 
    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. *) 
    2460 
    2561open Dtools 
     
    2763let log = Log.make ["decoder"] 
    2864 
     65(** A local file is simply identified by its filename. *) 
     66type file = string 
     67 
     68(** A stream is identified by a MIME type. *) 
     69type stream = string 
     70 
     71type 'a decoder = Decoder of ('a -> unit) 
     72 
     73type 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. *) 
     79type stream_decoder = input -> Generator.From_audio_video.t decoder 
     80 
    2981(** 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. *) 
     85type file_decoder = { 
    3286  fill : Frame.t -> int ; (* Return remaining ticks. *) 
    3387  close : unit -> unit ; 
    3488} 
    3589 
    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. *) 
     96let file_decoders : 
     97      (file -> Frame.content_kind -> (unit -> file_decoder) option) 
     98      Plug.plug = 
    3899  Plug.create 
    39     ~doc:"Method to read audio files." ~insensitive:true "audio file formats" 
     100    ~doc:"File decoding methods." ~insensitive:true "file decoding" 
     101 
     102let stream_decoders : 
     103      (stream -> Frame.content_kind -> stream_decoder option) Plug.plug = 
     104  Plug.create 
     105    ~doc:"Stream decoding methods." ~insensitive:true "stream decoding" 
    40106 
    41107let dummy = 
     
    45111    close = (fun _ -> ()) } 
    46112 
    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 = 
     113exception Exit of (string * (unit -> file_decoder)) 
     114 
     115(** Get a valid decoder creator for [filename]. *) 
     116let get_file_decoder filename kind : (unit -> file_decoder) option = 
    55117  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 -> ()) ; 
    65126    log#f 3 "Unable to decode %S!" filename ; 
    66127    None 
    67128  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 
     135exception Exit of stream_decoder 
     136 
     137let 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 
     154module Buffered(Generator:Generator.S) = 
     155struct 
     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 
     217end 
  • trunk/liquidsoap/src/decoder.mli

    r6925 r7110  
    22 
    33  Liquidsoap, a programmable audio stream generator. 
    4   Copyright 2003-2009 Savonet team 
     4  Copyright 2003-2010 Savonet team 
    55 
    66  This program is free software; you can redistribute it and/or modify 
     
    2121 *****************************************************************************) 
    2222 
    23 type decoder = { fill : Frame.t -> int; close : unit -> unit; } 
     23val log : Dtools.Log.t 
    2424 
    25 val formats : (string -> Frame.content_kind -> decoder option) Plug.plug 
     25type file = string 
     26type stream = string 
    2627 
    27 val search_valid : 
    28   string -> Frame.content_kind -> (string * (unit -> decoder)) option 
     28type input = int -> string * int 
     29 
     30type 'a decoder = Decoder of ('a -> unit) 
     31type stream_decoder = input -> Generator.From_audio_video.t decoder 
     32type file_decoder = { fill : Frame.t -> int; close : unit -> unit; } 
     33 
     34val file_decoders : 
     35  (file -> Frame.content_kind -> (unit -> file_decoder) option) 
     36  Plug.plug 
     37val stream_decoders : 
     38  (stream -> Frame.content_kind -> stream_decoder option) Plug.plug 
     39 
     40val get_file_decoder : 
     41  file -> Frame.content_kind -> (unit -> file_decoder) option 
     42val get_stream_decoder : 
     43  file -> Frame.content_kind -> stream_decoder option 
     44 
     45module 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  
    44top_srcdir= ../.. 
    55include $(top_srcdir)/Makefile.rules 
    6  
    7 id3tag_stubs.o: id3tag_stubs.c 
    8         $(CC) $(CFLAGS) -c -g -Wall -I$(shell $(OCAMLC) -where) $< 
    9  
    10 test_id3tag: id3tag_stubs.o id3tag.mli id3tag.ml test_id3tag.ml 
    11         ocamlc id3tag.mli 
    12         ocamlopt -c id3tag.ml 
    13         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  
    22 
    33  Liquidsoap, a programmable audio stream generator. 
    4   Copyright 2003-2009 Savonet team 
     4  Copyright 2003-2010 Savonet team 
    55 
    66  This program is free software; you can redistribute it and/or modify 
     
    2121 *****************************************************************************) 
    2222 
    23 (** Decode and read metadatas of mp3 files. *) 
     23(** Decode and read metadata from mp3 files. *) 
    2424 
    2525open Dtools 
    2626 
    2727module Generator = Generator.From_audio_video 
     28module Buffered = Decoder.Buffered(Generator) 
    2829 
    2930let log = Log.make ["format";"mp3"] 
     31 
     32let 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 
     46let create_file_decoder filename kind = 
     47  let generator = Generator.create Generator.Audio in 
     48    Buffered.file_decoder filename kind create_decoder generator 
    3049 
    3150let conf_mad = 
    3251  Conf.void ~p:(Configure.conf#plug "mad") 
    3352    "Mad (mp3) decoding options." 
    34 let conf_mime = 
    35   Conf.bool ~p:(conf_mad#plug "check_mime") ~d:true 
    36       "Enable magic mime test, if compiled." 
    3753let conf_mime_types = 
    3854  Conf.list ~p:(conf_mad#plug "mime_types") 
     
    4056    ~d:["audio/mpeg";"application/octet-stream";"video/x-unknown"] 
    4157 
    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. *) 
     61let 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 } 
    5873 
    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   } 
     74let () = 
     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 
     105let () = 
     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 
     126let 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) 
    91130 
    92131let duration file = 
    93   if not (check file) then 
    94     raise Not_found; 
     132  if not (check file) then raise Not_found ; 
    95133  let ans = Mad.duration file in 
    96134  match ans with 
     
    98136    | _ -> ans 
    99137 
    100 let () = Decoder.formats#register "MP3" (File_decoder.decode decoder); 
    101          Request.dresolvers#register "MP3" duration 
     138let () = 
     139  Request.dresolvers#register "MP3" duration 
  • trunk/liquidsoap/src/decoder/ogg_decoder.ml

    r7048 r7110  
    22 
    33  Liquidsoap, a programmable audio stream generator. 
    4   Copyright 2003-2009 Savonet team 
     4  Copyright 2003-2010 Savonet team 
    55 
    66  This program is free software; you can redistribute it and/or modify 
     
    2323(** Decode and read ogg files. *) 
    2424 
     25module Generator = Generator.From_audio_video 
     26module Buffered = Decoder.Buffered(Generator) 
     27 
    2528let log = Dtools.Log.make ["format";"ogg"] 
    26  
    27 module Generator = Generator.From_audio_video 
    2829 
    2930exception Channels of int 
     
    109110      end 
    110111 
    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 *) 
     114let 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 
    143129        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 
     159let 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 
     169let 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 
     203let () = 
     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) 
    212212 
    213213exception Metadata of (string*string) list 
    214214 
    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; 
     215let get_tags file = 
    219216  let sync,fd = Ogg.Sync.create_from_file file in 
    220217  let close () = 
  • trunk/liquidsoap/src/lang/lang_builtins.ml

    r7057 r7110  
    157157      Lang.unit) 
    158158 
     159(* TODO restore the following code when Wav and External decoding are back 
     160 
    159161let () = 
    160162  let process_t = 
     
    216218         Externalformat.register_external_metadata_resolver format resolver; 
    217219         Lang.unit) 
     220 *) 
    218221 
    219222let () = 
  • trunk/liquidsoap/src/playlists/playlist_basic.ml

    r6608 r7110  
    22 
    33  Liquidsoap, a programmable audio stream generator. 
    4   Copyright 2003-2009 Savonet team 
     4  Copyright 2003-2010 Savonet team 
    55 
    66  This program is free software; you can redistribute it and/or modify 
     
    2929 
    3030let test_text s =  
    31   match Configure.data_mime s with 
     31  match Configure.data_mime with 
    3232    | 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 
    3940 
    4041(* This parser cannot detect the format !! *) 
  • trunk/liquidsoap/src/request.ml

    r7057 r7110  
    22 
    33  Liquidsoap, a programmable audio stream generator. 
    4   Copyright 2003-2009 Savonet team 
     4  Copyright 2003-2010 Savonet team 
    55 
    66  This program is free software; you can redistribute it and/or modify 
     
    154154 
    155155(** 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 
    157157  * in which case validity implies finding a working decoder, or can be 
    158158  * something arbitrary, like a playlist. 
     
    217217  mutable root_metadata : metadata ; 
    218218  mutable indicators : indicator list list ; 
    219   mutable decoder : (unit -> Decoder.decoder) option ; 
     219  mutable decoder : (unit -> Decoder.file_decoder) option ; 
    220220} 
    221221 
     
    317317      let indicator = peek_indicator t in 
    318318      let name = indicator.string in 
    319         match Decoder.search_valid name kind with 
    320           | Some (format,f) -> 
     319        match Decoder.get_file_decoder name kind with 
     320          | Some f -> 
    321321              t.decoder <- Some f ; 
    322322              mresolvers#iter 
    323323                (fun _ resolver -> 
    324324                   try 
    325                      let ans = resolver ~format name in 
     325                     let ans = resolver name in 
    326326                       List.iter 
    327327                         (fun (k,v) -> 
     
    378378  try 
    379379    dresolvers#iter 
    380       (fun format resolver -> 
     380      (fun name resolver -> 
    381381        try 
    382382         let ans = resolver file in 
  • trunk/liquidsoap/src/request.mli

    r7057 r7110  
    22 
    33  Liquidsoap, a programmable audio stream generator. 
    4   Copyright 2003-2009 Savonet team 
     4  Copyright 2003-2010 Savonet team 
    55 
    66  This program is free software; you can redistribute it and/or modify 
     
    163163(** Return a decoder if the file has been resolved, guaranteed to have 
    164164  * available data to deliver. *) 
    165 val get_decoder : t -> Decoder.decoder option 
     165val get_decoder : t -> Decoder.file_decoder option 
    166166 
    167167(** {1 Plugs} 
     
    173173 
    174174val dresolvers : (string -> float) Plug.plug 
    175 val mresolvers : (format:string -> string -> (string*string) list) Plug.plug 
     175val mresolvers : (string -> (string*string) list) Plug.plug 
    176176val protocols : protocol Plug.plug 
  • trunk/liquidsoap/src/sources/generated.ml

    r7029 r7110  
    22 
    33  Liquidsoap, a programmable audio stream generator. 
    4   Copyright 2003-2009 Savonet team 
     4  Copyright 2003-2010 Savonet team 
    55 
    66  This program is free software; you can redistribute it and/or modify 
     
    2121 *****************************************************************************) 
    2222 
    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) = 
     23module Make (Generator:Generator.S) = 
    3424struct 
    3525 
  • trunk/liquidsoap/src/sources/harbor_input.ml

    r7052 r7110  
    22 
    33  Liquidsoap, a programmable audio stream generator. 
    4   Copyright 2003-2009 Savonet team 
     4  Copyright 2003-2010 Savonet team 
    55 
    66  This program is free software; you can redistribute it and/or modify 
     
    2424open Http_source 
    2525 
    26  
    2726(* {1 Input handling} *) 
    28  
    29 exception NoDecoder 
    3027 
    3128class http_input_server ~kind ~dumpfile ~logfile 
     
    3330                        ~on_connect ~on_disconnect 
    3431                        ~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 *) 
    3634object (self) 
    3735  inherit Source.source kind 
     
    4341  val mutable relaying = false 
    4442  val mutable ns = [] 
    45   val mutable decoder = fun _ -> raise NoDecoder 
    46   val mutable stype = None 
     43  val mutable create_decoder = fun _ -> assert false 
     44  val mutable mime_type = None 
    4745 
    4846  val mutable dump = None 
     
    6563    Generator.add_metadata generator m 
    6664 
    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 = 
    10277    self#log#f 3 "Decoding..." ; 
    103     let close () = () in 
    10478    let t0 = Unix.gettimeofday () in 
    10579    let read len = 
     
    11286              if float n >= Harbor.conf_timeout#get then 
    11387               begin 
    114                 self#log#f 4 "network activity timeout! disconnecting source" ; 
     88                self#log#f 4 "Network activity timeout! Disconnecting source." ; 
    11589                self#disconnect 
    11690               end 
     
    12296      let input = Unix.read socket buf 0 len in 
    12397      if input<=0 then raise End_of_file ; 
    124       let s = String.sub buf 0 input in 
    12598      begin match dump with 
    126         | Some b -> output_string b s 
     99        | Some b -> output_string b (String.sub buf 0 input) 
    127100        | None -> () 
    128101      end ; 
     
    133106        | None -> () 
    134107      end ; 
    135       s 
     108      buf,input 
    136109    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 
    151122 
    152123  method private wake_up _ = 
     
    176147  method private sleep = 
    177148    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 
    178156 
    179157  method relay (headers:(string*string) list) socket = 
     
    220198 
    221199let () = 
    222   (* TODO: handle video too! *) 
    223200  let kind = Lang.kind_type_of_kind_format ~fresh:1 Lang.audio_any in 
    224201    Lang.add_operator "input.harbor" 
  • trunk/liquidsoap/src/sources/http_source.ml

    r6974 r7110  
    22 
    33  Liquidsoap, a programmable audio stream generator. 
    4   Copyright 2003-2009 Savonet team 
     4  Copyright 2003-2010 Savonet team 
    55 
    66  This program is free software; you can redistribute it and/or modify 
     
    4242    ] 
    4343 
    44 type sink = { 
    45   read : int -> string ; 
    46   put : int -> float array array -> unit ; 
    47   insert_metadata : Frame.metadata -> unit ; 
    48   close : unit -> unit 
    49 } 
    50  
    5144(** Types for playlist handling *) 
    5245type playlist_mode =  Random | First | Randomize | Normal 
    5346 
    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 *) 
    6048let read_metadata () = let old_chunk = ref "" in fun socket -> 
    6149  let size = 
     
    128116  let chunkbuf = ref "" in 
    129117  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 
    139125      Unix.read socket buf 0 len 
    140126  in 
     
    144130            let b = String.create len in 
    145131            let r = read b 0 len in 
    146               if r < 0 then "" else String.sub b 0 r 
     132              if r < 0 then "",0 else b,r 
    147133      | Some metaint -> 
    148134          let readcnt = ref 0 in 
     
    151137              let b = String.create len in 
    152138              let r = read b 0 len in 
    153                 if r < 0 then "" else begin 
     139                if r < 0 then "",0 else begin 
    154140                  readcnt := !readcnt + r; 
    155141                  if !readcnt = metaint then begin 
     
    159145                      | None -> () 
    160146                  end ; 
    161                   String.sub b 0 r 
     147                  b,r 
    162148                end 
    163149 
    164 (** Generic http input *) 
     150(** HTTP input *) 
    165151 
    166152let url_expr = Str.regexp "^http://\\([^/]+\\)\\(/.*\\)?$" 
     
    202188        ~debug ?(logfile=None) 
    203189        ~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. *) 
    205208    Frame.audio_of_seconds (Pervasives.max max bufferize) 
    206209  in 
     
    225228  val mutable logf = None 
    226229 
    227   (* Frame content converter *) 
    228   (* TODO: VIDEO !*) 
    229   val converter = Rutils.create_audio () 
    230  
    231230  val mutable connected = false 
    232231  val mutable relaying = autostart 
     
    241240    if track_on_meta then Generator.add_break generator ; 
    242241 
    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 = 
    269243    connected <- true ; 
    270     let close () = Http.disconnect socket in 
    271244    let read = read_stream socket chunked metaint self#insert_metadata in 
    272245    let read = 
     
    276249            let t0 = Unix.gettimeofday () in 
    277250              fun len -> 
    278                 let ret : string = read len in 
     251                let ret = read len in 
    279252                let time = (Unix.gettimeofday () -. t0) /. 60. in 
    280253                  Printf.fprintf f "%f %d\n%!" time self#length ; 
    281254                  ret 
    282255    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 
    287257      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 
    291263      with 
    292264        | e -> 
     
    431403                        let dec = 
    432404                          match 
    433                             stream_decoders#get content_type 
     405                            Decoder.get_stream_decoder content_type kind 
    434406                          with 
    435407                            | Some d -> d 
     
    491463               ~descr:"Get the buffer's length, in seconds." 
    492464       (fun _ -> Printf.sprintf "%.2f" (Frame.seconds_of_audio self#length)) 
    493  
    494465 
    495466  method sleep = poll_should_stop <- true 
  • trunk/liquidsoap/src/sources/lastfm_input.ml

    r6993 r7110  
    22 
    33  Liquidsoap, a programmable audio stream generator. 
    4   Copyright 2003-2009 Savonet team 
     4  Copyright 2003-2010 Savonet team 
    55 
    66  This program is free software; you can redistribute it and/or modify 
     
    8080          session <- None ; 
    8181          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) 
    8384      | e -> self#log#f 4 "Lastfm connection failed: %s" (Printexc.to_string e) 
    8485 
    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. *) 
    8689 
    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 ; 
    9992    if Frame.is_partial ab then 
    10093     begin 
     
    108101        | None -> () 
    109102     end 
     103 
    110104end 
    111105 
     
    115109      ~kind:(Lang.Unconstrained k) 
    116110      ~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." 
    119113      [ "autostart", Lang.bool_t, Some (Lang.bool true), 
    120114        Some "Initially start relaying or not." ; 
     
    122116        Some "Duration of the pre-buffered data." ; 
    123117        "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." ; 
    126121        "timeout", Lang.float_t, Some (Lang.float 10.), 
    127122        Some "Timeout for HTTP connections." ; 
  • trunk/liquidsoap/src/stream/generator.ml

    r7065 r7110  
    22 
    33  Liquidsoap, a programmable audio stream generator. 
    4   Copyright 2003-2009 Savonet team 
     4  Copyright 2003-2010 Savonet team 
    55 
    66  This program is free software; you can redistribute it and/or modify 
     
    2121 *****************************************************************************) 
    2222 
    23 (** The base module doesn't even know what it is buffering. *) 
     23module type S = 
     24sig 
     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 
     31end 
     32 
     33(** The base module doesn't even know what kind of data it is buffering. *) 
    2434module Generator = 
    2535struct 
  • trunk/liquidsoap/src/stream/generator.mli

    r6935 r7110  
    22 
    33  Liquidsoap, a programmable audio stream generator. 
    4   Copyright 2003-2009 Savonet team 
     4  Copyright 2003-2010 Savonet team 
    55 
    66  This program is free software; you can redistribute it and/or modify 
     
    2020 
    2121 *****************************************************************************) 
     22 
     23module type S = 
     24sig 
     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 
     31end 
    2232 
    2333module Generator : 
  • trunk/liquidsoap/src/tools/harbor.ml

    r6994 r7110  
    22 
    33  Liquidsoap, a programmable audio stream generator. 
    4   Copyright 2003-2009 Savonet team 
     4  Copyright 2003-2010 Savonet team 
    55 
    66  This program is free software; you can redistribute it and/or modify 
     
    7171  method virtual is_taken : bool 
    7272  method virtual register_decoder : string -> unit 
    73   method virtual get_type : string option 
     73  method virtual get_mime_type : string option 
    7474 
    7575end 
     
    267267      | _ -> 
    268268          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 ; 
    270270          if not icy then write_answer ~keep:true c "HTTP/1.0 200 OK\r\n\r\n" ; 
    271271          s#relay headers c 
     
    366366                    "Source is not mp3") 
    367367              in 
    368               if not (List.mem (Utils.get_some s#get_type)  
     368              if not (List.mem (Utils.get_some s#get_mime_type)  
    369369                               conf_icy_metadata#get)  
    370370              then