// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// AudioMirroringManager is a singleton object that maintains a set of active
// audio mirroring destinations and auto-connects/disconnects audio streams
// to/from those destinations.  It is meant to be used exclusively on the IO
// thread.
//
// How it works:
//
//   1. AudioRendererHost gets a CreateStream message from the render process
//      and, among other things, creates an AudioOutputController to control the
//      audio data flow between the render and browser processes.  More
//      importantly, it registers the AudioOutputController with
//      AudioMirroringManager (as a Diverter).
//   2. A user request to mirror all the audio for a WebContents is made.  A
//      MirroringDestination is created, and StartMirroring() is called to begin
//      the mirroring session.  The MirroringDestination is queried to determine
//      which of all the known Diverters will re-route their audio to it.
//   3a. During a mirroring session, AudioMirroringManager may encounter new
//       Diverters, and will query all the MirroringDestinations to determine
//       which is a match, if any.
//   3b. During a mirroring session, a call to StartMirroring() can be made to
//       request a "refresh" query on a MirroringDestination, and this will
//       result in AudioMirroringManager starting/stopping only those Diverters
//       that are not correctly routed to the destination.
//   3c. When a mirroring session is stopped, the remaining destinations will be
//       queried to determine whether diverting should continue to a different
//       destination.

#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_AUDIO_MIRRORING_MANAGER_H_
#define CONTENT_BROWSER_MEDIA_CAPTURE_AUDIO_MIRRORING_MANAGER_H_

#include <map>
#include <set>
#include <utility>
#include <vector>

#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/threading/thread_checker.h"
#include "content/common/content_export.h"
#include "media/audio/audio_source_diverter.h"

namespace media {
class AudioOutputStream;
}

namespace content {

class CONTENT_EXPORT AudioMirroringManager {
 public:
  // Interface for diverting audio data to an alternative AudioOutputStream.
  typedef media::AudioSourceDiverter Diverter;

  // A SourceFrameRef is a RenderFrameHost identified by a <render_process_id,
  // render_frame_id> pair.
  typedef std::pair<int, int> SourceFrameRef;

  // Interface to be implemented by audio mirroring destinations.  See comments
  // for StartMirroring() and StopMirroring() below.
  class MirroringDestination {
   public:
    // Asynchronously query whether this MirroringDestination wants to consume
    // audio sourced from each of the |candidates|.  |results_callback| is run
    // to indicate which of them (or none) should have audio routed to this
    // MirroringDestination.  The second parameter of |results_callback|
    // indicates whether the MirroringDestination wants either: 1) exclusive
    // access to a diverted audio flow versus 2) a duplicate copy of the audio
    // flow. |results_callback| must be run on the same thread as the one that
    // called QueryForMatches().
    typedef base::Callback<void(const std::set<SourceFrameRef>&, bool)>
        MatchesCallback;
    virtual void QueryForMatches(
        const std::set<SourceFrameRef>& candidates,
        const MatchesCallback& results_callback) = 0;

    // Create a consumer of audio data in the format specified by |params|, and
    // connect it as an input to mirroring.  This is used to provide
    // MirroringDestination with exclusive access to pull the audio flow from
    // the source. When Close() is called on the returned AudioOutputStream, the
    // input is disconnected and the object becomes invalid.
    virtual media::AudioOutputStream* AddInput(
        const media::AudioParameters& params) = 0;

    // Create a consumer of audio data in the format specified by |params|, and
    // connect it as an input to mirroring.  This is used to provide
    // MirroringDestination with duplicate audio data, which is pushed from the
    // main audio flow. When Close() is called on the returned AudioPushSink,
    // the input is disconnected and the object becomes invalid.
    virtual media::AudioPushSink* AddPushInput(
        const media::AudioParameters& params) = 0;

   protected:
    virtual ~MirroringDestination() {}
  };

  // Note: Use GetInstance() for non-test code.
  AudioMirroringManager();
  virtual ~AudioMirroringManager();

  // Returns the global instance.
  static AudioMirroringManager* GetInstance();

