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 ()