Hi all !
I am preparing some extension of liquidsoap to allow implementating custom
operations using harbor, telnet etc..
The main tool that I need for now is a source.insert_metadata operator in the
scripting functions, so I have started looking at this.
The current insert_metadata returns a new source which has an internal
metadata variable and inserts the content of this variable when it is not None
during the next s#get_frame.
In the spirit of an object-like system, I figured out that we need a
source.insert_metadata that applies to any source. The other alternative would
have been something of the type:
source.insert_metadata: source -> (metadata -> unit) * source
which I find not really attractive..
So, I have backported the method mentioned above to the main source class and
added a method #insert_metadata.
It turned out that this conflicted with insert_metadata present in input.http
and etc... In fact, I think now it generalizes this the following way:
* For non-buffered sources, insert_metadata adds metadata directly into the
frame
* For buffered sources, insert_metadata adds the metadata in the current
buffer.
So, I have moved insert_metadata from the various generated sources into the
main generated class. I also factorized the add_track_on_meta option there --
which needed a little trick on the general Generator type.
Finally, there was also a insert_metadata in encoded sources, with an implicit
filtering. This was added some weeks ago. Those two functions do not
necessarily conflict but, since the name is the same, I think this is good
practice to make then consistent.
Therefore, I have moved the metadata filtering from Encoder to a general
Metautils. We do not always want to filter inserted metadata, so I have added
an option to bypass the filtering. A good default, I think is:
* Metadata that come from the system are not filtered by default. This is the
case on the main source class and for source.insert_metadata
* Metadata that come from the environment are filtered by default. For
instance, input.http, input.harbor, input.lastfm
* Metadata sent to the environement are filtered by default. This applies
only to encoded outputs for now.
I think this gives a nice generalization:
* insert_metadata on sources inserts metadata directly during s#get
* insert_metadata on buffered sources inserts metadata into the buffer
* insert_metadata on encoded outputs inserts metadata in the encoded stream
All 3 functions have the same type and may or may nor filter metadata.
Filtering is configured according to "metadata.export" (list of fields).
Additionally, insert_metadata for buffered sources has been merged into
generated.ml.
I know this is a long mail, but the diff is huge too and I wanted to explain
it..
Attached is the diff. I am, of course, waiting for your input before comiting.
I forgot: this diff removes the original insert_metadata operator. My plan is
to implement it directly in utils.liq -- and to document/implement a similar
operator using harbor and a HTTP GET request -- once those changes have been
discussed..
Romain
Index: outputs/output.mli
===================================================================
--- outputs/output.mli (révision 7979)
+++ outputs/output.mli (copie de travail)
@@ -69,10 +69,14 @@
method virtual output_stop : unit
end
+(** Parameters needed to instantiate an encodedoutput. *)
+val encoded_proto : (string * Lang.t * Lang.value option * string option) list
+
class virtual encoded :
content_kind:Frame.content_kind ->
output_kind:string ->
name:string ->
+ filter:bool ->
infallible:bool ->
on_start:(unit->unit) ->
on_stop:(unit->unit) ->
@@ -84,7 +88,7 @@
method output_send : Frame.t -> unit
method virtual encode : Frame.t -> int -> int -> string
- method virtual insert_metadata : Encoder.Meta.export_metadata -> unit
+ method virtual insert_metadata : Metautils.export_metadata -> unit
method virtual send : string -> unit
method virtual output_reset : unit
Index: outputs/icecast2.ml
===================================================================
--- outputs/icecast2.ml (révision 7979)
+++ outputs/icecast2.ml (copie de travail)
@@ -62,7 +62,7 @@
(Lang.string user_agent)
let proto kind =
- Output.proto @
+ Output.proto @ Output.encoded_proto @
[ ("restart", Lang.bool_t, Some (Lang.bool false),
Some "Restart output after a failure. By default, liquidsoap will stop \
if the output failed.") ;
@@ -289,6 +289,7 @@
mount
in
+ let filter = Lang.to_bool (List.assoc "filter_metadata" p) in
let autostart = Lang.to_bool (List.assoc "start" p) in
let infallible = not (Lang.to_bool (List.assoc "fallible" p)) in
let on_start =
@@ -333,7 +334,7 @@
inherit Output.encoded
~content_kind:kind ~output_kind:"output.icecast"
- ~infallible ~autostart ~on_start ~on_stop
+ ~infallible ~autostart ~on_start ~on_stop ~filter
~name:mount source
(** In this operator, we don't exactly follow the start/stop
@@ -379,7 +380,7 @@
(k,(Hashtbl.find h k))::l
with _ -> (k,d)::l
in
- let m = Encoder.Meta.to_metadata m in
+ let m = Metautils.to_metadata m in
let def_title =
match get m "uri" [] with
| (_,s)::_ -> let title = Filename.basename s in
@@ -464,7 +465,7 @@
assert (encoder = None) ;
let enc = encoder_factory self#id in
encoder <-
- Some (enc (Encoder.Meta.empty_metadata)) ;
+ Some (enc (Metautils.empty_metadata)) ;
assert (Cry.get_status connection = Cry.Disconnected) ;
begin match dumpfile with
| Some f -> dump <- Some (open_out_bin f)
Index: outputs/file_output.ml
===================================================================
--- outputs/file_output.ml (révision 7979)
+++ outputs/file_output.ml (copie de travail)
@@ -22,7 +22,7 @@
class file_output
~reload_delay ~reload_predicate ~reload_on_metadata
- ~append ~perm ~dir_perm filename
+ ~append ~perm ~dir_perm filename ~filter
~infallible ~on_start ~on_stop ~autostart
~encoder_factory ~kind source =
object (self)
@@ -30,7 +30,7 @@
inherit
Output.encoded
~infallible ~on_start ~on_stop ~autostart
- ~output_kind:"to_file" ~name:filename
+ ~output_kind:"to_file" ~name:filename ~filter
~content_kind:kind source
val mutable fd = None
@@ -47,7 +47,7 @@
let filename = Utils.home_unrelate filename in
let current_metadata =
match current_metadata with
- | Some m -> Hashtbl.find (Encoder.Meta.to_metadata m)
+ | Some m -> Hashtbl.find (Metautils.to_metadata m)
| None -> fun _ -> raise Not_found
in
(* Avoid / in metas for filename.. *)
@@ -71,7 +71,7 @@
let meta =
match current_metadata with
| Some m -> m
- | None -> Encoder.Meta.empty_metadata
+ | None -> Metautils.empty_metadata
in
encoder <- Some (enc meta) ;
@@ -123,7 +123,7 @@
let () =
let kind = Lang.univ_t 1 in
Lang.add_operator "output.file"
- (Output.proto @ [
+ (Output.proto @ Output.encoded_proto @ [
"append",
Lang.bool_t,
Some (Lang.bool false),
@@ -206,9 +206,10 @@
in
let filename = Lang.to_string (Lang.assoc "" 2 p) in
let source = Lang.assoc "" 3 p in
+ let filter = Lang.to_bool (List.assoc "filter_metadata" p) in
let kind = Encoder.kind_of_format format in
((new file_output
filename ~append ~perm ~dir_perm
- ~infallible ~on_start ~on_stop
+ ~infallible ~on_start ~on_stop ~filter
~reload_delay ~reload_predicate ~reload_on_metadata
~autostart ~encoder_factory ~kind source):>Source.source))
Index: outputs/output.ml
===================================================================
--- outputs/output.ml (révision 7979)
+++ outputs/output.ml (copie de travail)
@@ -280,18 +280,22 @@
((new dummy ~kind ~on_start ~on_stop ~infallible ~autostart
(List.assoc "" p)):>Source.source))
+let encoded_proto =
+ [ "filter_metadata", Lang.bool_t, Some (Lang.bool true),
+ Some "Filter received metadata." ]
+
(** More concrete abstract-class, which takes care of the #output_send
* method for outputs based on encoders. *)
class virtual encoded
~content_kind ~output_kind ~name
- ~infallible ~on_start ~on_stop
+ ~infallible ~on_start ~on_stop ~filter
~autostart source =
object (self)
inherit output
~infallible ~on_start ~on_stop
~content_kind ~output_kind ~name source autostart
- method virtual insert_metadata : Encoder.Meta.export_metadata -> unit
+ method virtual insert_metadata : Metautils.export_metadata -> unit
method virtual encode : Frame.t -> int -> int -> string
method virtual send : string -> unit
@@ -301,7 +305,7 @@
begin
match Frame.get_metadata frame start with
| None -> ()
- | Some m -> self#insert_metadata (Encoder.Meta.export_metadata m)
+ | Some m -> self#insert_metadata (Metautils.export_metadata ~filter m)
end ;
let data =
self#encode frame start (stop-start)
Index: sources/harbor_input.ml
===================================================================
--- sources/harbor_input.ml (révision 7979)
+++ sources/harbor_input.ml (copie de travail)
@@ -27,8 +27,8 @@
(* {1 Input handling} *)
-class http_input_server ~kind ~dumpfile ~logfile
- ~bufferize ~max ~icy ~port
+class http_input_server ~kind ~dumpfile ~logfile ~track_on_meta
+ ~bufferize ~max ~icy ~port ~filter
~mountpoint ~on_connect ~on_disconnect
~login ~debug p =
let max_ticks = Frame.master_of_seconds max in
@@ -41,7 +41,7 @@
inherit Generated.source
(Generator.create
~log ~kind ~overfull:(`Drop_old max_ticks) `Undefined)
- ~empty_on_abort:false ~bufferize as generated
+ ~empty_on_abort:false ~bufferize ~track_on_meta as generated
val mutable relay_socket = None
val mutable ns = []
@@ -51,22 +51,12 @@
val mutable dump = None
val mutable logf = None
+ method filter = filter
+
method login : string*(string -> string -> bool) = login
method stype = Source.Fallible
- (* Insert metadata *)
- method insert_metadata m =
- (* Metadata may contain only the "song" value
- * or "artist" and "title". Here, we use "song"
- * as the "title" field if "title" is not provided. *)
- if not (Hashtbl.mem m "title") then
- (try Hashtbl.add m "title" (Hashtbl.find m "song") with _ -> ());
- self#log#f 3 "New metadata chunk %s -- %s."
- (try Hashtbl.find m "artist" with _ -> "?")
- (try Hashtbl.find m "title" with _ -> "?") ;
- Generator.add_metadata generator m
-
method get_mime_type = mime_type
method feed socket =
@@ -266,6 +256,9 @@
Some (Lang.bool false),
Some "Enable ICY (shoutcast) protocol.";
+ "filter_metadata", Lang.bool_t, Some (Lang.bool true),
+ Some "Filter received metadata." ;
+
"auth",
Lang.fun_t [false,"",Lang.string_t;false,"",Lang.string_t] Lang.bool_t,
Some
@@ -284,6 +277,9 @@
Some "Log buffer status to file, for debugging purpose. \
Disabled if empty.";
+ "new_track_on_metadata", Lang.bool_t, Some (Lang.bool true),
+ Some "Treat new metadata as new track." ;
+
"debug", Lang.bool_t, Some (Lang.bool false),
Some "Run in debugging mode by not catching some exceptions.";
@@ -306,6 +302,7 @@
let password = Lang.to_string (List.assoc "password" p) in
let debug = Lang.to_bool (List.assoc "debug" p) in
let icy = Lang.to_bool (List.assoc "icy" p) in
+ let filter = Lang.to_bool (List.assoc "filter_metadata" p) in
let port = Lang.to_int (List.assoc "port" p) in
let auth_function = List.assoc "auth" p in
let login user pass =
@@ -352,6 +349,9 @@
in
let bufferize = Lang.to_float (List.assoc "buffer" p) in
let max = Lang.to_float (List.assoc "max" p) in
+ let track_on_meta =
+ Lang.to_bool (List.assoc "new_track_on_metadata" p)
+ in
if bufferize >= max then
raise (Lang.Invalid_value
(List.assoc "max" p,
@@ -372,8 +372,8 @@
ignore
(Lang.apply ~t:Lang.unit_t (List.assoc "on_disconnect" p) [])
in
- (new http_input_server ~kind
+ (new http_input_server ~kind ~track_on_meta
~bufferize ~max ~login ~mountpoint
- ~dumpfile ~logfile ~icy ~port
+ ~dumpfile ~logfile ~icy ~port ~filter
~on_connect ~on_disconnect ~debug
p :> Source.source))
Index: sources/generated.ml
===================================================================
--- sources/generated.ml (révision 7979)
+++ sources/generated.ml (copie de travail)
@@ -26,7 +26,7 @@
(* Reads data from an audio buffer generator.
* A thread safe generator should be used if it has to be fed concurrently.
* Store [bufferize] seconds before declaring itself as ready. *)
-class virtual source ~bufferize ~empty_on_abort gen =
+class virtual source ~bufferize ~empty_on_abort ~track_on_meta gen =
let bufferize = Frame.master_of_seconds bufferize in
object (self)
@@ -66,6 +66,20 @@
let l = Generator.remaining generator in
if buffering && r <= bufferize then 0 else l
+ (* Insert metadata *)
+ method insert_metadata m =
+ let m = Metautils.to_metadata m in
+ (* Metadata may contain only the "song" value
+ * or "artist" and "title". Here, we use "song"
+ * as the "title" field if "title" is not provided. *)
+ if not (Hashtbl.mem m "title") then
+ (try Hashtbl.add m "title" (Hashtbl.find m "song") with _ -> ());
+ self#log#f 3 "New metadata chunk %s -- %s."
+ (try Hashtbl.find m "artist" with _ -> "?")
+ (try Hashtbl.find m "title" with _ -> "?") ;
+ Generator.add_metadata generator m ;
+ if track_on_meta then Generator.add_break ~sync:`Ignore generator
+
method private get_frame ab =
buffering <- false ;
if should_fail then begin
@@ -97,7 +111,7 @@
class consumer ~kind generator =
object
inherit Source.source ~name:"buffer" kind
- inherit source generator ~bufferize:0. ~empty_on_abort:true
+ inherit source generator ~bufferize:0. ~empty_on_abort:true ~track_on_meta:false
method stype = Source.Fallible
end
Index: sources/http_source.ml
===================================================================
--- sources/http_source.ml (révision 7979)
+++ sources/http_source.ml (copie de travail)
@@ -123,7 +123,8 @@
if !readcnt = metaint then begin
readcnt := 0;
match read_metadata socket with
- | Some m -> insert_metadata m
+ | Some m ->
+ insert_metadata m
| None -> ()
end ;
b,r
@@ -167,7 +168,7 @@
class http ~kind
~playlist_mode ~poll_delay ~timeout ~track_on_meta ?(force_mime=None)
~bind_address ~autostart ~bufferize ~max
- ~debug ?(logfile=None)
+ ~debug ?(logfile=None) ~filter
~user_agent url =
let max_ticks = Frame.master_of_seconds (Pervasives.max max bufferize) in
(* We need a temporary log until
@@ -179,7 +180,7 @@
inherit
Generated.source
(Generator.create ~log ~kind ~overfull:(`Drop_old max_ticks) `Undefined)
- ~empty_on_abort:false ~bufferize as generated
+ ~empty_on_abort:false ~bufferize ~track_on_meta as generated
method stype = Source.Fallible
@@ -198,17 +199,14 @@
val mutable relaying = autostart
val mutable playlist_mode = playlist_mode
- (* Insert metadata *)
- method insert_metadata m =
- self#log#f 3 "New metadata chunk: %s -- %s."
- (try Hashtbl.find m "artist" with _ -> "?")
- (try Hashtbl.find m "title" with _ -> "?") ;
- Generator.add_metadata generator m ;
- if track_on_meta then Generator.add_break ~sync:`Ignore generator
-
method feeding ?(newstream=true) create_decoder socket chunked metaint =
connected <- true ;
- let read = read_stream socket chunked metaint self#insert_metadata in
+ let insert_metadata m =
+ self#insert_metadata (Metautils.export_metadata ~filter m)
+ in
+ let read =
+ read_stream socket chunked metaint insert_metadata
+ in
let read =
match logf with
| None -> read
@@ -323,7 +321,7 @@
if not poll_should_stop then
let metas = Hashtbl.create 2 in
List.iter (fun (a,b) -> Hashtbl.add metas a b) m;
- self#insert_metadata metas;
+ self#insert_metadata (Metautils.export_metadata ~filter metas);
self#private_connect uri
in
let randomize playlist =
@@ -461,6 +459,9 @@
"timeout", Lang.float_t, Some (Lang.float 10.),
Some "Timeout for http connection." ;
+ "filter_metadata", Lang.bool_t, Some (Lang.bool true),
+ Some "Filter received metadata." ;
+
"new_track_on_metadata", Lang.bool_t, Some (Lang.bool true),
Some "Treat new metadata as new track." ;
@@ -512,6 +513,7 @@
in
let url = Lang.to_string (List.assoc "" p) in
let autostart = Lang.to_bool (List.assoc "autostart" p) in
+ let filter = Lang.to_bool (List.assoc "filter_metadata" p) in
let bind_address = Lang.to_string (List.assoc "bind_address" p) in
let user_agent = Lang.to_string (List.assoc "user_agent" p) in
let track_on_meta =
@@ -542,6 +544,6 @@
"Maximum buffering inferior to pre-buffered data"));
let poll_delay = Lang.to_float (List.assoc "poll_delay" p) in
((new http ~kind ~playlist_mode ~timeout ~autostart ~track_on_meta
- ~force_mime ~bind_address ~poll_delay
+ ~force_mime ~bind_address ~poll_delay ~filter
~bufferize ~max ~debug ~logfile ~user_agent url)
:> Source.source))
Index: sources/external_input.ml
===================================================================
--- sources/external_input.ml (révision 7979)
+++ sources/external_input.ml (copie de travail)
@@ -28,7 +28,7 @@
exception Finished of string*bool
class external_input ~kind ~restart ~bufferize ~channels
- ~restart_on_error ~max
+ ~restart_on_error ~max ~track_on_meta
~samplerate command =
let abg_max_len = Frame.audio_of_seconds max in
let in_freq = float samplerate in
@@ -44,7 +44,8 @@
let priority = Tutils.Non_blocking in
object (self)
inherit Source.source kind
- inherit Generated.source abg ~empty_on_abort:false ~bufferize
+ inherit Generated.source abg ~empty_on_abort:false
+ ~bufferize ~track_on_meta
val mutable should_stop = false
@@ -155,6 +156,9 @@
"restart_on_error", Lang.bool_t, Some (Lang.bool false),
Some "Restart process when exited with error.";
+ "new_track_on_metadata", Lang.bool_t, Some (Lang.bool true),
+ Some "Treat new metadata as new track." ;
+
"", Lang.string_t, None,
Some "Command to execute." ]
~kind:Lang.audio_any
@@ -172,7 +176,10 @@
let restart_on_error =
Lang.to_bool (List.assoc "restart_on_error" p)
in
+ let track_on_meta =
+ Lang.to_bool (List.assoc "new_track_on_metadata" p)
+ in
let max = Lang.to_float (List.assoc "max" p) in
((new external_input ~kind ~restart ~bufferize ~channels
- ~restart_on_error ~max
+ ~restart_on_error ~max ~track_on_meta
~samplerate command):>Source.source))
Index: sources/lastfm_input.ml
===================================================================
--- sources/lastfm_input.ml (révision 7979)
+++ sources/lastfm_input.ml (copie de travail)
@@ -24,13 +24,13 @@
class lastfm ~kind ~autostart ~poll_delay ~track_on_meta
~bufferize ~timeout ~bind_address ~user ~password
- ~debug ~max ~user_agent uri =
+ ~debug ~max ~user_agent ~filter uri =
let playlist_mode = Http_source.First in
let bufferize_time = Frame.master_of_seconds bufferize in
object (self)
inherit Http_source.http ~kind ~playlist_mode ~poll_delay ~timeout
~autostart ~bind_address ~bufferize
- ~max ~track_on_meta
+ ~max ~track_on_meta ~filter
~debug ~user_agent uri as http
val mutable session = None
@@ -87,7 +87,7 @@
in
let metas = Hashtbl.create 2 in
List.iter (fun (a,b) -> Hashtbl.add metas a b) m;
- http#insert_metadata metas;
+ http#insert_metadata (Metautils.export_metadata ~filter metas);
http#connect uri
with
| Liqfm.Radio.Error e ->
@@ -145,6 +145,8 @@
Some "Polling delay." ;
"new_track_on_metadata", Lang.bool_t, Some (Lang.bool true),
Some "Treat new metadata as new track." ;
+ "filter_metadata", Lang.bool_t, Some (Lang.bool true),
+ Some "Filter received metadata." ;
"debug", Lang.bool_t, Some (Lang.bool false),
Some "Run in debugging mode by not catching some exceptions." ;
"max", Lang.float_t, Some (Lang.float 25.),
@@ -177,12 +179,13 @@
let timeout = Lang.to_float (List.assoc "timeout" p) in
let poll_delay = Lang.to_float (List.assoc "poll_delay" p) in
let max = Lang.to_float (List.assoc "max" p) in
+ let filter = Lang.to_bool (List.assoc "filter_metadata" p) in
let user = Lang.to_string (List.assoc "user" p) in
let password = Lang.to_string (List.assoc "password" p) in
if bufferize > max then
raise (Lang.Invalid_value
(List.assoc "max" p,
"Maximun buffering inferior to pre-buffered data"));
- ((new lastfm ~kind ~autostart ~poll_delay ~bufferize
+ ((new lastfm ~kind ~autostart ~poll_delay ~bufferize ~filter
~track_on_meta ~bind_address ~timeout ~max ~debug
~user_agent ~user ~password uri):>Source.source))
Index: stream/generator.mli
===================================================================
--- stream/generator.mli (révision 7979)
+++ stream/generator.mli (copie de travail)
@@ -28,6 +28,7 @@
val clear : t -> unit
val fill : t -> Frame.t -> unit
val add_metadata : t -> Frame.metadata -> unit
+ val add_break : ?sync:[`Strict|`Ignore|`Drop] -> t -> unit
end
module Generator :
@@ -51,7 +52,8 @@
val length : t -> int
val remaining : t -> int
val add_metadata : t -> Frame.metadata -> unit
- val add_break : t -> unit
+ (* Sync argument is ignored here. *)
+ val add_break : ?sync:[`Strict|`Ignore|`Drop] -> t -> unit
val remove : t -> int -> unit
val feed : t ->
?copy:bool ->
Index: stream/generator.ml
===================================================================
--- stream/generator.ml (révision 7979)
+++ stream/generator.ml (copie de travail)
@@ -28,6 +28,7 @@
val clear : t -> unit
val fill : t -> Frame.t -> unit
val add_metadata : t -> Frame.metadata -> unit
+ val add_break : ?sync:[`Strict|`Ignore|`Drop] -> t -> unit
end
module type S_Asio =
@@ -194,7 +195,7 @@
let add_metadata fg m =
fg.metadata <- fg.metadata @ [length fg, m]
- let add_break fg =
+ let add_break ?sync fg =
fg.breaks <- fg.breaks @ [length fg]
(* Advance metadata and breaks by [len] ticks. *)
Index: tools/harbor.ml
===================================================================
--- tools/harbor.ml (révision 7985)
+++ tools/harbor.ml (copie de travail)
@@ -59,7 +59,8 @@
inherit Source.source kind
method virtual relay : (string*string) list -> Unix.file_descr -> unit
- method virtual insert_metadata : (string, string) Hashtbl.t -> unit
+ method virtual filter : bool
+ method virtual insert_metadata : Metautils.export_metadata -> unit
method virtual login : (string*(string -> string -> bool))
method virtual is_taken : bool
method virtual register_decoder : string -> unit
@@ -378,7 +379,8 @@
let args =
Hashtbl.fold f args (Hashtbl.create (Hashtbl.length args))
in
- s#insert_metadata args ;
+ let filter = s#filter in
+ s#insert_metadata (Metautils.export_metadata ~filter args) ;
raise (Answer (fun () -> write_answer c ans))
| _ -> raise (Answer ans_500)
in
Index: tools/metautils.mli
===================================================================
--- tools/metautils.mli (révision 0)
+++ tools/metautils.mli (révision 0)
@@ -0,0 +1,31 @@
+(*****************************************************************************
+
+ Liquidsoap, a programmable audio stream generator.
+ Copyright 2003-2010 Savonet team
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details, fully stated in the COPYING
+ file at the root of the liquidsoap distribution.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ *****************************************************************************)
+
+(* I would like to use a private
+ * type here but its only for ocaml >= 3.11.. *)
+type export_metadata
+
+val export_metadata : filter:bool -> Frame.metadata -> export_metadata
+
+val to_metadata : export_metadata -> Frame.metadata
+
+val empty_metadata : export_metadata
Index: tools/metautils.ml
===================================================================
--- tools/metautils.ml (révision 0)
+++ tools/metautils.ml (révision 0)
@@ -0,0 +1,47 @@
+(*****************************************************************************
+
+ Liquidsoap, a programmable audio stream generator.
+ Copyright 2003-2010 Savonet team
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details, fully stated in the COPYING
+ file at the root of the liquidsoap distribution.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ *****************************************************************************)
+
+
+let conf_meta =
+ Dtools.Conf.void ~p:(Configure.conf#plug "metadata") "Metadata settings"
+ ~comments:["Settings for the encoded metadata."]
+
+(** The list of metadata that labels
+ * are exported when encoding data. *)
+let conf_export_metadata =
+ Dtools.Conf.list ~p:(conf_meta#plug "export") "Exported metdata"
+ ~d:["artist";"title";"album";"genre";"date";"tracknumber";
+ "comment"]
+ ~comments:["The list of labels of exported metadata."]
+
+type export_metadata = Frame.metadata
+
+let export_metadata ~filter m =
+ let ret = Hashtbl.create 10 in
+ let l = conf_export_metadata#get in
+ Hashtbl.iter (fun x y -> if not filter || List.mem (String.lowercase x) l then
+ Hashtbl.add ret x y) m;
+ ret
+
+let to_metadata m = m
+
+let empty_metadata = Hashtbl.create 0
Index: ogg_formats/ogg_flac_encoder.ml
===================================================================
--- ogg_formats/ogg_flac_encoder.ml (révision 7979)
+++ ogg_formats/ogg_flac_encoder.ml (copie de travail)
@@ -106,7 +106,7 @@
| Encoder.Ogg.Flac flac ->
let reset ogg_enc m =
let comments =
- Utils.list_of_metadata (Encoder.Meta.to_metadata m)
+ Utils.list_of_metadata (Metautils.to_metadata m)
in
let enc =
create_encoder ~flac ~comments ()
Index: ogg_formats/dirac_encoder.ml
===================================================================
--- ogg_formats/dirac_encoder.ml (révision 7979)
+++ ogg_formats/dirac_encoder.ml (copie de travail)
@@ -144,7 +144,7 @@
| Encoder.Ogg.Dirac dirac ->
let reset ogg_enc m =
let metadata =
- Utils.list_of_metadata (Encoder.Meta.to_metadata m)
+ Utils.list_of_metadata (Metautils.to_metadata m)
in
let enc =
create_encoder ~metadata dirac
Index: ogg_formats/theora_encoder.ml
===================================================================
--- ogg_formats/theora_encoder.ml (révision 7979)
+++ ogg_formats/theora_encoder.ml (copie de travail)
@@ -164,7 +164,7 @@
| Encoder.Ogg.Theora theora ->
let reset ogg_enc m =
let metadata =
- Utils.list_of_metadata (Encoder.Meta.to_metadata m)
+ Utils.list_of_metadata (Metautils.to_metadata m)
in
let enc =
create_encoder ~theora ~metadata ()
Index: ogg_formats/speex_encoder.ml
===================================================================
--- ogg_formats/speex_encoder.ml (révision 7979)
+++ ogg_formats/speex_encoder.ml (copie de travail)
@@ -167,7 +167,7 @@
| Encoder.Ogg.Speex speex ->
let reset ogg_enc m =
let m =
- Utils.list_of_metadata (Encoder.Meta.to_metadata m)
+ Utils.list_of_metadata (Metautils.to_metadata m)
in
let title =
try
Index: ogg_formats/vorbis_encoder.ml
===================================================================
--- ogg_formats/vorbis_encoder.ml (révision 7979)
+++ ogg_formats/vorbis_encoder.ml (copie de travail)
@@ -105,7 +105,7 @@
let channels = vorbis.Encoder.Vorbis.channels in
let samplerate = vorbis.Encoder.Vorbis.samplerate in
let reset ogg_enc m =
- let m = (Encoder.Meta.to_metadata m) in
+ let m = (Metautils.to_metadata m) in
let get h k =
try
Some (Hashtbl.find h k)
Index: Makefile
===================================================================
--- Makefile (révision 7991)
+++ Makefile (copie de travail)
@@ -75,7 +75,7 @@
tools/liqMM.ml
operators = \
- operators/insert_metadata.ml operators/map_metadata.ml \
+ operators/map_metadata.ml \
operators/on_metadata.ml operators/store_metadata.ml \
operators/on_track.ml operators/on_end.ml operators/delay.ml \
operators/switch.ml operators/fade.ml operators/add.ml \
@@ -131,9 +131,9 @@
# io/marshal_io.ml
tools = tools/doc.ml tools/plug.ml tools/utils.ml \
- tools/rqueue.ml \
- tools/liq_sockets.ml \
- tools/wav.ml $(if $(BYTE),tools/dynliq.ml) configure.ml \
+ tools/rqueue.ml configure.ml \
+ tools/liq_sockets.ml tools/metautils.ml \
+ tools/wav.ml $(if $(BYTE),tools/dynliq.ml) \
tools/tutils.ml tools/http.ml tools/pool.ml
stream =stream/frame.ml stream/generator.ml \
Index: operators/switch.ml
===================================================================
--- operators/switch.ml (révision 7979)
+++ operators/switch.ml (copie de travail)
@@ -45,6 +45,29 @@
* or only at track limits (sensitive). *)
type track_mode = Sensitive | Insensitive
+(** A source that inserts the given metadata
+ * in first returned frame. *)
+class replay ~kind meta src =
+object (self)
+ inherit operator kind [src]
+
+ val mutable first = true
+
+ method stype = src#stype
+ method is_ready = src#is_ready
+ method abort_track = src#abort_track
+ method remaining = src#remaining
+
+ method private get_frame ab =
+ let start = Frame.position ab in
+ src#get ab ;
+ if first then begin
+ if Frame.get_metadata ab start = None then
+ Frame.set_metadata ab start meta ;
+ first <- false
+ end
+end
+
class virtual switch ~kind ~name
?(mode=Sensitive) ?(replay_meta=true) (cases : child list) =
object (self)
@@ -159,7 +182,7 @@
* transition in between. *)
match c.cur_meta with
| Some m when replay_meta ->
- new Insert_metadata.replay ~kind m c.source
+ new replay ~kind m c.source
| _ -> c.source
in
let s =
Index: operators/insert_metadata.ml
===================================================================
--- operators/insert_metadata.ml (révision 7979)
+++ operators/insert_metadata.ml (copie de travail)
@@ -1,126 +0,0 @@
-(*****************************************************************************
-
- Liquidsoap, a programmable audio stream generator.
- Copyright 2003-2010 Savonet team
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details, fully stated in the COPYING
- file at the root of the liquidsoap distribution.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
- *****************************************************************************)
-
-open Source
-open Genlex
-exception Error
-
-class insert_metadata ~kind source =
-object (self)
- inherit operator kind [source] as super
-
- method stype = source#stype
- method is_ready = source#is_ready
- method remaining = source#remaining
- method abort_track = source#abort_track
-
- val mutable metadata = None
- val mutable ns = []
-
- method private wake_up l =
- super#wake_up l ;
- if ns = [] then
- ns <- Server.register [self#id] "insert_metadata" ;
- self#set_id (Server.to_string ns) ;
- Server.add ~ns "insert" ~usage:"insert key1=\"val1\",key2=\"val2\",.."
- ~descr:"Insert a metadata chunk."
- (fun s ->
- let l = String.length s in
- let pos = ref 0 in
- let str =
- Stream.from (fun i ->
- pos := i ;
- if i<l then Some s.[i] else None)
- in
- let lexer = make_lexer [",";"="] str in
- let m = Hashtbl.create 10 in
- let state = ref `Ident in
- try
- while true do
- match Stream.next lexer with
- | Kwd "," when `Comma = !state ->
- state := `Ident
- | Ident key when `Ident = !state ->
- state := `Internal ;
- begin match Stream.next lexer with
- | Kwd "=" -> begin match Stream.next lexer with
- | String s ->
- Hashtbl.add m key s ;
- state := `Comma
- | _ -> raise Error
- end
- | _ -> raise Error
- end
- | _ -> raise Error
- done ;
- assert false
- with
- | Stream.Failure when `Indent = !state || `Comma = !state ->
- metadata <- Some m ;
- "Done"
- | Error | Stream.Failure | Stream.Error _ ->
- "Syntax error: use key1=\"val1\",key2=\"val2\",..")
-
- method private get_frame buf =
- let p = Frame.position buf in
- source#get buf ;
- match metadata with
- | Some m ->
- Frame.set_metadata buf p m ;
- metadata <- None
- | None -> ()
-
-end
-
-let register =
- let kind = Lang.univ_t 1 in
- Lang.add_operator "insert_metadata" [ "", Lang.source_t kind, None, None ]
- ~category:Lang.SoundProcessing
- ~descr:"Interactively insert metadata using the command \
- <code>ID.insert key1=\"val1\",key2=\"val2\",...</code>."
- ~kind:(Lang.Unconstrained kind)
- (fun p kind ->
- let source = Lang.to_source (Lang.assoc "" 1 p) in
- new insert_metadata ~kind source)
-
-(** Insert metadata at the beginning if none is set.
- * Currently used by the switch classes. *)
-class replay ~kind meta src =
-object (self)
- inherit operator kind [src]
-
- val mutable first = true
-
- method stype = src#stype
- method is_ready = src#is_ready
- method abort_track = src#abort_track
- method remaining = src#remaining
-
- method private get_frame ab =
- let start = Frame.position ab in
- src#get ab ;
- if first then begin
- if Frame.get_metadata ab start = None then
- Frame.set_metadata ab start meta ;
- first <- false
- end
-end
Index: encoder/ogg_encoder.ml
===================================================================
--- encoder/ogg_encoder.ml (révision 7979)
+++ encoder/ogg_encoder.ml (copie de travail)
@@ -27,7 +27,7 @@
encode : Ogg_muxer.t -> nativeint ->
Frame.content -> int -> int -> unit ;
reset : Ogg_muxer.t ->
- Encoder.Meta.export_metadata -> nativeint ;
+ Metautils.export_metadata -> nativeint ;
mutable id : nativeint option
}
Index: encoder/encoder.ml
===================================================================
--- encoder/encoder.ml (révision 7979)
+++ encoder/encoder.ml (copie de travail)
@@ -20,46 +20,6 @@
*****************************************************************************)
-let conf =
- Dtools.Conf.void ~p:(Configure.conf#plug "encoder") "Encoder settings"
- ~comments:["Settings for the encoder"]
-
-
-let conf_meta =
- Dtools.Conf.void ~p:(conf#plug "encoder") "Metadata settings"
- ~comments:["Settings for the encoded metadata."]
-
-(** The list of metadata that labels
- * are exported when encoding data. *)
-let conf_export_metadata =
- Dtools.Conf.list ~p:(conf_meta#plug "export") "Exported metdata"
- ~d:["artist";"title";"album";"genre";"date";"tracknumber";
- "comment"]
- ~comments:["The list of labels of exported metadata."]
-
-(* This is because I am too lazy to
- * write encoder.mli.. *)
-module Meta :
- sig
- (* I would like to use a private
- * type here but its only for ocaml >= 3.11.. *)
- type export_metadata
- val export_metadata : Frame.metadata -> export_metadata
- val to_metadata : export_metadata -> Frame.metadata
- val empty_metadata : export_metadata
- end =
- struct
- type export_metadata = Frame.metadata
- let export_metadata m =
- let ret = Hashtbl.create 10 in
- let l = conf_export_metadata#get in
- Hashtbl.iter (fun x y -> if List.mem (String.lowercase x) l then
- Hashtbl.add ret x y) m;
- ret
- let to_metadata m = m
- let empty_metadata = Hashtbl.create 0
- end
-
let string_of_stereo s =
if s then "stereo" else "mono"
@@ -414,12 +374,12 @@
* its stream. This is ok, though, because the ogg container/streams
* is meant to be sequentialized but not the mp3 format. *)
type encoder = {
- insert_metadata : Meta.export_metadata -> unit ;
+ insert_metadata : Metautils.export_metadata -> unit ;
encode : Frame.t -> int -> int -> string ;
stop : unit -> string
}
-type factory = string -> Meta.export_metadata -> encoder
+type factory = string -> Metautils.export_metadata -> encoder
(** A plugin might or might not accept a given format.
* If it accepts it, it gives a function creating suitable encoders. *)
Index: encoder/flac_encoder.ml
===================================================================
--- encoder/flac_encoder.ml (révision 7979)
+++ encoder/flac_encoder.ml (copie de travail)
@@ -24,7 +24,7 @@
let encoder flac meta =
let comments =
- Utils.list_of_metadata (Encoder.Meta.to_metadata meta)
+ Utils.list_of_metadata (Metautils.to_metadata meta)
in
let channels = flac.Encoder.Flac.channels in
let samplerate_converter =
Index: source.mli
===================================================================
--- source.mli (révision 7979)
+++ source.mli (copie de travail)
@@ -70,6 +70,9 @@
(** What kind of content does this source produce. *)
method kind : Frame.content_kind
+ (** Insert a metadata block in the current stream. *)
+ method insert_metadata : Metautils.export_metadata -> unit
+
(** Number of frames left in the current track. Defaults to -1=infinity. *)
method virtual remaining : int
Index: lang/lang_builtins.ml
===================================================================
--- lang/lang_builtins.ml (révision 7985)
+++ lang/lang_builtins.ml (copie de travail)
@@ -256,6 +256,17 @@
Lang.unit)
end
+let () =
+ add_builtin "metadata.export" ~cat:Liq
+ ~descr:"Filter-out internal metadata."
+ ["",Lang.metadata_t,None,None] Lang.metadata_t
+ (fun p ->
+ Lang.metadata
+ (Metautils.to_metadata
+ (Metautils.export_metadata ~filter:true
+ (Lang.to_metadata
+ (List.assoc "" p)))))
+
let () =
let resolver_t =
Lang.fun_t
@@ -1285,6 +1296,22 @@
Lang.unit)
let () =
+ add_builtin "source.insert_metadata" ~cat:Liq ~descr:"Insert metadata into a stream."
+ [ "filter", Lang.bool_t, Some (Lang.bool false),
+ Some "Filter metadata before inserting.";
+ "",Lang.metadata_t,None,None;
+ "",Lang.source_t (Lang.univ_t 1),None,None] Lang.unit_t
+ (fun p ->
+ let filter = Lang.to_bool (List.assoc "filter" p) in
+ let m =
+ Metautils.export_metadata ~filter
+ (Lang.to_metadata
+ (Lang.assoc "" 1 p))
+ in
+ (Lang.to_source (Lang.assoc "" 2 p))#insert_metadata m;
+ Lang.unit)
+
+let () =
add_builtin "request.create.raw" ~cat:Liq
~descr:"Create a raw request, i.e. for files that should not be decoded \
for streaming. Creation may fail if there is no available RID, \
Index: source.ml
===================================================================
--- source.ml (révision 7979)
+++ source.ml (copie de travail)
@@ -425,6 +425,13 @@
(* In caching mode, remember what has been given during the current tick *)
val memo = Frame.create content_kind
+ (* Metadata used to dynamically insert metadata. *)
+ val mutable metadata = None
+
+ (* Insert metadata block in current stream. *)
+ method insert_metadata m =
+ metadata <- Some (Metautils.to_metadata m)
+
(* [#get buf] completes the frame with the next data in the stream.
* Depending whether caching is enabled or not, it calls [#get_frame] directly
* or tries to get data from the cache frame, filling it if needed.
@@ -435,7 +442,15 @@
assert (Frame.is_partial buf) ;
if not caching then begin
let b = Frame.breaks buf in
+ let p = Frame.position buf in
self#get_frame buf ;
+ begin
+ match metadata with
+ | Some m ->
+ Frame.set_metadata buf p m ;
+ metadata <- None
+ | None -> ()
+ end ;
if List.length b + 1 <> List.length (Frame.breaks buf) then begin
self#log#f 2 "#get_frame didn't add exactly one break!" ;
assert false
------------------------------------------------------------------------------
Increase Visibility of Your 3D Game App & Earn a Chance To Win $500!
Tap into the largest installed PC base & get more eyes on your game by
optimizing for Intel(R) Graphics Technology. Get started today with the
Intel(R) Software Partner Program. Five $500 cash prizes are up for grabs.
http://p.sf.net/sfu/intelisp-dev2dev
_______________________________________________
Savonet-devl mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/savonet-devl