  // Add/Remove a diverter for an audio stream with a known RenderFrame source
  // (represented by |render_process_id| + |render_frame_id|).  Multiple
  // diverters may be added for the same source frame, but never the same
  // diverter.  |diverter| must live until after RemoveDiverter() is called.
  virtual void AddDiverter(int render_process_id, int render_frame_id,
                           Diverter* diverter);
  virtual void RemoveDiverter(Diverter* diverter);

  // (Re-)Start/Stop mirroring to the given |destination|.  |destination| must
  // live until after StopMirroring() is called.  A client may request a
  // re-start by calling StartMirroring() again; and this will cause
  // AudioMirroringManager to query |destination| and only re-route those
  // diverters that are missing/new to the returned set of matches.
  virtual void StartMirroring(MirroringDestination* destination);
  virtual void StopMirroring(MirroringDestination* destination);

 private:
  friend class AudioMirroringManagerTest;

  struct StreamRoutingState {
    // The source render frame associated with the audio stream.
    SourceFrameRef source_render_frame;

    // The diverter for re-routing the audio stream.
    Diverter* diverter;

    // If not NULL, the audio stream is currently being diverted to this
    // destination.
    MirroringDestination* destination;

    // The destinations to which audio stream is duplicated. AudioPushSink is
    // owned by the Diverter, but AudioMirroringManager must guarantee
    // StopDuplicating() is called to release them.
    std::map<MirroringDestination*, media::AudioPushSink*> duplications;

    StreamRoutingState(const SourceFrameRef& source_frame,
                       Diverter* stream_diverter);
    StreamRoutingState(const StreamRoutingState& other);
    ~StreamRoutingState();
  };

  typedef std::vector<StreamRoutingState> StreamRoutes;
  typedef std::vector<MirroringDestination*> Destinations;

  // Helper to find a destination other than |old_destination| for the given
  // |candidates| to be diverted to.
  void InitiateQueriesToFindNewDestination(
      MirroringDestination* old_destination,
      const std::set<SourceFrameRef>& candidates);

  // MirroringDestination query callback.  |matches| contains all RenderFrame
  // sources that will be diverted or duplicated to |destination|.
  // If |add_only| is false, then any audio flows currently routed to
  // |destination| but not found in |matches| will be stopped.
  // If |is_duplicate| is true, the audio data flow will be duplicated to the
  // destination instead of diverted.
  void UpdateRoutesToDestination(MirroringDestination* destination,
                                 bool add_only,
                                 const std::set<SourceFrameRef>& matches,
                                 bool is_duplicate);

  // |matches| contains all RenderFrame sources that will be diverted to
  // |destination|.  If |add_only| is false, then any Diverters currently routed
  // to |destination| but not found in |matches| will be stopped.
  void UpdateRoutesToDivertDestination(MirroringDestination* destination,
                                       bool add_only,
                                       const std::set<SourceFrameRef>& matches);

  // |matches| contains all RenderFrame sources that will be duplicated to
  // |destination|.  If |add_only| is false, then any Diverters currently
  // duplicating to |destination| but not found in |matches| will be stopped.
  void UpdateRoutesToDuplicateDestination(
      MirroringDestination* destination,
      bool add_only,
      const std::set<SourceFrameRef>& matches);

  // Starts diverting audio to the |new_destination|, if not NULL.  Otherwise,
  // stops diverting audio.
  static void RouteDivertedFlow(StreamRoutingState* route,
                                MirroringDestination* new_destination);

  // Routing table.  Contains one entry for each Diverter.
  StreamRoutes routes_;

  // All active mirroring sessions.
  Destinations sessions_;

  // Used to check that all AudioMirroringManager code runs on the same thread.
  base::ThreadChecker thread_checker_;

  DISALLOW_COPY_AND_ASSIGN(AudioMirroringManager);
};

}  // namespace content

#endif  // CONTENT_BROWSER_MEDIA_CAPTURE_AUDIO_MIRRORING_MANAGER_H_
