1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
|
(*****************************************************************************
Liquidsoap, a programmable audio stream generator.
Copyright 2003-2017 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
*****************************************************************************)
(** Decode and read metadata from flac files. *)
open Dtools
let log = Log.make ["decoder";"flac"]
exception End_of_stream
module Make (Generator:Generator.S_Asio) =
struct
let create_decoder input =
let resampler = Rutils.create_audio () in
let read = input.Decoder.read in
let seek =
match input.Decoder.lseek with
| Some f -> Some (fun len -> ignore(f (Int64.to_int len)))
| None -> None
in
let tell =
match input.Decoder.tell with
| Some f -> Some (fun () -> (Int64.of_int (f ())))
| None -> None
in
let length =
match input.Decoder.length with
| Some f -> Some (fun () -> (Int64.of_int (f ())))
| None -> None
in
let dummy_c =
Flac.Decoder.get_callbacks
?seek ?tell ?length read (fun _ -> ())
in
let decoder = Flac.Decoder.create dummy_c in
let decoder,info,_ = Flac.Decoder.init decoder dummy_c in
let sample_freq,_ = info.Flac.Decoder.sample_rate,
info.Flac.Decoder.channels
in
let processed = ref Int64.zero in
{ Decoder.
seek =
(fun ticks ->
let duration = Frame.seconds_of_master ticks in
let samples =
Int64.of_float (duration *. (float sample_freq))
in
let pos = Int64.add !processed samples in
let c =
Flac.Decoder.get_callbacks
?seek ?tell ?length read (fun _ -> ())
in
let ret = Flac.Decoder.seek decoder c pos in
if ret = true then
begin
processed := pos;
ticks
end
else
match Flac.Decoder.state decoder c with
| `Seek_error ->
if Flac.Decoder.flush decoder c then
0
else
(* Flushing failed, we are in an unknown state.. *)
raise End_of_stream
| _ -> 0);
decode =
(fun gen ->
let c =
Flac.Decoder.get_callbacks ?seek ?tell ?length read
(fun data ->
let len =
try
Array.length data.(0)
with
| _ -> 0
in
processed := Int64.add !processed (Int64.of_int len);
let content =
resampler ~audio_src_rate:(float sample_freq) data
in
Generator.set_mode gen `Audio ;
Generator.put_audio gen content 0 (Array.length content.(0)))
in
match Flac.Decoder.state decoder c with
| `Search_for_metadata
| `Read_metadata
| `Search_for_frame_sync
| `Read_frame ->
Flac.Decoder.process decoder c
| _ -> raise End_of_stream) }
end
(** Configuration keys for flac. *)
let mime_types =
Conf.list ~p:(Decoder.conf_mime_types#plug "flac")
"Mime-types used for guessing FLAC format"
~d:["audio/x-flac"]
let file_extensions =
Conf.list ~p:(Decoder.conf_file_extensions#plug "flac")
"File extensions used for guessing FLAC format"
~d:["flac"]
module G = Generator.From_audio_video
module Buffered = Decoder.Buffered(G)
module D = Make(G)
let create_file_decoder filename kind =
let generator = G.create `Audio in
Buffered.file_decoder filename kind D.create_decoder generator
(* Get the number of channels of audio in an MP3 file.
* This is done by decoding a first chunk of data, thus checking
* that libmad can actually open the file -- which doesn't mean much. *)
let get_type filename =
let fd =
Unix.openfile filename [Unix.O_RDONLY] 0o640
in
Tutils.finalize ~k:(fun () -> Unix.close fd)
(fun () ->
let write = fun _ -> () in
let h = Flac.Decoder.File.create_from_fd write fd in
let info = h.Flac.Decoder.File.info in
let rate,channels = info.Flac.Decoder.sample_rate,
info.Flac.Decoder.channels
in
log#f 4
"Libflac recognizes %S as FLAC (%dHz,%d channels)."
filename rate channels ;
{ Frame.
audio = channels ;
video = 0 ;
midi = 0 })
let () =
Decoder.file_decoders#register
"FLAC"
~sdoc:"Use libflac to decode any file \
if its MIME type or file extension is appropriate."
(fun ~metadata:_ filename kind ->
if not (Decoder.test_file ~mimes:mime_types#get
~extensions:file_extensions#get
~log filename) then
None
else
if kind.Frame.audio = Frame.Variable ||
kind.Frame.audio = Frame.Succ Frame.Variable ||
(* libmad always respects the first two kinds *)
if Frame.type_has_kind (get_type filename) kind then true else begin
log#f 3
"File %S has an incompatible number of channels."
filename ;
false
end
then
Some (fun () -> create_file_decoder filename kind)
else
None)
module D_stream = Make(Generator.From_audio_video_plus)
let () =
Decoder.stream_decoders#register
"FLAC"
~sdoc:"Use libflac to decode any stream with an appropriate MIME type."
(fun mime kind ->
let (<:) a b = Frame.mul_sub_mul a b in
if List.mem mime mime_types#get &&
(* Check that it is okay to have zero video and midi,
* and at least one audio channel. *)
Frame.Zero <: kind.Frame.video &&
Frame.Zero <: kind.Frame.midi &&
kind.Frame.audio <> Frame.Zero
then
(* In fact we can't be sure that we'll satisfy the content
* kind, because the MP3 stream might be mono or stereo.
* For now, we let this problem result in an error at
* decoding-time. Failing early would only be an advantage
* if there was possibly another plugin for decoding
* correctly the stream (e.g. by performing conversions). *)
Some D_stream.create_decoder
else
None)
let log = Dtools.Log.make ["metadata";"flac"]
let get_tags file =
if not (Decoder.test_file ~mimes:mime_types#get
~extensions:file_extensions#get
~log file) then
raise Not_found ;
let fd =
Unix.openfile file [Unix.O_RDONLY] 0o640
in
Tutils.finalize ~k:(fun () -> Unix.close fd)
(fun () ->
let write = fun _ -> () in
let h = Flac.Decoder.File.create_from_fd write fd in
match h.Flac.Decoder.File.comments with
| Some (_,m) -> m
| None -> [])
let () = Request.mresolvers#register "FLAC" get_tags
let check filename =
match Configure.file_mime with
| Some f -> List.mem (f filename) mime_types#get
| None -> (try ignore (get_type filename) ; true with _ -> false)
let duration file =
if not (check file) then raise Not_found ;
let fd =
Unix.openfile file [Unix.O_RDONLY] 0o640
in
Tutils.finalize ~k:(fun () -> Unix.close fd)
(fun () ->
let write = fun _ -> () in
let h = Flac.Decoder.File.create_from_fd write fd in
let info = h.Flac.Decoder.File.info in
match info.Flac.Decoder.total_samples with
| x when x = Int64.zero -> raise Not_found
| x -> (Int64.to_float x) /. (float info.Flac.Decoder.sample_rate))
let () =
Request.dresolvers#register "FLAC" duration
|