Hi all,
As part of a secret project with Samuel, I ended up writing a decoder
for files containing only metadata: 0 tracks of midi, 0 of audio, 0 of
video.
The format is quite simple (the numbers are seconds, floats are
allowed, and you can also write 3:02 for 3 minutes and 2 seconds,
etc.). It's a bit like subtitles (but this is not what our secret
project is about...)
$ cat foo.txt
0 "ah" "blah"
1 "ah" "blih"
2 "ah" "bloh"
5 "ah" "bluh"
For example,
$ src/liquidsoap 'output.dummy(fallible=true,on_metadata(fun
(m)->log(string_of(m)),id.empty(single(argv(1)))))' -- foo.txt
2010/02/11 00:07:14 [lang:3] [("ah","blah")]
2010/02/11 00:07:15 [lang:3] [("ah","blih")]
2010/02/11 00:07:16 [lang:3] [("ah","bloh")]
2010/02/11 00:07:19 [lang:3] [("ah","bluh")]
2010/02/11 00:07:19 [lang:3] [("ah","blah")]
2010/02/11 00:07:19 [lang:3] [("ah","bluh")]
2010/02/11 00:07:20 [lang:3] [("ah","blih")]
2010/02/11 00:07:21 [lang:3] [("ah","bloh")]
2010/02/11 00:07:24 [lang:3] [("ah","bluh")]
It's that simple. The only glitch is that the last metadata is mixed
with the first one, and appears twice as a result. Also it made me
realize that it's kind of dirty to allow two metadata packets at the
same position in a frame.
The main problem, though, is that when I added arbitrary content types
in liquidsoap, I decided to use (0,0,0) for representing non-media
files, such as playlists. This simplified the code, but it now appears
that we may want to decode files for the "empty" type of stream. The
attached patch is not ready for production since it works around this
problem by breaking playlist support.
To complete this feature we probably need to roll back to the old
media/non-media distinction -- which is doable but sadly heavy. Any
other idea, opinion?
Cheers,
--
David
Index: src/Makefile
===================================================================
--- src/Makefile (revision 7156)
+++ src/Makefile (working copy)
@@ -38,6 +38,7 @@
decoders = \
decoder/wav_decoder.ml decoder/midi_decoder.ml \
+ decoder/metadata_decoder.ml \
$(if $(W_OGG),decoder/ogg_decoder.ml) \
$(if $(W_MP3),decoder/mp3.ml) \
$(if $(W_VORBIS),decoder/vorbisduration.ml) \
Index: src/operators/dyn_op.ml
===================================================================
--- src/operators/dyn_op.ml (revision 7156)
+++ src/operators/dyn_op.ml (working copy)
@@ -62,6 +62,9 @@
| _ -> assert false
method is_ready =
+ (* TODO maybe try a #select, but then we'd need a caching mechanism
+ * also we might want to indicate to the function whether it's a
+ * "pure" is_ready, a reslect inside a track, or at the end of it. *)
match source with Some s when s#is_ready -> true | _ -> false
method private get_frame frame =
Index: src/lang/lang.mli
===================================================================
--- src/lang/lang.mli (revision 7156)
+++ src/lang/lang.mli (working copy)
@@ -111,7 +111,8 @@
(lang_kind_format,lang_kind_format,lang_kind_format) Frame.fields
val any_fixed : lang_kind_formats
-val any_fixed_with : ?audio:int -> ?video:int -> ?midi:int -> unit -> lang_kind_formats
+val any_fixed_with :
+ ?audio:int -> ?video:int -> ?midi:int -> unit -> lang_kind_formats
val audio_any : lang_kind_formats
val audio_mono : lang_kind_formats
Index: src/request.ml
===================================================================
--- src/request.ml (revision 7156)
+++ src/request.ml (working copy)
@@ -337,7 +337,7 @@
with
| No_indicator -> ()
in
- if t.kind <> raw_kind then check_decodable t.kind
+ (* if t.kind <> raw_kind then *) check_decodable t.kind
let push_indicators t l =
if l <> [] then
Index: src/decoder/mp3.ml
===================================================================
--- src/decoder/mp3.ml (revision 7156)
+++ src/decoder/mp3.ml (working copy)
@@ -38,6 +38,7 @@
let content,length =
resampler ~audio_src_rate:(float sample_freq) data
in
+ (* TODO length = array.length content.(0) *)
Generator.set_mode gen `Audio ;
Generator.put_audio gen content 0 (Array.length content.(0)))
Index: src/decoder/metadata_decoder.ml
===================================================================
--- src/decoder/metadata_decoder.ml (revision 0)
+++ src/decoder/metadata_decoder.ml (revision 0)
@@ -0,0 +1,102 @@
+(*****************************************************************************
+
+ 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 log = Dtools.Log.make ["decoder";"mp3"]
+
+exception Error
+
+(** Parse next item:
+ * [[Int:]Int:]Int String String
+ * Int can be Float, and there can be more than hours. *)
+let parse_item stream =
+ let rec ts_of_list d = function
+ | hd::tl -> hd*.d +. ts_of_list (d*.60.) tl
+ | [] -> 0.
+ in
+ let rec read_meta timestamp =
+ match Stream.next stream with
+ | Genlex.Kwd ":" -> read_meta timestamp
+ | Genlex.Int i -> read_meta (float i :: timestamp)
+ | Genlex.Float f -> read_meta (f :: timestamp)
+ | Genlex.String key ->
+ begin match Stream.next stream with
+ | Genlex.String value -> ts_of_list 1. timestamp, key, value
+ | _ -> raise Error
+ end
+ | _ -> raise Error
+ in
+ read_meta []
+
+let parse_file filename =
+ let input = open_in filename in
+ let lexer = Genlex.make_lexer [":"] (Stream.of_channel input) in
+ let rec parse acc =
+ match try Some (parse_item lexer) with _ -> None with
+ | Some i -> parse (i::acc)
+ | None -> List.rev acc
+ in
+ let data = parse [] in
+ close_in input ;
+ data
+
+let empty = {Frame.audio=0;video=0;midi=0}
+
+let file_deco filename =
+ let events = ref (parse_file filename) in
+ let t = ref 0. in
+ let size = Lazy.force Frame.size in
+ let fill frame =
+ let pos = Frame.position frame in
+ let duration = Frame.seconds_of_master (size-pos) in
+ let rec aux t' =
+ match !events with
+ | (ts,k,v)::tl ->
+ if ts < !t+.duration then
+ let pos = Frame.master_of_seconds (ts -. !t) in
+ let meta = Hashtbl.create 1 in
+ Hashtbl.add meta k v ;
+ Frame.set_metadata frame pos meta ;
+ events := tl ;
+ aux pos
+ else
+ Frame.add_break frame size
+ | [] -> Frame.add_break frame pos
+ in
+ ignore (Frame.content_of_type frame pos empty) ;
+ aux pos ;
+ (* log#f 4 "Decoded metadata file chunk (%d-%d)." pos (Frame.position) ;
+ *)
+ t := !t +. Frame.seconds_of_master (Frame.position frame - pos) ;
+ -1 (* TODO remaining time *)
+ in
+ { Decoder. fill = fill ; close = ignore }
+
+let () =
+ Decoder.file_decoders#register "META"
+ (fun filename kind ->
+ if Frame.type_has_kind empty kind then begin
+ log#f 4 "Trying to read %S as pure metadata." filename ;
+ ignore (parse_file filename) ;
+ log#f 4 "Read %S as pure metadata." filename ;
+ Some (fun () -> file_deco filename)
+ end else
+ None)
Index: src/conversions/id.ml
===================================================================
--- src/conversions/id.ml (revision 7156)
+++ src/conversions/id.ml (working copy)
@@ -87,3 +87,19 @@
let f v = List.assoc v p in
let src = Lang.to_source (f "") in
new id ~kind src)
+
+let () =
+ let kind =
+ { Frame. audio = Frame.Zero ; video = Frame.Zero ; midi = Frame.Zero }
+ in
+ let kind_type = Lang.kind_type_of_frame_kind kind in
+ Lang.add_operator "id.empty"
+ ["", Lang.source_t kind_type, None, None]
+ ~category:Lang.SoundProcessing
+ ~descr:"Does not do anything, \
+ but forces the stream type of the input source."
+ ~kind:(Lang.Unconstrained kind_type)
+ (fun p kind ->
+ let f v = List.assoc v p in
+ let src = Lang.to_source (f "") in
+ new id ~kind src)
------------------------------------------------------------------------------
SOLARIS 10 is the OS for Data Centers - provides features such as DTrace,
Predictive Self Healing and Award Winning ZFS. Get Solaris 10 NOW
http://p.sf.net/sfu/solaris-dev2dev
_______________________________________________
Savonet-devl mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/savonet-devl