在嵌入式设备中实现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 \n", 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 \n", 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:\n" << 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:\n" << 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:\n" << 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\n", dc->name, isBinary, msgLen);
110   if(!isBinary) {
111     printf("\t%.*s\n", 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\n", frame->size);
190 }
191 
192 int main() {
193   //printf("STATUS_ICE_CANDIDATE_INIT_MALFORMED %lu\n", 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

 

  最后我把所有代码都上传上来供大家参考(代码是很早以前写的,那时候我甚至还不会c++,所以写的比较乱,大家担待)

  代码包含了一个嵌入式的CMake项目(需配合一些h264/alaw/opus等资源文件,可以自己提供,也可用kvs附带的)和tomcat信令服务器。

  (注意,此次提供的代码中.a文件并非海思芯片库,而是另一款soc,基于linaro linux的aarch64版本)

 

  最后修改时间 2022-09-02 18:12:22

posted @ 2020-11-09 11:36  云中双月  阅读(2815)  评论(9编辑  收藏  举报