Windows Phone 8のリリース前は、voipアプリケーションのユーザーはバックグラウンドでの作業に非常にがっかりしていました。実際、これは実質的に欠席していました。聞いてすぐに消えます。 一方で、アプリケーションがバックグラウンドで完全に機能するかのようにバッテリーを消費することはできませんでしたが、一方で、あまり有用ではないツールになりました。 WP8より前は、MicrosoftはSkypeをオペレーティングシステムに統合し、バックグラウンドで動作することを約束して、プラットフォームの新しいバージョンに対する公共の関心を高めました。 さて、彼らは約束を果たしました-今では可能になりました:
- 電話の連絡先帳からSkype通話を開始する
- 意図的または誤ってアプリケーションを最小化しても、Skypeで話し続けます(以前、会話中に誤って検索ボタンを押した場合-会話は終了しました)
- 最も興味深いのは、Skypeが実行されていない(フォアグラウンドではない)状態で通常のgsm-callインターフェースで着信コールを受信し、さらにバックグラウンドで何もしない(バッテリーを消費しない)ことです。
マイクロソフトは自社製品にこの排他的な機会を作らず(コンタクトブックへの統合を除く)、APIを公開しました。これにより、サードパーティの開発者は特権パートナーにならずに同じスクリプトを実装できます(ネイティブSDKを使用するWP7の場合)。 また、連絡先帳に統合することも不可能ですが、ContactStoreおよびProtocolハンドラを使用して、連絡先のURLフィールドを変更し、クリックでアプリケーションを開くことができます。
記事の最後には、2つのプロジェクトのソースコードが添付されています。1つはMicrosoft Chatterboxの例です。これは、バックグラウンドプロセスが着信呼び出しやビデオでのバックエンドシミュレーションでどのように機能するかを説明します。 2番目は、2つのデバイスでvoipを介して通信し、voipプッシュ通知を使用できるシンプルなバックエンドを使用した私のプロジェクトですが、最初に行うことです。
バックグラウンドで動作するVoIPアプリケーションアーキテクチャ
本格的なvoipアプリケーションを作成しようとすると、残念ながら(または幸いなことに)C ++のネイティブコンポーネントなしではできません(オーディオデバイスを操作するための通常のAPIが管理対象部分から利用できないため)。要するに、voipはアプリケーションです。バックグラウンドで動作するものは、2つのプロセスで構成される必要があります。
- フォアグラウンドは、実際には、アプリケーションインターフェイスが「実行」される通常のプロセスです。
- バックグラウンドは、基本的に4つのエージェントで構成される2番目のプロセスです。
- VoipHttpIncomingCallTask-プッシュチャンネルに着信コールが到着すると開始します(特別なタイプのプッシュ通知については以下で説明します)。
- VoipForegroundLifetimeAgent-アプリケーションがアクティブになると開始し、アプリケーションが最小化または閉じるまで動作します。
- VoipCallInProgressAgent-呼び出しをサポートするためにプロセスにさらにプロセッサリソースが割り当てられたことを通知する呼び出しで実行されます。 したがって、ビデオとオーディオの(デ)エンコードは、このイベントの後に開始する必要があります。
- VoipKeepAliveTask -6時間ごとに定期的に実行されます。 実際、アプリケーションが電話にまだインストールされていることをサーバーに定期的に通知するために必要です。
- アウトプロセスは、最初の2つの間の通信の問題を解決するために設計されたプロセス間コンポーネントです。 これは実際には同じ2番目のプロセスです。
グラフィカルには、次のようになります。
独自のVoIPアプリケーションを作成する方法は?
順番に始めましょう:
1.輸送
まず、データの転送レベルを把握しましょう。 もちろん、これは私が1日で構築した非常に単純な例ですので、ここでメガクレイジーな作品はありません-あなた自身が理解しています:トランスポートの作成、オーディオの録音、再生は十分ではありません-弱い通信チャネルでも遅延なく素早く動作するためにこれも必要です-しかし、単一の本ではないトピック。 したがって、トランスポートには、新しいAPIの非常に便利なクラスであるDatagramSocketを使用します(シンプルで、オーディオ/ビデオストリームよりも論理的なUDPで動作します(各オーディオパケットの配信の確認を待つ必要はありませんか?)。async \ await the work彼との非常に簡単です:
const string host = "192.168.1.12"; const string port = "12398"; var socket = new DatagramSocket(); socket.MessageReceived += (s, e) => { // var reader = e.GetDataReader(); string message = reader.ReadString(reader.UnconsumedBufferLength); }; await socket.BindServiceNameAsync(host); var stream = await socket.GetOutputStreamAsync(new HostName(host), port); var dataWriter = new DataWriter(stream); // dataWriter.WriteString("Hello!"); await dataWriter.StoreAsync(); //
私はサーバー側にも同じクラスを使用しているので、非同期に待つことに慣れています(デスクトップエンドポイントでWinRT APIを使用する方法については、 こちらを参照してください)。 プロトコルも非常に単純です:COMMAND!BODY-この例では十分です。
2.音声録音
マイクからのデータを記録するためのマネージドパーツには、2つのクラスがあります。
- XNAマイク
- AudioVideoCaptureDevice
この例では、ネイティブapiを使用せずに2番目から音声を再生する方法を個人的に理解できなかったため、最初の1つを使用します(WP7でも引き続き使用可能です)が、もちろん、深刻なvoipアプリケーションを実装するには、2番目の方法を使用する必要があります(StartRecordingToSinkAsync、マイクからのデータのクリーンな非圧縮ストリームを提供します)。 そのため、マイクからのデータの記録はわずか数行で構成されています。
_microphone = Microphone.Default; _microphone.BufferDuration = TimeSpan.FromMilliseconds(500); _microphoneBuffer = new byte[_microphone.GetSampleSizeInBytes(_microphone.BufferDuration)]; _microphone.BufferReady += (s, e) => { _microphone.GetData(_microphoneBuffer); // }; _microphone.Start();
3.オーディオを再生する
この例では、非常に最適ではないが機能する小さなコードを使用します。
_soundEffect = new SoundEffect(e.Data, _microphone.SampleRate, AudioChannels.Mono); _soundEffect.Play();
残念ながら、代替手段はなく、マネージドパーツを介して通話のためにスピーカーで音声を再生する方法はありませんが、スピーカーでのみ音声が再生されるため、エコーやその他のノイズが発生する可能性があります(これは簡単な例です)。
4. VoIPプッシュ通知
この例のキラー機能は、このアプリケーションを2つのデバイスにインストールすると、そのデバイス上のアプリケーションのフォアグラウンドにいなくても、アプリケーションを介して別のデバイスに呼び出しを行うことができることです。 最初に、サーバー上の両方のデバイスのプッシュURIをユーザーIDとともに登録する必要があります(Skypeでは、これはViberの任意の名前-ユーザーの電話番号)。 次に、デバイスAがデバイスBを呼び出す場合、サーバーにコマンドを送信し、サーバーはデバイスBのプッシュURIを見つけ、ヘッダーに要求X-NotificationClass = 4が含まれていることを前提として、呼び出し元に関する情報をMPNS xmlに送信します。 WP8以前のプッシュ通知には3つのクラスしかありませんでした
- タイル
- 生
- トースト
ご覧のとおり、WP8では新しい4番目のクラスであるVoIPが追加されています。 MPNSは、そのチャネルを介してこのパケットをクライアントに送信し、この目的のために特別に起動されたScheduledTaskAgentを発生させます。 このエージェントが正常に機能する場合、ユーザーには着信画面が表示されます(通常のGSM通話と同様)。 それでは、 ScheduledTaskAgentは何をすべきでしょうか?
var incomingCallTask = task as VoipHttpIncomingCallTask; if (incomingCallTask != null) { // XML Notification pushNotification; using (var ms = new MemoryStream(incomingCallTask.MessageBody)) { var xs = new XmlSerializer(typeof(Notification)); pushNotification = (Notification)xs.Deserialize(ms); } VoipPhoneCall callObj; var callCoordinator = VoipCallCoordinator.GetDefault(); // gsm-call-like callCoordinator.RequestNewIncomingCall("/MainPage.xaml?incomingCall=" + pushNotification.Number, pushNotification.Name, pushNotification.Number, new Uri(defaultContactImageUri), "Voip.Client.Phone", new Uri(appLogoUri), " VoIP-push!", new Uri(logoUrl), VoipCallMedia.Audio, TimeSpan.FromMinutes(5), out callObj); callObj.AnswerRequested += (s, e) => { s.NotifyCallActive(); // // : // , // managed code, NotifyCallActive // , // , await Task.Delay(3000); s.NotifyCallEnded(); }; callObj.RejectRequested += (s, e) => s.NotifyCallEnded(); }
VoIPプッシュは、他のすべてのタイプとは異なり、開いているアプリケーションと閉じられている場合の両方に到達できることに注意してください-Skypeは、現在フォアグラウンドにある場合でもプッシュ経由でのみ着信を受け入れます-実際には物議を醸す決定.k。 VoIPプッシュは時々遅くなります。 残念ながら、この例では、アプリケーションの起動時にvoip pushが到着した場合、会話を上げることができません。この例には、メインプロセスにこれを通知するネイティブプロセス間コンポーネントがありません(そして、はい、OnNavigatedTo、Fromは着信UIが表示されたときに機能しません、フレームでObscuredイベントを呼び出すことは可能かもしれませんが、発信者の番号を取得することはできません)-したがって、私の例では、ホストは会話を正しくピックアップするために電話をかけるときにアプリケーションを終了する必要があります。
おわりに
これで、1日で簡単なVoIPアプリケーションを作成できました。 残念ながら、スピーカーを介してのみ話すことができ、耳に持ってきたときに画面をオフにすることはできず(近接センサー)、アプリケーションが最小化されている場合は会話を続行できません-これにはネイティブコンポーネントが必要です。これはMicrosoft Chatterboxの例で詳細に説明されています-私の例はより簡単です、しかし、サーバー側で。 当初は、VoIPプッシュについてのみ説明したかったのですが、もう少しわかりました。 もちろん、本格的なVoIPアプリケーションを実装するには、急速に開発されているWebRTCに目を向けたほうがよいでしょう。これは、Androidのchromeで既に公式に動作していますが、うまくいけば、私の例が誰かに役立つことを願っています。
ソース: