method private get_frame ab =
(* Choose the next child to be played.
* [forget] tells that the current child has finished its track,
* in which case a transition does not make sense and would actually start
* playing a next track (if available) on the left child. *)
let reselect ?(forget=false) () =
if not forget then need_eot <- true ;
match let c = self#cached_select in cached_selected <- None ; c with
| Some c ->
begin match selected with
| None ->
self#log#f 3 "Switch to %s." c.source#id ;
(* The source is already ready, this call is only there for
* allowing an uniform treatment of switches, triggering
* a #leave call. *)
c.source#get_ready activation ;
selected <- Some (c,c.source)
| Some (old_c,old_s) when old_c != c ->
self#log#f 3 "Switch to %s with%s transition."
c.source#id
(if forget then " forgetful" else "") ;
old_s#leave (self:>source) ;
to_finish <- old_s::to_finish ;
Clock.collect_after begin fun () ->
let old_source =
if forget then Blank.empty kind else old_c.source
in
let new_source =
(* Force insertion of old metadata if relevant.
* It can't be done in a static way: we need to start
* pulling data to see if new metadata comes out, in case
* the source was shared and kept streaming from somewhere
* else (this is thanks to Frame.get_chunk).
* A quicker hack might have been doable if there wasn't a
* transition in between. *)
match c.cur_meta with
| Some m when replay_meta ->
new Insert_metadata.replay ~kind m c.source
| _ -> c.source
in
let s =
let t = Lang.source_t (Lang.kind_type_of_frame_kind kind) in
Lang.to_source
(Lang.apply ~t
c.transition
[ "",Lang.source old_source ;
"",Lang.source new_source ])
in
Clock.unify s#clock self#clock ;
s#get_ready activation ;
selected <- Some (c,s)
end
| _ ->
(* We are staying on the same child,
* don't start a new track. *)
need_eot <- false
end
| None ->
begin match selected with
| Some (old_c,old_s) ->
old_s#leave (self:>source) ;
to_finish <- old_s::to_finish ;
selected <- None
| None -> ()
end
in
(* #select is called only when selected=None, and the cache is cleared
* as soon as the new selection is set. *)
assert (selected=None || cached_selected=None) ;
if need_eot then begin
need_eot <- false ;
Frame.add_break ab (Frame.position ab)
end else
match selected with
| None ->
reselect ~forget:true () ;
(* Our #is_ready, and caching, ensure the following. *)
assert (selected <> None) ;
self#get_frame ab
| Some (c,s) ->
s#get ab ;
c.cur_meta <-
(if Frame.is_partial ab then
None
else
match
List.fold_left
(function
| None -> fun (p,m) -> Some (p,m)
| Some (curp,curm) -> fun (p,m) ->
Some (if p>=curp then (p,m) else (curp,curm)))
(match c.cur_meta with
| None -> None
| Some m -> Some (-1,m))
(Frame.get_all_metadata ab)
with
| None -> None
| Some (_,m) -> Some m) ;
if Frame.is_partial ab then
reselect ~forget:true ()
else
if mode = Insensitive then
reselect ()