【WebRTC研究(3)】WebRTC接入IPC——數據源是YUV格式

本文基於peerconnection_client改動,新增FakeCapture類,繼承於VideoCaptureImpl, 通過海康SDK獲取YUV數據,調用IncomingFrame接口,自動進入WebRTC的編碼和發送模塊。實現推送IPC實時視頻的功能。

代碼修改

新增fake_capture_ipc.h文件

在peerconnection_client工程新增fake_capture_ipc.h文件

namespace webrtc {
namespace videocapturemodule {
class FakeCapture : public VideoCaptureImpl
{
public:
  FakeCapture();
  virtual ~FakeCapture();

  virtual int32_t Init(const char* deviceUniqueIdUTF8);

  /*************************************************************************
   *
   *   Start/Stop
   *
   *************************************************************************/
  int32_t StartCapture(const VideoCaptureCapability& capability) override;
  int32_t StopCapture() override;

  /**************************************************************************
   *
   *   Properties of the set device
   *
   **************************************************************************/

  bool CaptureStarted() override;
  int32_t CaptureSettings(VideoCaptureCapability& settings) override;

}
} // namespace videocapturemodule
} // namespace webrtc

新增文件需要修改ninja文件才能編譯通過

  • include_dirs 頭文件目錄,類似於VS附加包含目錄
  • build 參考其他文件,依次添加即可

修改video_capture_factory_windows.cc文件

rtc::scoped_refptr<VideoCaptureModule> VideoCaptureImpl::Create(
    const char* device_id) {
  if (device_id == nullptr)
    return nullptr;

    //註釋原有代碼
  // TODO(tommi): Use Media Foundation implementation for Vista and up.
//   rtc::scoped_refptr<VideoCaptureDS> capture(
//       new rtc::RefCountedObject<VideoCaptureDS>());
//   if (capture->Init(device_id) != 0) {
//     return nullptr;
//   }

  // 新增
    rtc::scoped_refptr<FakeCapture> capture(
      new rtc::RefCountedObject<FakeCapture>());
    if (capture->Init(device_id) != 0) {
      return nullptr;
    }

    return capture;
}

修改vcm_capture.cc文件

bool VcmCapturer::Init(size_t width,
                       size_t height,
                       size_t target_fps,
                       size_t capture_device_index) {
  /*************************************************************************
   *   省略部分代碼
   *************************************************************************/
  vcm_->RegisterCaptureDataCallback(this);

  // chb
//   device_info->GetCapability(vcm_->CurrentDeviceName(), 0, capability_);
//   capability_.width = static_cast<int32_t>(width);
//   capability_.height = static_cast<int32_t>(height);
//   capability_.maxFPS =  static_cast<int32_t>(target_fps);
//   capability_.videoType = VideoType::kI420;

    //這裏暫時手動修改成IPC的視頻信息,
    //正式使用需要自己實現DeviceInfo類,通過SDK自動獲取視頻信息
  capability_.width = 2560;//static_cast<int32_t>(width);
  capability_.height = 1920;//static_cast<int32_t>(height);
  capability_.maxFPS = 20;    // static_cast<int32_t>(target_fps);
  capability_.videoType = VideoType::kYV12;//VideoType::kI420;

  if (vcm_->StartCapture(capability_) != 0) {
    Destroy();
    return false;
  }

  RTC_CHECK(vcm_->CaptureStarted());

  return true;
}

WebRTC添加第三方lib庫

參考webrtc添加第三方庫

我使用文章裏面的方法三,如下:

在相關的h或者cpp中#pragma comment(lib, "./*.lib")

裏面的方法四失敗了,不知道什麼原因,沒有繼續研究。

驗證

通過web頁面進行測試,測試代碼如下:

