From 07e78cc5c541ef3194f38a948b18ea9b10681c1b Mon Sep 17 00:00:00 2001 From: Romain Beauxis Date: Sat, 14 Sep 2024 14:10:31 -0500 Subject: [PATCH] Prioritize, use file decoders file extensions when computing request duration. --- src/core/decoder/external_decoder.ml | 18 +++++++--- src/core/decoder/ffmpeg_decoder.ml | 18 ++++++---- src/core/decoder/liq_flac_decoder.ml | 8 +++-- src/core/decoder/mad_decoder.ml | 9 +++-- src/core/decoder/ogg_flac_duration.ml | 10 ++++-- src/core/decoder/vorbisduration.ml | 10 ++++-- src/core/decoder/wav_aiff_decoder.ml | 9 +++-- src/core/request.ml | 49 ++++++++++++++++++++++++--- src/core/request.mli | 11 +++++- 9 files changed, 116 insertions(+), 26 deletions(-) diff --git a/src/core/decoder/external_decoder.ml b/src/core/decoder/external_decoder.ml index 77bf4b6e81..2a60670808 100644 --- a/src/core/decoder/external_decoder.ml +++ b/src/core/decoder/external_decoder.ml @@ -120,13 +120,18 @@ let register_stdin ~name ~doc ~priority ~mimes ~file_extensions ~test process = stream_decoder = Some (fun ~ctype:_ _ -> create_stream process); }; - let duration ~metadata:_ filename = + let dresolver ~metadata:_ filename = let process = Printf.sprintf "cat %s | %s" (Filename.quote filename) process in duration process in - Plug.register Request.dresolvers name ~doc duration + Plug.register Request.dresolvers name ~doc + { + dpriority = (fun () -> priority); + file_extensions = (fun () -> Option.value ~default:[] file_extensions); + dresolver; + } (** Now an external decoder that directly operates * on the file. The remaining time in this case @@ -196,5 +201,10 @@ let register_oblivious ~name ~doc ~priority ~mimes ~file_extensions ~test stream_decoder = None; }; - let duration ~metadata:_ filename = duration (process filename) in - Plug.register Request.dresolvers name ~doc duration + let dresolver ~metadata:_ filename = duration (process filename) in + Plug.register Request.dresolvers name ~doc + { + dpriority = (fun () -> priority); + file_extensions = (fun () -> Option.value ~default:[] file_extensions); + dresolver; + } diff --git a/src/core/decoder/ffmpeg_decoder.ml b/src/core/decoder/ffmpeg_decoder.ml index bcdf167a34..054ac0179f 100644 --- a/src/core/decoder/ffmpeg_decoder.ml +++ b/src/core/decoder/ffmpeg_decoder.ml @@ -546,7 +546,7 @@ let parse_file_decoder_args metadata = | Some args -> parse_input_args args | None -> ([], None) -let duration ~metadata file = +let dresolver ~metadata file = let args, format = parse_file_decoder_args metadata in let opts = Hashtbl.create 10 in List.iter (fun (k, v) -> Hashtbl.replace opts k v) args; @@ -558,10 +558,16 @@ let duration ~metadata file = Option.map (fun d -> Int64.to_float d /. 1000.) duration) let () = - Plug.register Request.dresolvers "ffmepg" ~doc:"" (fun ~metadata fname -> - match duration ~metadata fname with - | None -> raise Not_found - | Some d -> d) + Plug.register Request.dresolvers "ffmepg" ~doc:"" + { + dpriority = (fun () -> priority#get); + file_extensions = (fun () -> file_extensions#get); + dresolver = + (fun ~metadata fname -> + match dresolver ~metadata fname with + | None -> raise Not_found + | Some d -> d); + } let tags_substitutions = [("track", "tracknumber")] @@ -1087,7 +1093,7 @@ let mk_streams ~ctype ~decode_first_metadata container = let create_decoder ~ctype ~metadata fname = let args, format = parse_file_decoder_args metadata in - let file_duration = duration ~metadata fname in + let file_duration = dresolver ~metadata fname in let remaining = Atomic.make file_duration in let set_remaining ~pts ~duration stream = let pts = diff --git a/src/core/decoder/liq_flac_decoder.ml b/src/core/decoder/liq_flac_decoder.ml index 826616cae7..5a7ff869a6 100644 --- a/src/core/decoder/liq_flac_decoder.ml +++ b/src/core/decoder/liq_flac_decoder.ml @@ -184,7 +184,7 @@ let check filename = true with _ -> false -let duration ~metadata:_ file = +let dresolver ~metadata:_ file = if not (check file) then raise Not_found; let fd = Decoder.openfile file in Fun.protect @@ -199,4 +199,8 @@ let duration ~metadata:_ file = let () = Plug.register Request.dresolvers "flac" ~doc:"Compute duration of flac files." - duration + { + dpriority = (fun () -> priority#get); + file_extensions = (fun () -> file_extensions#get); + dresolver; + } diff --git a/src/core/decoder/mad_decoder.ml b/src/core/decoder/mad_decoder.ml index cb341c0e99..1479b6999f 100644 --- a/src/core/decoder/mad_decoder.ml +++ b/src/core/decoder/mad_decoder.ml @@ -189,11 +189,16 @@ let check filename = true with _ -> false -let duration ~metadata:_ file = +let dresolver ~metadata:_ file = if not (check file) then raise Not_found; let ans = Mad.duration file in match ans with 0. -> raise Not_found | _ -> ans let () = Plug.register Request.dresolvers "mad" - ~doc:"Compute duration of mp3 files using MAD library." duration + ~doc:"Compute duration of mp3 files using MAD library." + { + dpriority = (fun () -> priority#get); + file_extensions = (fun () -> file_extensions#get); + dresolver; + } diff --git a/src/core/decoder/ogg_flac_duration.ml b/src/core/decoder/ogg_flac_duration.ml index f291cc4999..339c3d642e 100644 --- a/src/core/decoder/ogg_flac_duration.ml +++ b/src/core/decoder/ogg_flac_duration.ml @@ -22,7 +22,7 @@ (** Read duration of ogg/flac files. *) -let duration ~metadata:_ file = +let dresolver ~metadata:_ file = let sync, fd = Ogg.Sync.create_from_file file in Fun.protect ~finally:(fun () -> Unix.close fd) @@ -63,4 +63,10 @@ let duration ~metadata:_ file = if samples <= 0. then raise Not_found; samples /. float info.Flac.Decoder.sample_rate) -let () = Plug.register Request.dresolvers "ogg/flac" ~doc:"" duration +let () = + Plug.register Request.dresolvers "ogg/flac" ~doc:"" + { + dpriority = (fun () -> Liq_ogg_decoder.priority#get); + file_extensions = (fun () -> Liq_ogg_decoder.file_extensions#get); + dresolver; + } diff --git a/src/core/decoder/vorbisduration.ml b/src/core/decoder/vorbisduration.ml index 0b5dee4952..679f83ac1b 100644 --- a/src/core/decoder/vorbisduration.ml +++ b/src/core/decoder/vorbisduration.ml @@ -22,10 +22,16 @@ (** Read duration of ogg/vorbis files. *) -let duration ~metadata:_ file = +let dresolver ~metadata:_ file = let dec, fd = Vorbis.File.Decoder.openfile file in Fun.protect ~finally:(fun () -> Unix.close fd) (fun _ -> Vorbis.File.Decoder.duration dec (-1)) -let () = Plug.register Request.dresolvers "vorbis" ~doc:"" duration +let () = + Plug.register Request.dresolvers "vorbis" ~doc:"" + { + dpriority = (fun () -> Liq_ogg_decoder.priority#get); + file_extensions = (fun () -> Liq_ogg_decoder.file_extensions#get); + dresolver; + } diff --git a/src/core/decoder/wav_aiff_decoder.ml b/src/core/decoder/wav_aiff_decoder.ml index 868bea6da9..ed0f0b0fcc 100644 --- a/src/core/decoder/wav_aiff_decoder.ml +++ b/src/core/decoder/wav_aiff_decoder.ml @@ -219,14 +219,19 @@ let () = } let () = - let duration ~metadata:_ file = + let dresolver ~metadata:_ file = let w = Wav_aiff.fopen file in let ret = Wav_aiff.duration w in Wav_aiff.close w; ret in Plug.register Request.dresolvers "wav/aiff" - ~doc:"Native computation of wav and aiff files duration." duration + ~doc:"Native computation of wav and aiff files duration." + { + dpriority = (fun () -> aiff_priorities#get); + file_extensions = (fun () -> aiff_file_extensions#get); + dresolver; + } let basic_mime_types = Dtools.Conf.list diff --git a/src/core/request.ml b/src/core/request.ml index 42cdd400d7..74773d990c 100644 --- a/src/core/request.ml +++ b/src/core/request.ml @@ -121,20 +121,59 @@ let status { status } = Atomic.get status let indicator ?(metadata = Frame.Metadata.empty) ?temporary s = { uri = home_unrelate s; temporary = temporary = Some true; metadata } -(** Length *) +type dresolver = { + dpriority : unit -> int; + file_extensions : unit -> string list; + dresolver : metadata:Frame.metadata -> string -> float; +} + let dresolvers_doc = "Methods to extract duration from a file." -let dresolvers = Plug.create ~doc:dresolvers_doc "audio file formats (duration)" +let conf_dresolvers = + Dtools.Conf.list ~p:(conf#plug "dresolvers") ~d:[] + "Methods to extract file duration." + +let f c v = + match c#get_d with + | None -> c#set_d (Some [v]) + | Some d -> c#set_d (Some (d @ [v])) + +let dresolvers = + Plug.create ~doc:dresolvers_doc + ~register_hook:(fun name _ -> f conf_dresolvers name) + "audio file formats (duration)" + +let get_dresolvers ~file () = + let extension = try Utils.get_ext file with _ -> "" in + let f cur name = + match Plug.get dresolvers name with + | Some ({ file_extensions } as p) + when List.mem extension (file_extensions ()) -> + (name, p) :: cur + | Some _ -> cur + | None -> + log#severe "Cannot find duration resolver %s" name; + cur + in + let resolvers = List.fold_left f [] conf_dresolvers#get in + List.sort + (fun (_, a) (_, b) -> compare (b.dpriority ()) (a.dpriority ())) + resolvers let compute_duration ~metadata file = try - Plug.iter dresolvers (fun _ resolver -> + List.iter + (fun (name, { dpriority; dresolver }) -> try - let ans = resolver ~metadata file in + log#info "Trying duration resolver %s (priority: %d) for file %s.." + name (dpriority ()) + (Lang_string.quote_string file); + let ans = dresolver ~metadata file in raise (Duration ans) with | Duration e -> raise (Duration e) - | _ -> ()); + | _ -> ()) + (get_dresolvers ~file ()); raise Not_found with Duration d -> d diff --git a/src/core/request.mli b/src/core/request.mli index ef3d9aa55d..a17b637942 100644 --- a/src/core/request.mli +++ b/src/core/request.mli @@ -113,6 +113,9 @@ type resolve_flag = [ `Resolved | `Failed | `Timeout ] (** Metadata resolvers priorities. *) val conf_metadata_decoder_priorities : Dtools.Conf.ut +(** Read the request's metadata. *) +val read_metadata : t -> unit + (** [resolve request timeout] tries to resolve the request within [timeout] seconds. *) val resolve : t -> float -> resolve_flag @@ -165,8 +168,14 @@ val done_playing : source:Source.source -> t -> unit (** {1 Plugs} *) +type dresolver = { + dpriority : unit -> int; + file_extensions : unit -> string list; + dresolver : metadata:Frame.metadata -> string -> float; +} + (** Functions for computing duration. *) -val dresolvers : (metadata:Frame.metadata -> string -> float) Plug.t +val dresolvers : dresolver Plug.t (** Type for a metadata resolver. Resolvers are executed in priority order and the first returned metadata take precedence over any other