Changeset 7142

Show
Ignore:
Timestamp:
02/05/10 23:11:40 (5 weeks ago)
Author:
metamorph68
Message:
  • Switched ocaml-cry send implementation to Unix.write. This has been reported to generate much less load.
  • Added back the icy_metadata option to output.icecast. Guess works with mp3 and ogg.
  • Added a new operator: icy.update_metadata to manually update metadata on any icecast/shoutcast mountpoint
  • Updated the doc for video.txt and external_encoders.txt
Location:
trunk
Files:
1 added
8 modified

Legend:

Unmodified
Added
Removed
  • trunk/liquidsoap/doc/content/encoding_formats.txt

    r7067 r7142  
    7979h4. External encoders 
    8080 
     81For a detailed presentation of external encoders, see "this page":external_encoders.html. 
     82 
    8183%% 
    8284%external(channels=2,samplerate=44100,header=true, 
  • trunk/liquidsoap/doc/content/external_encoders.txt

    r7137 r7142  
    11title: External encoders 
    2  
    3 **External encoders are currently unavailable in the SVN trunk. This page will be updated when they are available again.** 
    42 
    53h3. Introduction 
     
    108When using an external encoding process, uncompressed PCM data will be sent to the process through its standard input (@stdin@), and encoded data will be read through its standard output (@stdout@). When using a process that does only file input or output, @/dev/stdin@ and @/dev/stdout@ can be used, though this may generate issues if the encoding process expects to be able to go backward/forward in the file. 
    119 
    12 h3. Main operators 
    13  
    14 The main operators are: 
    15  
    16 * @output.file.external@ 
    17 * @output.pipe.external@ 
    18 * @output.icecast.external@ 
    19  
    20 The available options are the same as the usual @output.file@ and @output.icecast@ operators, 
    21 with the following additions: 
     10h3. External encoders 
     11 
     12For a general presentation on encoding formats, see "this page":encoding_formats.html 
     13 
     14The main operators that can be used with external encoders are: 
     15 
     16* @output.file@ 
     17* @output.icecast@ 
     18 
     19In order to use external encoders with these operators, you have to use the @%external@ encoding format. 
     20Its syntax is: 
     21 
     22%% 
     23%external(channels=2,samplerate=44100,header=true, 
     24          restart_on_crash=false, 
     25          restart_on_new_track, 
     26          restart_after_delay=<int>, 
     27          process="") 
     28%% 
     29 
     30The available options are: 
    2231* @process@: this parameter is a function that takes the current metadata and return the process to start. 
    2332* @header@: if set to @false@ then no WAV header will be added to the data fed to the encoding process, thus the encoding process shall operate on RAW data. 
     
    2635* @restart_encoder_delay@: Restart the encoder after some delay. This can be useful for encoders that cannot operate on infinite streams, or are buggy after some time, like the @lame@ binary. The default for @lame@ and @accplusenc@-based encoders is to restart the encoder every hour. 
    2736 
     37Only one of @restart_encoder_delay@ or @restart_on_new_track@ should be used. 
     38 
    2839The restart mechanism strongly relies on the good behaviour of the encoding process. The restart operation will  
    2940close the standard input of the encoding process. The encoding process is then expected to finish its own operations and 
     
    3344process yourself. 
    3445 
    35 Some specific options are available for @output.icecast.external@: 
     46If you use an external encoder with the @output.icecast@ operator, you should also use the following options 
     47of @output.icecast@ 
    3648* @icy_metadata@: send new metadata as ICY update. This is the case for headerless formats, such as MP3 or AAC, and it appears to work also for ogg/vorbis streams. 
    37 * @shout_raw@: ask shout not to do any synchronization. Useful when sending formats that shout does not recognize, like AAC. Since liquidsoap is real-time, data is sent at real-rate anyway. 
    38 * @format@: shout format. Must be one of "ogg" or "mp3". Use "mp3" for sending other headerless audio data, such as AAC. 
     49* @format@: Content-type (mime) of the data sent to icecast. For instance, for ogg data, it is one of 
     50"application/ogg", "audio/ogg" or "video/ogg" and for mp3 data it is "audio/mpeg". 
    3951 
    4052h3. Wrappers 
  • trunk/liquidsoap/doc/content/video.txt

    r7141 r7142  
    11title: Video in Liquidsoap 
    2  
    3 **This page needs to be updated !** 
    42 
    53h3. Video 
     
    97In order to stream video, you should first inform your script that you are going to use video by doing 
    108%% 
    11 set("frame.video.channels",1) 
     9set("frame.video.channels",0) 
    1210%% 
    1311Optionnaly, you can also set the width, the height and the number of frames per seconds of the streamed video with 
     
    1917And that's almost it! You can use usual operators such as <code>single</code> or <code>playlist</code> to read video files. For now, only file in the ogg/theora format are supported (you can use the "ffmpeg2theora":http://v2v.cc/~j/ffmpeg2theora/ program to convert your videos to this format). For instance, a playlist of ogg videos can be streamed using 
    2018%% 
    21 output.icecast.theora(playlist("my_playlist")) 
     19output.icecast(%ogg(%theora,%vorbis), playlist("my_playlist")) 
    2220%% 
    2321where <code>my_playlist</code> is a list of video files. 
    2422 
     23For more details about the encoding settings, you can see the "encoding formats":encoding_formats.html page. 
     24 
    2525If you have SDL support in Liquidsoap, the video part of the stream can be previewed locally using the <code>output.sdl</code> operator. For example, the script 
    2626%% 
    27 output.sdl(single("file.ogv")) 
     27output.sdl(drop_audio(single("file.ogv"))) 
    2828%% 
    2929will display the video file <code>file.ogv</code>. 
     30 
     31We use the @drop_audio@ operator because the @output.sdl@ operator only supports pure video stream.  
     32Hence, @drop_audio@ returns a source where only the video track is present. 
    3033 
    3134h4. Video effects 
     
    3336A lot of video effects are available in Liquidsoap. The corresponding operators are named <code>video.*</code>. In particular, the <code>video.fade.in</code> and <code>video.fade.out</code> should be used to program transitions, similarly to the <code>fade.in</code> and <code>fade.out</code> operators for audio. 
    3437 
    35 h4. Known issues and limitations. 
    36  
    37 Current support for video is not complete. There are several limitations. In particular, liquidsoap cannot do any video  
    38 framerate conversion. This means that all video files must have the same number of frames per second as the global setting. 
    39  
    40 Additionally, in order to maintain audio and video synchronisation, you have to make sure that the duration of 
    41 a liquidsoap frame is a multiple of the duration of a video frame and the duration of an audio frame. 
    42  
    43 For instance, if you want to read videos with 10 frames per seconds, and audio is sampled at 44.100 Hz, then  
    44 if @x@ is the length of a liquidsoap frame -- set with @set("frame.size",x)@: 
    45  
    46 * For now liquidsoap frame duration is exactly the audio duration, then the liquidsoap frame duration is: @x*44.100@. 
    47 * For this duration, there are exactly: @y = floor(x*44.100/10)@ video frames. 
    48 * The duration of the @y@ frames is: @y*10@. 
    49  
    50 Hence, the equation is: @y*10 = x*44.100@ for @x@ and @y@ integers. We can for instance take @y = 1@, such that there  
    51 will be a single video frame per liquidsoap frame, and then @x = 4.410@, and you have to add the following at the begining of your script: 
    52  
    53 %% 
    54 set("frame.video.channels",1)  
    55 set("frame.size",4410)  
    56 set("frame.video.fps",10) 
    57 %% 
    58  
    59  
    60 This limitation will be removed with the next major release. 
    61  
  • trunk/liquidsoap/src/Makefile

    r7133 r7142  
    166166        $(if $(W_MAGIC),lang/lang_magic.ml) \ 
    167167        $(if $(W_LAST),lang/lang_lastfm.ml) \ 
     168        $(if $(W_CRY),lang/lang_cry.ml) \ 
    168169        main.ml 
    169170 
  • trunk/liquidsoap/src/outputs/icecast2.ml

    r7109 r7142  
    3333  } 
    3434 
     35type icy_metadata = Guess | True | False 
     36 
    3537let no_mount = "Use [name] with .ogg extension if relevant" 
    3638let no_name = "Use [mount]" 
     39 
     40let user_agent = 
     41  Printf.sprintf "liquidsoap %s" Configure.version 
     42let user_agent = Lang.product (Lang.string "User-Agent") 
     43                              (Lang.string user_agent) 
    3744 
    3845let proto kind = 
     
    6168    "public", Lang.bool_t, Some (Lang.bool true), None ; 
    6269    ("headers", Lang.metadata_t, 
    63      Some (Lang.list (Lang.product_t Lang.string_t Lang.string_t) []), 
     70     Some (Lang.list (Lang.product_t Lang.string_t Lang.string_t) [user_agent]), 
    6471     Some "Additional headers.") ; 
     72    "icy_metadata", Lang.string_t, Some (Lang.string "guess"), 
     73    Some "Send new metadata using the ICY protocol. One of: \"guess\", \"true\", \"false\""; 
    6574    ("format", Lang.string_t, Some (Lang.string ""), 
    6675     Some "Format, e.g. \"audio/ogg\". When empty, the encoder is used to guess.") ; 
     
    8695                     (v, "Valid values are 'http' (icecast) \ 
    8796                          and 'icy' (shoutcast)")) 
     97  in 
     98 
     99  let icy_metadata =  
     100    let v = List.assoc "icy_metadata" p in 
     101    match Lang.to_string v with 
     102      | "guess" -> Guess 
     103      | "true"  -> True 
     104      | "false" -> False 
     105      | _ ->  
     106            raise (Lang.Invalid_value 
     107                     (v, "Valid values are 'guess', \ 
     108                          'true' or 'false'")) 
    88109  in 
    89110 
     
    172193                                                  please specify one.")) 
    173194  in 
     195   
     196  let icy_metadata =  
     197    let f = Cry.string_of_content_type in 
     198    match format, icy_metadata with 
     199      | _, True -> true 
     200      | _, False -> false 
     201      | x, _ when f x = f Cry.mpeg -> true 
     202      | x, _ when f x = f Cry.ogg_application || 
     203                         f x = f Cry.ogg_audio || 
     204                         f x = f Cry.ogg_video -> false 
     205      | _, Guess ->  
     206           raise (Lang.Invalid_value (List.assoc "icy_metadata" p, 
     207                                                 "Could not guess icy_metadata \ 
     208                                                  for this format, please specify \ 
     209                                                  either 'true' or 'false'.")) 
     210  in 
     211 
    174212 
    175213  let ogg =  
     
    266304 
    267305  method reset_encoder m = 
    268     if ogg then 
    269       (Utils.get_some encoder).Encoder.reset m 
    270     else 
     306    (* Update metadata using ICY if told to.. *) 
     307    if icy_metadata then 
     308     begin 
    271309      let get h k l = 
    272310        try 
     
    311349        match Cry.get_status connection with 
    312350          | Cry.Connected _ -> 
    313               (try Cry.update_metadata connection m ; "" with _ -> "") 
     351              (try Cry.update_metadata connection m with _ -> ()) 
    314352          | Cry.Disconnected -> 
    315353              (* Do nothing if shout connection isn't available *) 
    316               "" 
     354              () 
     355     end ; 
     356    (* Now send the remaining data.. *) 
     357    if ogg then 
     358      (Utils.get_some encoder).Encoder.reset m  
     359    else "" 
     360 
    317361  method send b = 
    318362    match Cry.get_status connection with 
     
    376420      f icecast_info.channels "channels" string_of_int; 
    377421      let user_agent = 
    378         Printf.sprintf "liquidsoap %s" Configure.version 
     422        try 
     423          List.assoc "User-Agent" headers 
     424        with 
     425          | Not_found -> Printf.sprintf "liquidsoap %s" Configure.version 
    379426      in  
    380427      let source =  
  • trunk/ocaml-cry/CHANGES

    r6863 r7142  
     10.2.0 () 
     2===== 
     3* Changed send function to  
     4  use Unix.write instead of Pervasives. 
     5  This has been reported to generate much less load. 
     6* Added manual_update_metadata to  
     7  update metadata on any source with being 
     8  previously connected/streaming on it. 
     9 
    1100.1.1 (2009-10-26) 
    211===== 
  • trunk/ocaml-cry/src/cry.ml

    r6838 r7142  
    309309  Printf.sprintf "SOURCE %s HTTP/1.1\r\n%s\r\n\r\n" 
    310310 
    311 let get_auth source =  
    312   Printf.sprintf "Basic %s" (encode64 (source.user ^ ":" ^ source.password)) 
     311let get_auth user password =  
     312  Printf.sprintf "Basic %s" (encode64 (user ^ ":" ^ password)) 
    313313 
    314314let write_data socket request = 
     
    387387 
    388388let connect_http c socket source =  
    389   let auth = get_auth source in  
     389  let auth = get_auth source.user source.password in  
    390390  try 
    391391    Hashtbl.add source.headers "Authorization" auth; 
     
    501501    "GET /admin.cgi?mode=updinfo&pass=%s%s HTTP/1.0\r\n%s\r\n" 
    502502 
    503 let update_metadata c m =  
    504   let source =  
    505     match c.status with 
    506       | PrivConnected (x,_) -> x  
    507       | _ -> raise (Error Not_connected) 
    508   in 
    509   if not c.icy_cap then 
    510     raise (Error Invalid_usage); 
    511   let socket = create_socket ~ipv6:c.ipv6 ?bind:c.bind () in 
     503let manual_update_metadata  
     504       ~host ~port ~protocol ~user ~password  
     505       ~mount ?headers ?(ipv6=false)  
     506       ?bind m = 
     507  let mount =  
     508    if mount.[0] <> '/' then 
     509      "/" ^ mount 
     510    else 
     511      mount 
     512  in  
     513  let headers =  
     514    match headers with 
     515      | Some x -> x 
     516      | None   -> Hashtbl.create 0 
     517  in 
     518  let socket = create_socket ~ipv6 ?bind () in 
    512519  let close () =  
    513520   try 
     
    517524  in 
    518525  try 
    519     connect_socket socket source.host source.port ; 
     526    connect_socket socket host port ; 
    520527    let user_agent = 
    521528      try 
    522         Hashtbl.find source.headers "User-Agent" 
     529        Hashtbl.find headers "User-Agent" 
    523530      with 
    524531        | Not_found -> "ocaml-cry" 
     
    526533    (** This seems to be needed for shoutcast *) 
    527534    let agent_complement =  
    528       if source.protocol = Icy then 
     535      if protocol = Icy then 
    529536        " (Mozilla compatible)" 
    530537      else 
     
    544551    in 
    545552    let request =  
    546       match source.protocol with 
     553      match protocol with 
    547554        | Http -> 
    548555            let headers =  
    549               Printf.sprintf "Authorization: %s\r\n%s" (get_auth source) user_agent 
     556              Printf.sprintf "Authorization: %s\r\n%s" (get_auth user password) user_agent 
    550557            in 
    551             http_meta_request source.mount meta headers 
    552         | Icy -> icy_meta_request source.password meta user_agent 
     558            http_meta_request mount meta headers 
     559        | Icy -> icy_meta_request password meta user_agent 
    553560    in 
    554561    write_data socket request; 
     
    569576       raise e 
    570577 
     578let update_metadata c m =  
     579  let source =                                                               
     580    match c.status with                                                      
     581      | PrivConnected (x,_) -> x                                             
     582      | _ -> raise (Error Not_connected)  
     583  in 
     584  if not c.icy_cap then                                                      
     585    raise (Error Invalid_usage); 
     586  let user = source.user in 
     587  let port = source.port in 
     588  let password = source.password in 
     589  let headers = Some source.headers in 
     590  let protocol = source.protocol in 
     591  let mount = source.mount in 
     592  let host = source.host in 
     593  let ipv6 = c.ipv6 in 
     594  let bind = c.bind in 
     595  manual_update_metadata  
     596       ~host ~port ~protocol  
     597       ~user ~password 
     598       ~mount ?headers  
     599       ~ipv6 ?bind m 
     600 
    571601let send c x = 
    572602  try 
    573     let socket = get_socket c in  
    574     let out_e = Unix.out_channel_of_descr socket in 
    575     output_string out_e x; 
    576     flush out_e 
     603    let socket = get_socket c in 
     604    let len = String.length x in 
     605    let rec write ofs =  
     606      let rem = len - ofs in 
     607      let ret = Unix.write socket x ofs rem in 
     608      if ret < rem then 
     609        write (ofs+ret) 
     610    in 
     611    write 0  
    577612  with 
    578613    | _ -> raise (Error Write) 
  • trunk/ocaml-cry/src/cry.mli

    r6838 r7142  
    196196val update_metadata : t -> metadata -> unit 
    197197 
     198(** Manually update metadata on any source with 
     199  * being connected or streaming.  
     200  * 
     201  * Use it only if you know what you are doing ! *) 
     202val manual_update_metadata :   
     203           host:string -> 
     204           port:int -> 
     205           protocol:protocol -> 
     206           user:string -> 
     207           password:string -> 
     208           mount:string -> 
     209           ?headers:(string, string) Hashtbl.t -> 
     210           ?ipv6:bool -> ?bind:string -> metadata -> unit 
     211 
    198212(** Send data to a source connection.  
    199213  *