Skip to content

Commit

Permalink
Merge pull request #31 from shiguredo/feature/add-comments
Browse files Browse the repository at this point in the history
コメントの追加
  • Loading branch information
voluntas committed Aug 28, 2023
2 parents 546a2c3 + e4863cf commit 67a2625
Show file tree
Hide file tree
Showing 15 changed files with 413 additions and 12 deletions.
37 changes: 37 additions & 0 deletions src/dispose_listener.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,48 @@

#include <vector>

/**
* 実装上の留意点:Sora Python SDK は Sora, AudioSource, VideoSource, Connection, Track など、
* それぞれを Python で別々で扱うことができるようになっているが実態としては親を破棄すると子が止まる関係性が存在する。
* これを適切にハンドリングしなければリークを引き起こしてしまうため、破棄された、もしくはされることを通知して、
* 適切にハンドリングを行うためのクラス DisposeSubscriber, DisposePublisher を用意した。
*/

/**
* 破棄された通知を受ける DisposeSubscriber です。
*
* これを継承することで、 DisposePublisher から破棄された通知を受け取ることができます。
*/
class DisposeSubscriber {
public:
/**
* Subscribe している Publisher が破棄された際に呼び出される関数です。
*/
virtual void PublisherDisposed() = 0;
};

/**
* 破棄された際に DisposeSubscriber に通知を送る DisposePublisher です。
*
* 継承して使うことを想定しています。 1 つのインスタンスで複数ので DisposePublisher に破棄を通知することができます。
*/
class DisposePublisher {
public:
// クラスによって、 Disposed を呼ぶタイミングを調整する必要があるためデストラクタでの一律 Disposed 呼び出しは行わない

/**
* Subscribe する際に呼ぶ関数です。
*
* @param subscriber Subscribe する DisposeSubscriber
*/
void AddSubscriber(DisposeSubscriber* subscriber) {
subscribers_.push_back(subscriber);
}
/**
* Subscribe を解除する際に呼ぶ関数です。
*
* @param subscriber Subscribe を解除する DisposeSubscriber
*/
void RemoveSubscriber(DisposeSubscriber* subscriber) {
subscribers_.erase(
std::remove_if(subscribers_.begin(), subscribers_.end(),
Expand All @@ -21,6 +53,11 @@ class DisposePublisher {
}),
subscribers_.end());
}
/**
* Subscriber に破棄されたことを通知する際に呼ぶ関数です。
*
* TODO(tnoho): 役割的に protected にして良いのでは。
*/
virtual void Disposed() {
for (DisposeSubscriber* subscriber : subscribers_) {
subscriber->PublisherDisposed();
Expand Down
15 changes: 13 additions & 2 deletions src/dummy_audio_mixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@ void DummyAudioMixer::Mix(size_t number_of_channels,
webrtc::AudioFrame* audio_frame_for_mixing) {
webrtc::MutexLock lock(&mutex_);
for (auto& source_and_status : audio_source_list_) {
// 第一引数の設定値にサンプリングレートがリサンプリングされる
// -1 を指定するとリサンプリングされなくなる
/**
* webrtc::AudioTrackSinkInterface の OnData はこの関数内で呼ばれる
*
* 第一引数の設定値にサンプリングレートがリサンプリングされるが、
* -1 を指定するとリサンプリングされなくなる。
* SoraAudioSinkImpl の OnData 内でリサンプリングするため、
* ここでは -1 を指定している。
*/
source_and_status->audio_source->GetAudioFrameWithInfo(
-1, &source_and_status->audio_frame);
}
Expand All @@ -41,6 +47,11 @@ void DummyAudioMixer::RemoveSource(Source* audio_source) {

DummyAudioMixer::DummyAudioMixer(webrtc::TaskQueueFactory* task_queue_factory)
: task_queue_factory_(task_queue_factory) {
/**
* 通常 webrtc::AudioMixer の Mix は音声出力デバイスのループで呼ばれるが、
* sora::SoraClientContextConfig::use_audio_device を false にした際に設定される、
* webrtc::AudioDeviceDummy はループを回さないため、ここでループを作ることとした。
*/
task_queue_ =
std::make_unique<rtc::TaskQueue>(task_queue_factory_->CreateTaskQueue(
"TestAudioDeviceModuleImpl",
Expand Down
12 changes: 12 additions & 0 deletions src/dummy_audio_mixer.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@
#include <rtc_base/task_utils/repeating_task.h>
#include <rtc_base/thread_annotations.h>

/**
* webrtc::AudioMixer を継承した DummyAudioMixer です。
*
* PeerConnectionFactory 生成時に渡す cricket::MediaEngineDependencies の
* audio_mixer を指定しない場合 webrtc::AudioMixerImpl が使用されます。
* これはすべての AudioTrack の出力データのサンプリングレートとチャネル数を揃え、
* ミキシングした上で音声出力デバイスに渡す役割を担います。
* しかし、 Python SDK では音声をデバイスに出力することはありません。
* ですが、 AudioTrack からデータを受け取る AudioSinkInterface::OnData は
* AudioMixer により駆動されているため、 AudioSinkInterface::OnData を呼び出す仕組みだけを持つ
* シンプルな webrtc::AudioMixer になっています。
*/
class DummyAudioMixer : public webrtc::AudioMixer {
public:
struct SourceStatus;
Expand Down
95 changes: 95 additions & 0 deletions src/sora.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,77 @@
#include "sora_track_interface.h"
#include "sora_video_source.h"

/**
* Sora Python SDK のベースになるクラスです。
*
* SoraFactory を内包し Connection や AudioSource、VideoSource を生成します。
* 一つの Sora インスタンスから複数の Connection、AudioSource、VideoSource が生成できます。
* 同じ Sora インスタンス内でしか Connection や AudioSource、VideoSource を共有できないので、
* 複数の Sora インスタンスを生成することは不具合の原因になります。
*/
class Sora : public DisposePublisher {
public:
/**
* このタイミングで SoraFactory の生成まで行うため SoraFactory の生成にあたって必要な引数はここで設定します。
*
* @param use_hardware_encoder (オプション)ハードウェアエンコーダーの有効無効 デフォルト: true
* @param openh264 (オプション) OpenH264 ライブラリへのパス
*/
Sora(std::optional<bool> use_hardware_encoder,
std::optional<std::string> openh264);
~Sora();

/**
* Sora と接続する Connection を生成します。
*
* 実装上の留意点:Sora C++ SDK が observer に std::weak_ptr を要求するためポインタで返す Source とは異なり、
* std::shared_ptr で返しますが Python での扱いは変わりません。
*
* @param signaling_urls シグナリングに使用する URL のリスト
* @param role ロール recvonly | sendonly | sendrecv
* @param channel_id チャネル ID
* @param client_id (オプション)クライアント ID
* @param bundle_id (オプション)バンドル ID
* @param metadata (オプション)認証メタデータ
* @param signaling_notify_metadata (オプション)シグナリング通知メタデータ
* @param audio_source (オプション)音声ソース CreateAudioSource で生成した SoraAudioSource を渡してください
* @param video_source (オプション)映像ソース CreateVideoSource で生成した SoraVideoSource を渡してください
* @param audio (オプション)音声の有効無効 デフォルト: true
* @param video (オプション)映像の有効無効 デフォルト: true
* @param audio_codec_type (オプション)音声コーデック OPUS | LYRA デフォルト: OPUS
* @param video_codec_type (オプション)映像コーデック VP8 | VP9 | AV1 | H264 デフォルト: VP9
* @param video_bit_rate (オプション)映像ビットレート kbps 単位です
* @param audio_bit_rate (オプション)音声ビットレート kbps 単位です
* @param video_vp9_params (オプション)映像コーデック VP9 設定
* @param video_av1_params (オプション)映像コーデック AV1 設定
* @param video_h264_params (オプション)映像コーデック H264 設定
* @param simulcast (オプション)サイマルキャストの有効無効
* @param spotlight (オプション)スポットライトの有効無効
* @param spotlight_number (オプション)スポットライトのフォーカス数
* @param simulcast_rid (オプション)サイマルキャストで受信したい RID
* @param spotlight_focus_rid (オプション)スポットライトでフォーカスしているときのサイマルキャスト RID
* @param spotlight_unfocus_rid (オプション)スポットライトでフォーカスしていないときのサイマルキャスト RID
* @param forwarding_filter (オプション)転送フィルター設定
* @param data_channels (オプション) DataChannel 設定
* @param data_channel_signaling (オプション)シグナリングを DataChannel に切り替える機能の有効無効
* @param ignore_disconnect_websocket (オプション)シグナリングを DataChannel に切り替えた際に WebSocket が切断されても切断としない機能の有効無効
* @param data_channel_signaling_timeout (オプション) DataChannel シグナリングタイムアウト
* @param disconnect_wait_timeout (オプション) 切断待ちタイムアウト
* @param websocket_close_timeout (オプション) WebSocket クローズタイムアウト
* @param websocket_connection_timeout (オプション) WebSocket 接続タイムアウト
* @param audio_codec_lyra_bitrate (オプション) 音声コーデック Lyra のビットレート
* @param audio_codec_lyra_usedtx (オプション) 音声コーデック Lyra で DTX の有効無効
* @param check_lyra_version (オプション) 音声コーデック Lyra のバージョンチェック有効無効
* @param audio_streaming_language_code (オプション) 音声ストリーミング機能で利用する言語コード設定
* @param insecure (オプション) 証明書チェックの有効無効 デフォルト: false
* @param client_cert (オプション) クライアント証明書
* @param client_key (オプション) クライアントシークレットキー
* @param proxy_url (オプション) Proxy URL
* @param proxy_username (オプション) Proxy ユーザー名
* @param proxy_password (オプション) Proxy パスワード
* @param proxy_agent (オプション) Proxy エージェント
* @return SoraConnection インスタンス
*/
std::shared_ptr<SoraConnection> CreateConnection(
// 必須パラメータ
const nb::handle& signaling_urls,
Expand Down Expand Up @@ -67,10 +132,40 @@ class Sora : public DisposePublisher {
std::optional<std::string> proxy_password,
std::optional<std::string> proxy_agent);

/**
* Sora に音声データを送る受け口である SoraAudioSource を生成します。
*
* AudioSource に音声データを渡すことで、 Sora に音声を送ることができます。
* AudioSource は MediaStreamTrack として振る舞うため、
* AudioSource と同一の Sora インスタンスから生成された複数の Connection で共用できます。
*
* @param channels AudioSource に入力する音声データのチャネル数
* @param sample_rate AudioSource に入力する音声データのサンプリングレート
* @return SoraAudioSource インスタンス
*/
SoraAudioSource* CreateAudioSource(size_t channels, int sample_rate);
/**
* Sora に映像データを送る受け口である SoraVideoSource を生成します。
*
* VideoSource にフレームデータを渡すことで、 Sora に映像を送ることができます。
* VideoSource は MediaStreamTrack として振る舞うため、
* VideoSource と同一の Sora インスタンスから生成された複数の Connection で共用できます。
*
* @return SoraVideoSource インスタンス
*/
SoraVideoSource* CreateVideoSource();

private:
/**
* Python で渡された値を boost::json::value に変換します。
*
* metadata のように JSON の値として扱える内容であれば自由に指定できるものを、
* nanobind::handle で受け取って Sora C++ SDK で使っている boost::json::value に変換します。
*
* @param value Python から渡された値の nanobind::handle
* @param error_message 変換に失敗した際に nanobind::type_error で返す際のエラーメッセージ
* @return boost::json::value
*/
boost::json::value ConvertJsonValue(nb::handle value,
const char* error_message);
std::vector<sora::SoraSignalingConfig::DataChannel> ConvertDataChannels(
Expand Down
25 changes: 25 additions & 0 deletions src/sora_audio_sink.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,23 @@

namespace nb = nanobind;

/**
* Sora からの音声を受け取る SoraAudioSinkImpl です。
*
* Connection の OnTrack コールバックから渡されるリモート Track から音声を取り出すことができます。
* Track からの音声はコンストラクタで設定したサンプリングレートとチャネル数に変換し、
* SoraAudioSinkImpl 内のバッファに溜め込まれるため、任意のタイミングで音声を取り出すことができます。
* 実装上の留意点:Track の参照保持のための Impl のない SoraAudioSink を __init__.py に定義しています。
* SoraAudioSinkImpl を直接 Python から呼び出すことは想定していません。
*/
class SoraAudioSinkImpl : public webrtc::AudioTrackSinkInterface,
public DisposeSubscriber {
public:
/**
* @param track 音声を取り出す OnTrack コールバックから渡されるリモート Track
* @param output_sample_rate 音声の出力サンプリングレート
* @param output_channels 音声の出力チャネル数
*/
SoraAudioSinkImpl(SoraTrackInterface* track,
int output_sample_rate,
size_t output_channels);
Expand All @@ -38,11 +52,22 @@ class SoraAudioSinkImpl : public webrtc::AudioTrackSinkInterface,
size_t number_of_frames,
absl::optional<int64_t> absolute_capture_timestamp_ms) override;

/**
* 実装上の留意点:コールバックと Read 関数の共存はパフォーマンスや使い方の面で難しいことが判明したので、
* on_data_, on_format_ ともに廃止予定です。
*/
std::function<void(
nb::ndarray<nb::numpy, int16_t, nb::shape<nb::any, nb::any>>)>
on_data_;
std::function<void(int, size_t)> on_format_;

/**
* 受信済みのデータをバッファから読み出す
*
* @param frames 受け取るチャンネルごとのサンプル数。0 を指定した場合には、受信済みのすべてのサンプルを返す
* @param timeout 溜まっているサンプル数が frames で指定した数を満たさない場合の待ち時間。秒単位の float で指定する
* @return Tuple でインデックス 0 には成否が、成功した場合のみインデックス 1 には NumPy の配列 numpy.ndarray で チャンネルごとのサンプル数 x チャンネル数 になっている音声データ
*/
nb::tuple Read(size_t frames, float timeout);

private:
Expand Down
41 changes: 41 additions & 0 deletions src/sora_audio_source.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@

namespace nb = nanobind;

/**
* SoraAudioSourceInterface は SoraAudioSource の実体です。
*
* 実装上の留意点:webrtc::Notifier<webrtc::AudioSourceInterface> を継承しているクラスは
* nanobind で直接的な紐付けを行うとエラーが出るため SoraAudioSource とはクラスを分けました。
*/
class SoraAudioSourceInterface
: public webrtc::Notifier<webrtc::AudioSourceInterface> {
public:
Expand Down Expand Up @@ -55,6 +61,13 @@ class SoraAudioSourceInterface
int64_t last_timestamp_;
};

/**
* Sora に音声データを送る受け口である SoraAudioSource です。
*
* AudioSource に音声データを渡すことで、 Sora に音声を送ることができます。
* AudioSource は MediaStreamTrack として振る舞うため、
* AudioSource と同一の Sora インスタンスから生成された複数の Connection で共用できます。
*/
class SoraAudioSource : public SoraTrackInterface {
public:
SoraAudioSource(DisposePublisher* publisher,
Expand All @@ -63,15 +76,43 @@ class SoraAudioSource : public SoraTrackInterface {
size_t channels,
int sample_rate);

/**
* Sora に送る音声データを渡します。
*
* @param data 送信する 16bit PCM データの参照
* @param samples_per_channel チャンネルごとのサンプル数
* @param timestamp Python の time.time() で取得できるエポック秒で表されるフレームのタイムスタンプ
*/
void OnData(const int16_t* data,
size_t samples_per_channel,
double timestamp);
/**
* Sora に送る音声データを渡します。
*
* タイムスタンプは先に受け取ったデータと連続になっていると想定してサンプル数から自動生成します。
*
* @param data 送信する 16bit PCM データの参照
* @param samples_per_channel チャンネルごとのサンプル数
*/
void OnData(const int16_t* data, size_t samples_per_channel);
/**
* Sora に送る音声データを渡します。
*
* @param ndarray NumPy の配列 numpy.ndarray で チャンネルごとのサンプル数 x チャンネル数 になっている音声データ
* @param timestamp Python の time.time() で取得できるエポック秒で表されるフレームのタイムスタンプ
*/
void OnData(nb::ndarray<int16_t,
nb::shape<nb::any, nb::any>,
nb::c_contig,
nb::device::cpu> ndarray,
double timestamp);
/**
* Sora に送る音声データを渡します。
*
* タイムスタンプは先に受け取ったデータと連続になっていると想定してサンプル数から自動生成します。
*
* @param ndarray NumPy の配列 numpy.ndarray で チャンネルごとのサンプル数 x チャンネル数 になっている音声データ
*/
void OnData(nb::ndarray<int16_t,
nb::shape<nb::any, nb::any>,
nb::c_contig,
Expand Down
Loading

0 comments on commit 67a2625

Please sign in to comment.