• 在嵌入式设备中实现webrtc的第三种方式③


      本系列的最后一篇,讲解收发音视频数据。

      贴出最终效果:

      

      其实很简单,直接调用writeFrame即可,如下图:

      

       当然,这是部分代码,完整代码在下面,展开可见:

      1 #include "com/amazonaws/kinesis/video/webrtcclient/Include.h"
      2 #include "json/json.h"
      3 
      4 #include "ws_client.h"
      5 
      6 #include <iostream>
      7 #include <thread>
      8 
      9 static PRtcPeerConnection peer_;
     10 static PRtcDataChannel dc_;
     11 static PRtcRtpTransceiver videoTransceiver_;
     12 static PRtcRtpTransceiver audioTransceiver_;
     13 static WSClient *ws = nullptr;
     14 
     15 STATUS readFrameFromDisk(PBYTE pFrame, PUINT32 pSize, PCHAR frameFilePath)
     16 {
     17     STATUS retStatus = STATUS_SUCCESS;
     18     UINT64 size = 0;
     19 
     20     if (pSize == NULL) {
     21         printf("[KVS Master] readFrameFromDisk(): operation returned status code: 0x%08x 
    ", STATUS_NULL_ARG);
     22         goto CleanUp;
     23     }
     24 
     25     size = *pSize;
     26 
     27     // Get the size and read into frame
     28     retStatus = readFile(frameFilePath, TRUE, pFrame, &size);
     29     if (retStatus != STATUS_SUCCESS) {
     30         printf("[KVS Master] readFile(): operation returned status code: 0x%08x 
    ", retStatus);
     31         goto CleanUp;
     32     }
     33 
     34 CleanUp:
     35 
     36     if (pSize != NULL) {
     37         *pSize = (UINT32) size;
     38     }
     39 
     40     return retStatus;
     41 }
     42 
     43 static void onWsData(const std::string& data, void* usrParam) {
     44   Json::Reader reader;
     45   Json::Value root;
     46   if (reader.parse(data, root)) {
     47     if(root["type"] == "answer") {
     48       RtcSessionDescriptionInit answer;
     49       Json::FastWriter writer;
     50       writer.omitEndingLineFeed();
     51       std::string _sdp_json_answer = writer.write(root);
     52       int ret = deserializeSessionDescriptionInit((PCHAR)_sdp_json_answer.data(), _sdp_json_answer.size(), &answer);
     53       std::cout << "deserializeSessionDescriptionInit:" << std::hex << ret << std::endl;
     54       std::cout << "answer:
    " << answer.sdp << std::endl;
     55       if(ret == 0) {
     56         std::cout << "setRemoteDescription:" << std::hex << setRemoteDescription(peer_, &answer) << std::endl;
     57       }
     58     } else if (root["type"] == "candidate") {
     59       if(!root["candidate"].asString().empty()) {
     60         //std::cout << "addIceCandidate:" << addIceCandidate(peer_, "") << std::endl;
     61       //} else{
     62         RtcIceCandidateInit ice;
     63         Json::FastWriter writer;
     64         writer.omitEndingLineFeed();
     65         std::string _ice_json = writer.write(root);
     66         int ret = deserializeRtcIceCandidateInit((PCHAR)_ice_json.data(), _ice_json.size(), &ice);
     67         std::cout << "deserializeRtcIceCandidateInit:" << std::hex << ret << std::endl;
     68         if(ret == 0) {
     69           std::cout << "remote-ice:" << ice.candidate << std::endl;
     70           std::cout << "addIceCandidate:" << std::hex << addIceCandidate(peer_, ice.candidate) << std::endl;
     71         }
     72       }
     73     } else if(root["type"] == "hello") {
     74 
     75       RtcSessionDescriptionInit offer;
     76       std::cout << "createOffer:" << std::hex << createOffer(peer_, &offer) << std::endl;
     77       std::cout << "offer:
    " << offer.sdp << std::endl;
     78 
     79       std::cout << "setLocalDescription:" << std::hex << setLocalDescription(peer_, &offer) << std::endl;
     80 
     81       char sdp_json[10240] = {0};
     82       unsigned int sdp_json_buf_len = sizeof sdp_json;
     83       std::cout << "serializeSessionDescriptionInit:" << std::hex << serializeSessionDescriptionInit(&offer, sdp_json, &sdp_json_buf_len) << std::endl;
     84       std::cout << "sdp_json_offer:
    " << sdp_json << std::endl;
     85 
     86       ws->Send(sdp_json);
     87     }
     88   } else {
     89     std::cout << "Json parse failed" << std::endl;
     90   }
     91 }
     92 
     93 void func_RtcOnIceCandidate(UINT64 custmerData, PCHAR iceStr) {
     94   if(iceStr != nullptr && strlen(iceStr) > 0) {
     95     std::cout << "local-ice:" << iceStr << std::endl;
     96     Json::Value ice;
     97     Json::Reader reader;
     98     if(reader.parse(iceStr, ice)) {
     99       ice["type"] = "candidate";
    100       Json::FastWriter writer;
    101       writer.omitEndingLineFeed();
    102       std::string _ice_json = writer.write(ice);
    103       ws->Send(_ice_json);
    104     }
    105   }
    106 }
    107 
    108 void func_RtcOnMessage(UINT64 customData, PRtcDataChannel dc, BOOL isBinary, PBYTE msgData, UINT32 msgLen) {
    109   printf("func_RtcOnMessage dc_label=%s isBinary=%d msgLen=%d
    ", dc->name, isBinary, msgLen);
    110   if(!isBinary) {
    111     printf("	%.*s
    ", msgLen, msgData);
    112   }
    113 }
    114 
    115 void func_RtcOnConnectionStateChange(UINT64 customData, RTC_PEER_CONNECTION_STATE state) {
    116   if(state == RTC_PEER_CONNECTION_STATE::RTC_PEER_CONNECTION_STATE_CONNECTED) {
    117     RtcDataChannelInit dcInit;
    118     memset(&dcInit, 0, sizeof dcInit);
    119     dcInit.ordered = TRUE;
    120     std::cout << "createDataChannel:" << std::hex << createDataChannel(peer_, (PCHAR)"op", &dcInit, &dc_) << std::endl;
    121     std::cout << "dataChannelOnMessage:" << std::hex << dataChannelOnMessage(dc_, 0, func_RtcOnMessage) << std::endl;
    122     std::thread([]{
    123       int vidx = 6;
    124       char vfn[256] = {0};
    125       Frame frame = {0};
    126       char vbuffer[100 * 1024];
    127       frame.frameData = (PBYTE)vbuffer;
    128       STATUS retStatus = STATUS_SUCCESS;
    129       UINT64 startTime, lastFrameTime, elapsed;
    130       startTime = GETTIME();
    131       lastFrameTime = startTime;
    132       while(true) {
    133         sprintf(vfn, "/data_fs/h264s/%d.h264", vidx++);
    134         frame.size = sizeof vbuffer;
    135         retStatus = readFrameFromDisk(frame.frameData, &frame.size, vfn);
    136         if(retStatus != STATUS_SUCCESS) {
    137           std::cout << "readFrameFromDisk:" << std::hex << retStatus << std::endl;
    138           break;
    139         }
    140         frame.presentationTs += (((10LL * 1000LL) * 1000LL) / 25);
    141         retStatus = writeFrame(videoTransceiver_, &frame);
    142         if(retStatus != STATUS_SUCCESS) {
    143           if(retStatus == STATUS_SRTP_NOT_READY_YET) {
    144             //std::cout << "writeFrame(video):" << std::hex << retStatus << std::endl;
    145             vidx--;
    146           } else{
    147             std::cout << "writeFrame(video):" << std::hex << retStatus << std::endl;
    148             break;
    149           }
    150         }
    151         elapsed = lastFrameTime - startTime;
    152         std::this_thread::sleep_for(std::chrono::nanoseconds((((10LL * 1000LL) * 1000LL) / 25) - elapsed % (((10LL * 1000LL) * 1000LL) / 25)));
    153         lastFrameTime = GETTIME();
    154       }
    155     }).detach();
    156     std::thread([]{
    157       int aidx = 1;
    158       char afn[256] = {0};
    159       Frame frame = {0};
    160       char abuffer[100 * 1024];
    161       frame.frameData = (PBYTE)abuffer;
    162       STATUS retStatus = STATUS_SUCCESS;
    163       while(true) {
    164         sprintf(afn, "/data_fs/pcms/%d.pcma", aidx++);
    165         frame.size = sizeof abuffer;
    166         retStatus = readFrameFromDisk(frame.frameData, &frame.size, afn);
    167         if(retStatus != STATUS_SUCCESS) {
    168           std::cout << "readFrameFromDisk:" << std::hex << retStatus << std::endl;
    169           break;
    170         }
    171         frame.presentationTs += (20 * (10LL * 1000LL));
    172         retStatus = writeFrame(audioTransceiver_, &frame);
    173         if(retStatus != STATUS_SUCCESS) {
    174           if(retStatus == STATUS_SRTP_NOT_READY_YET) {
    175             //std::cout << "writeFrame(audio):" << std::hex << retStatus << std::endl;
    176             aidx--;
    177           } else{
    178             std::cout << "writeFrame(audio):" << std::hex << retStatus << std::endl;
    179             break;
    180           }
    181         }
    182         std::this_thread::sleep_for(std::chrono::nanoseconds((20 * (10LL * 1000LL))));
    183       }
    184     }).detach();
    185   }
    186 }
    187 
    188 void func_RtcOnFrame(UINT64 customData, PFrame frame) {
    189   printf("func_RtcOnFrame(audio) size=%d
    ", frame->size);
    190 }
    191 
    192 int main() {
    193   //printf("STATUS_ICE_CANDIDATE_INIT_MALFORMED %lu
    ", STATUS_ICE_CANDIDATE_INIT_MALFORMED);
    194 
    195   char wsUrl[64];  
    196   sprintf(wsUrl, "ws://192.168.0.44:8080/webrtc/channel/01239F-C8ADE4-630DEE");
    197   ws = new WSClient(wsUrl);
    198 
    199   ws->SetDataCb(onWsData, nullptr);
    200   if(!ws->Start()) return -1;
    201 
    202   initKvsWebRtc();
    203 
    204   RtcConfiguration conf_;
    205   memset(&conf_, 0, sizeof conf_);
    206   conf_.iceTransportPolicy = ICE_TRANSPORT_POLICY::ICE_TRANSPORT_POLICY_ALL;
    207   //conf_.kvsRtcConfiguration.generatedCertificateBits = 2048;
    208   conf_.kvsRtcConfiguration.generateRSACertificate = 1;
    209   strcpy(conf_.iceServers[0].urls, "stun:77.72.169.213:3478");
    210   std::cout << "createPeerConnection:" << std::hex << createPeerConnection(&conf_, &peer_) << std::endl;
    211 
    212   std::cout << "peerConnectionOnIceCandidate:" << std::hex << peerConnectionOnIceCandidate(peer_, 0, func_RtcOnIceCandidate) << std::endl;
    213   std::cout << "peerConnectionOnConnectionStateChange:" << std::hex << peerConnectionOnConnectionStateChange(peer_, 0, func_RtcOnConnectionStateChange) << std::endl;
    214 
    215   PRtcMediaStreamTrack video_track_ = new RtcMediaStreamTrack;
    216   memset(video_track_, 0, sizeof *video_track_);
    217   video_track_->codec = RTC_CODEC::RTC_CODEC_H264_PROFILE_42E01F_LEVEL_ASYMMETRY_ALLOWED_PACKETIZATION_MODE;
    218   video_track_->kind = MEDIA_STREAM_TRACK_KIND::MEDIA_STREAM_TRACK_KIND_VIDEO;
    219   strcpy(video_track_->streamId, "0");
    220   strcpy(video_track_->trackId, "0");
    221   RtcRtpTransceiverInit rtcRtpTransceiverInit_v_;
    222   rtcRtpTransceiverInit_v_.direction = RTC_RTP_TRANSCEIVER_DIRECTION::RTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY;
    223   std::cout << "addTransceiver:" << std::hex << addTransceiver(peer_, video_track_, &rtcRtpTransceiverInit_v_, &videoTransceiver_) << std::endl;
    224 
    225   PRtcMediaStreamTrack audio_track_ = new RtcMediaStreamTrack;
    226   memset(audio_track_, 0, sizeof *audio_track_);
    227   audio_track_->codec = RTC_CODEC::RTC_CODEC_ALAW;
    228   audio_track_->kind = MEDIA_STREAM_TRACK_KIND::MEDIA_STREAM_TRACK_KIND_AUDIO;
    229   strcpy(audio_track_->streamId, "1");
    230   strcpy(audio_track_->trackId, "1");
    231   RtcRtpTransceiverInit rtcRtpTransceiverInit_a_;
    232   rtcRtpTransceiverInit_a_.direction = RTC_RTP_TRANSCEIVER_DIRECTION::RTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV;
    233   std::cout << "addTransceiver:" << std::hex << addTransceiver(peer_, audio_track_, &rtcRtpTransceiverInit_a_, &audioTransceiver_) << std::endl;
    234   std::cout << "transceiverOnFrame:" << std::hex << transceiverOnFrame(audioTransceiver_, 0, func_RtcOnFrame) << std::endl;
    235 
    236   while(true) {
    237     std::this_thread::sleep_for(std::chrono::seconds(1));
    238   }
    239   deinitKvsWebRtc();
    240 
    241   return -1;
    242 }
    View Code

      由于上一篇文章已经把项目发出来了,因此这里就没有再发,这个文件替换即可。

      (发h264时,就是每个nal发一次,当然了,一般I帧前头带着SPS/PPS,好像行业内的编码器都是这样,是可以的。pcma的话就是160一段;我是8K的,50Hz)

      收数据就是transceiverOnFrame注册的回调,代码中也有,但我暂时没有从h5获取,因为我电脑也没有mic。

      资源文件如下:

      

      (pcm我没录到,因为录音设备没有mic,这边算是一个互动,大家自己动手试试)

      

       

      That's all.

      

      有高手希望指点的话可以通过微信与我联系,我的id是wxid_8r2mjkbcu2an22

       最后修改时间 2020-11-09 11:30:08

  • 相关阅读:
    FirewallD 详解
    EventBus源码解析
    详谈 Jquery Ajax 异步处理Json数据.
    jQuery Ajax异步处理Json数据详解
    7款jQuery图片轮播滑动插件
    分享15款为jQuery Mobile定制的插件
    在springmvc中解决FastJson循环引用的问题
    SpringMVC与fastjson整合并同时解决中文乱码问题
    SpringMVC处理ajax请求
    spring mvc 返回json的配置
  • 原文地址:https://www.cnblogs.com/Johness/p/implement-webrtc-in-embedded-system-sec-3.html
Copyright © 2020-2023  润新知