- JS 側のインタフェース設計をどうするか?
- iOS/Android の DataChannel のインタフェースを調査する
- そもそも libwebrtc の機能的に iOS/Android の間で差異があるか?
https://developer.mozilla.org/ja/docs/Web/API/RTCDataChannel
- createDataChannel()
- https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/createDataChannel
- ピアに新たに datachannel を作成する
- ondatachannel
- https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/ondatachannel
- リモートのピアに datachannel が追加されたときに発火する
- label
- ordered
- protocol
- id
- readyState
- bufferedAmount
- binaryType
- maxPacketLifeType
- maxRetransmits
- negotiated
- reliable (dublicated)
- stream (duplicated)
https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannelEvent
- onopen
- onmessage
- onclose
- onerror
- close()
- send()
- DOMString (だいたい String), Blob, ArrayBuffer, ArrayBufferView を送信することができる
public DataChannel createDataChannel(String label, DataChannel.Init init) {
return nativeCreateDataChannel(label, init);
}
createDataChannel 相当のメソッドが実装されている
public class DataChannel {
/** Java wrapper for WebIDL RTCDataChannel. */
public static class Init {
public boolean ordered = true;
// Optional unsigned short in WebIDL, -1 means unspecified.
public int maxRetransmitTimeMs = -1;
// Optional unsigned short in WebIDL, -1 means unspecified.
public int maxRetransmits = -1;
public String protocol = "";
public boolean negotiated;
// Optional unsigned short in WebIDL, -1 means unspecified.
public int id = -1;
@CalledByNative("Init")
boolean getOrdered() {
return ordered;
}
@CalledByNative("Init")
int getMaxRetransmitTimeMs() {
return maxRetransmitTimeMs;
}
@CalledByNative("Init")
int getMaxRetransmits() {
return maxRetransmits;
}
@CalledByNative("Init")
String getProtocol() {
return protocol;
}
@CalledByNative("Init")
boolean getNegotiated() {
return negotiated;
}
@CalledByNative("Init")
int getId() {
return id;
}
}
DataChannel.Init クラス。こちらを configuration 的にcreateDataChannel に渡して datachannel を作成できる。
/** Java version of C++ DataChannelObserver. */
public interface Observer {
/** The data channel's bufferedAmount has changed. */
@CalledByNative("Observer") public void onBufferedAmountChange(long previousAmount);
/** The data channel state has changed. */
@CalledByNative("Observer") public void onStateChange();
/**
* A data buffer was successfully received. NOTE: |buffer.data| will be
* freed once this function returns so callers who want to use the data
* asynchronously must make sure to copy it first.
*/
@CalledByNative("Observer") public void onMessage(Buffer buffer);
}
....
/** Send |data| to the remote peer; return success. */
public boolean send(Buffer buffer) {
checkDataChannelExists();
// TODO(fischman): this could be cleverer about avoiding copies if the
// ByteBuffer is direct and/or is backed by an array.
byte[] data = new byte[buffer.data.remaining()];
buffer.data.get(data);
return nativeSend(data, buffer.binary);
}
こちらの send 関数の Buffer に boolean binary プロパティが付与されていて、こちらで
binaryType については binary
パラメータによって UTF-8 or binary data
が選択できる模様。
@implementation RTCPeerConnection (DataChannel)
- (nullable RTCDataChannel *)dataChannelForLabel:(NSString *)label
configuration:(RTCDataChannelConfiguration *)configuration {
std::string labelString = [NSString stdStringForString:label];
const webrtc::DataChannelInit nativeInit =
configuration.nativeDataChannelInit;
rtc::scoped_refptr<webrtc::DataChannelInterface> dataChannel =
self.nativePeerConnection->CreateDataChannel(labelString,
&nativeInit);
if (!dataChannel) {
return nil;
}
return [[RTCDataChannel alloc] initWithFactory:self.factory nativeDataChannel:dataChannel];
}
@end
dataChannelForLabel
が pc.createDataChannel()
相当のメソッドとなる。
class DataChannelDelegateAdapter : public DataChannelObserver {
public:
DataChannelDelegateAdapter(RTCDataChannel *channel) { channel_ = channel; }
void OnStateChange() override {
[channel_.delegate dataChannelDidChangeState:channel_];
}
void OnMessage(const DataBuffer& buffer) override {
RTCDataBuffer *data_buffer =
[[RTCDataBuffer alloc] initWithNativeBuffer:buffer];
[channel_.delegate dataChannel:channel_
didReceiveMessageWithBuffer:data_buffer];
}
void OnBufferedAmountChange(uint64_t previousAmount) override {
id<RTCDataChannelDelegate> delegate = channel_.delegate;
SEL sel = @selector(dataChannel:didChangeBufferedAmount:);
if ([delegate respondsToSelector:sel]) {
[delegate dataChannel:channel_ didChangeBufferedAmount:previousAmount];
}
}
onmessage, onstatechange(close やら open やら error の状態変更), onbufferedamountchange などのイベントを受け取れそう
各種 configration(isOrdered) などは↑の RTCDataChannelConfiguration クラスで定義してある。
こちらも binaryType については isBinary
パラメータによって UTF-8 or binary data
が選択できる模様
react-native-webrtc-kit の WebRTCPeerConnectionObserver.java
に以下のようなコードがある。
@Override
public void onDataChannel(DataChannel dataChannel) {
// DataChannel は現在対応しない
}
ここで peer.ondatachannel
相当のイベントを受け取ることができる。
RTCPeerConnection.m に以下のようなコードがある
- (void)peerConnection:(RTCPeerConnection*)peerConnection didOpenDataChannel:(RTCDataChannel*)dataChannel {
// DataChannel は現在対応しない
}
ここで peer.ondatachannel
相当のイベントを受け取ることができる。
以上を踏まえて、JS / Native 側に必要な実装事項をまとめると
-
RTCDataChannel
クラスを追加する -
datachannel.onstatechange, datachannel.onmessage datachannel.onbufferedamountchange
コールバックを実装する- datachannel のイベントハンドラを実装する
-
peerConnection.createDataChannel()
メソッドを追加する- 新たに datachannel を作成する関数
- 引数は label, options にすると仮定すると
- options で isBinary, isOrderd, MaxPacketLifeTime, MaxRetransmits などを設定できるようにする必要がある
- 引数は label, options にすると仮定すると
- 新たに datachannel を作成する関数
-
peerConnection.ondatachannel()
イベントハンドラを追加する- リモートに datachannel が新規に作成されたときのコールバック
ネイティブ から ondatachannel を JS に受け渡す例として
- (void)peerConnection:(RTCPeerConnection*)peerConnection didOpenDataChannel:(RTCDataChannel*)dataChannel {
// DataChannel は現在対応しない
}
に
- (void)peerConnection:(RTCPeerConnection*)peerConnection didOpenDataChannel:(RTCDataChannel*)dataChannel {
[self.bridge.eventDispatcher sendDeviceEventWithName:@"peerConnectionGotDataChannel"
body:@{@"valueTag": peerConnection.valueTag,
@"datachannel": @{
@"label": ... // 色々情報を付与する
}
}
}
のような実装が考えられる。
Hi @kdxu
This info is really helpful, Thank you!
I also need one more suggestion, currently I'm working on cross platform VideoChat application and I need to connect ios client with JS master (ios user will be viewer and master will be opening the app inn browser). All other events are triggering properly like didDataChannelOpen and all, but onMessage implementation on ios is not triggering, irrespective of what dataType I pass. In the library, the listener for onmessage expects the data to be in RTCBufferData but I'm unable to send a message of that type from website. Can you please help me with this?