<html>
<head>
<title>PeerConnection server test page</title>
</head>
<script>
    var request = null;
    var hangingGet = null;
    var localName;
    var server;
    var myId = -1;
    var otherPeers = {};
    var messageCounter = 0;
    var pc;    
    var pcConfig = {"iceServers": [{"url": "stun:stun.l.google.com:19302"}]};
    var pcOptions = {
        optional: [
            {DtlsSrtpKeyAgreement: true}
        ]
    }
    var mediaConstraints = {'mandatory': {
        'OfferToReceiveAudio': true,
        'OfferToReceiveVideo': true }};
    var remoteStream;
    
    RTCPeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
    RTCSessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription;
    RTCIceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate;
    getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
    URL = window.webkitURL || window.URL;
    
    function createPeerConnection(peer_id) {
        try {
            pc = new RTCPeerConnection(pcConfig, pcOptions);
            pc.onicecandidate = function(event) {
                if (event.candidate) {
                    var candidate = {
                        sdpMLineIndex: event.candidate.sdpMLineIndex,
                        sdpMid: event.candidate.sdpMid,
                        candidate: event.candidate.candidate
                    };
                    sendToPeer(peer_id, JSON.stringify(candidate));
                } else {
                  console.log("End of candidates.");
                }
            };
            pc.onconnecting = onSessionConnecting;
            pc.onopen = onSessionOpened;
            pc.onaddstream = onRemoteStreamAdded;
            pc.onremovestream = onRemoteStreamRemoved;
            console.log("Created RTCPeerConnnection with config: " + JSON.stringify(pcConfig));
        } 
        catch (e) {
            console.log("Failed to create PeerConnection with " + connectionId + ", exception: " + e.message);
        }
    }
    

    function onRemoteStreamAdded(event) {
      console.log("Remote stream added:", URL.createObjectURL(event.stream));
      var remoteVideoElement = document.getElementById('remote-video');
      remoteVideoElement.src = URL.createObjectURL(event.stream);
      remoteVideoElement.play();
    }

    function sld_success_cb() {
    }

    function sld_failure_cb() {
      console.log("setLocalDescription failed");
    }

    function aic_success_cb() {
    }

    function aic_failure_cb() {
      console.log("addIceCandidate failed");
    }

    
    function handlePeerMessage(peer_id, data) {
        ++messageCounter;
        var str = "Message from '" + otherPeers[peer_id] + ":" + data;
        trace(str);
        
        var dataJson = JSON.parse(data);
        console.log("received ", dataJson);
        if (data.search("offer") != -1) {
            createPeerConnection(peer_id);
            pc.setRemoteDescription(new RTCSessionDescription(dataJson), onRemoteSdpSucces, onRemoteSdpError);              
            pc.createAnswer(function(sessionDescription) {
                console.log("Create answer:", sessionDescription);
                pc.setLocalDescription(sessionDescription,sld_success_cb,sld_failure_cb);
                var data = JSON.stringify(sessionDescription);
                sendToPeer(peer_id, data);
            }, function(error) { // error
                console.log("Create answer error:", error);
            }, mediaConstraints); // type error  ); //}, null          
        }
        else {
            console.log("Adding ICE candiate ", dataJson);
            var candidate = new RTCIceCandidate({sdpMLineIndex: dataJson.sdpMLineIndex, candidate: dataJson.candidate});
            pc.addIceCandidate(candidate, aic_success_cb, aic_failure_cb);
        }
    }    
    
    function trace(txt) {
        var elem = document.getElementById("debug");
        elem.innerHTML += txt + "<br>";
    }
    
    function handleServerNotification(data) {
        trace("Server notification: " + data);
        var parsed = data.split(',');
        if (parseInt(parsed[2]) != 0)
            otherPeers[parseInt(parsed[1])] = parsed[0];
    }
    
    function parseIntHeader(r, name) {
        var val = r.getResponseHeader(name);
        return val != null && val.length ? parseInt(val) : -1;
    }
    
    function hangingGetCallback() {
        try {
            if (hangingGet.readyState != 4)
                return;
            if (hangingGet.status != 200) {
                trace("server error: " + hangingGet.statusText);
                disconnect();
            } else {
                var peer_id = parseIntHeader(hangingGet, "Pragma");
                console.log("Message from:", peer_id, ':', hangingGet.responseText);
                if (peer_id == myId) {
                  handleServerNotification(hangingGet.responseText);
                } else {
                  handlePeerMessage(peer_id, hangingGet.responseText);
                }
            }

            if (hangingGet) {
                hangingGet.abort();
                hangingGet = null;
            }

            if (myId != -1)
                window.setTimeout(startHangingGet, 0);
      } catch (e) {
          trace("Hanging get error: " + e.description);
      }
    }
    
    function startHangingGet() {
        try {
            hangingGet = new XMLHttpRequest();
            hangingGet.onreadystatechange = hangingGetCallback;
            hangingGet.ontimeout = onHangingGetTimeout;
            hangingGet.open("GET", server + "/wait?peer_id=" + myId, true);
            hangingGet.send();  
        } catch (e) {
            trace("error" + e.description);
        }
    }
    
    function onHangingGetTimeout() {
        trace("hanging get timeout. issuing again.");
        hangingGet.abort();
        hangingGet = null;
        if (myId != -1)
            window.setTimeout(startHangingGet, 0);
    }
    
    function signInCallback() {
        try {
            if (request.readyState == 4) {
                if (request.status == 200) {
                    var peers = request.responseText.split("\n");
                    myId = parseInt(peers[0].split(',')[1]);
                    trace("My id: " + myId);
                    for (var i = 1; i < peers.length; ++i) {
                        if (peers[i].length > 0) {
                            trace("Peer " + i + ": " + peers[i]);
                            var parsed = peers[i].split(',');
                            otherPeers[parseInt(parsed[1])] = parsed[0];
                        }
                    }
                    startHangingGet();
                    request = null;
                }
            }
        } catch (e) {
            trace("error: " + e.description);
        }
    }
    
    function signIn() {
      try {
          request = new XMLHttpRequest();
          request.onreadystatechange = signInCallback;
          request.open("GET", server + "/sign_in?" + localName, true);
          request.send();
      } catch (e) {
          trace("error: " + e.description);
      }
    }

    function dummy() {
    }
    
    function sendToPeer(peer_id, data) {
      try {
          console.log(peer_id," Send ", data);
          if (myId == -1) {
              alert("Not connected");
              return;
          }
          if (peer_id == myId) {
              alert("Can't send a message to oneself :)");
              return;
          }
          var r = new XMLHttpRequest();
          r.onreadystatechange = dummy
          r.open("POST", server + "/message?peer_id=" + myId + "&to=" + peer_id, true);
          r.setRequestHeader("Content-Type", "text/plain");
          r.send(data);
      } catch (e) {
          trace("send to peer error: " + e.description);
      }
    }
    
    function connect() {
        localName = document.getElementById("local").value.toLowerCase();
        server = document.getElementById("server").value.toLowerCase();
        if (localName.length == 0) {
            alert("I need a name please.");
            document.getElementById("local").focus();
        } else {
            document.getElementById("connect").disabled = true;
            document.getElementById("disconnect").disabled = false;
            signIn();
        }
    }
    
    function disconnect() {
        if (request) {
            request.abort();
            request = null;
        }
        
        if (hangingGet) {
            hangingGet.abort();
            hangingGet = null;
        }
      
        if (myId != -1) {
            request = new XMLHttpRequest();
            request.open("GET", server + "/sign_out?peer_id=" + myId, false);
            request.send();
            request = null;
            myId = -1;
        }
      
        document.getElementById("connect").disabled = false;
        document.getElementById("disconnect").disabled = true;
    }
    
    window.onbeforeunload = disconnect;
    
    function send() {
        var text = document.getElementById("message").value;
        var peer_id = parseInt(document.getElementById("peer_id").value);
        if (!text.length || peer_id == 0) {
            alert("No text supplied or invalid peer id");
        } else {
            sendToPeer(peer_id, text);
        }
    }
    
    function toggleMe(obj) {
        var id = obj.id.replace("toggle", "msg");
        var t = document.getElementById(id);
        if (obj.innerText == "+") {
            obj.innerText = "-";
            t.style.display = "block";
        } else {
            obj.innerText = "+";
            t.style.display = "none";
        }
    }
    
    function onSessionConnecting(message) {
        console.log("Session connecting.");
    }
    
    function onSessionOpened(message) {
        console.log("Session opened.");
    }
    
    function onRemoteStreamRemoved(event) {
        console.log("Remote stream removed.");
    }
    
    function onRemoteSdpError(event) {
        console.error('onRemoteSdpError', event.name, event.message);
    }
    
    function onRemoteSdpSucces() {
        console.log('onRemoteSdpSucces');
    } 
</script>
<body>
    <div id="container">
        <div id="remote">
            <video id="remote-video" width="50%" height="50%"></video>
        </div>
    </div>
    Server: <input type="text" id="server" value="http://192.168.28.198:8888" /><br>
    Your name: <input type="text" id="local" value="myclient"/>
    <button id="connect" onclick="connect();">Connect</button>
    <button disabled="true" id="disconnect" onclick="disconnect();">Disconnect</button>
    <br>
    <button onclick="document.getElementById('debug').innerHTML='';">Clear log</button>
    <pre id="debug"></pre>
    <br>
    <hr>
</body>
</html>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章