• 基于metaRTC嵌入式webrtc的H265网页播放器实现(我与metaRTC的缘分)


           目前100ms延迟已达成,暂不进一步开始,过程中还是感觉有点力不从心,还是多点积累再出发!我与meta RTC的缘分开始于实现H265网页播放的理想,搜遍全网,也只发现metaRTC实现了webrtc H265编码的发送,相信很多网友也是这个初衷,所以大家聚到了一起,也是这个机缘让我认识了一众大佬,很多资深的音视频开发大佬都藏身于metaRTC的群里,我给自己打开了一扇窗,见识了更广阔的世界。在了解metaRTC后,很长一段时间没有怎么实质的做什么研究工作,metaRTC更新也很快,很多基于ffmpeg的,我也不熟,中途只是埋头做自己的pion系列服务器软件(其中大佬开源m7s(langhuihui/monibuca: 🧩 Monibuca is a Modularized, Extensible framework for building Streaming Server (github.com))媒体服务器软件也给我极大的帮助),(期间做了一个kvs 的自研信令和flutterwebrtc客户端,这些都为我后来深入了解metaRTC打下了基础。在群里也就偶尔发一下言,潜水听大佬们讲一个一个的新概念和专业知识,受益良多,后来慢慢发现自己不能置身事外,正值杨大佬开始做metaRTC5.0稳定版,于是我开始跟进源码的运用,先后移植了自己以前基于kvs()做的信令系统,完善了datachannel传输,多peer管理等,并基于IPC应用做了一个基于RV1126嵌入式IPC音视频的硬编传输(详见metaRTC性能测试_superxxd的博客-CSDN博客),也感受了群主强大的研发能力,深受鼓舞。但一直的梦想H265 浏览器播放没有得到满足。

        在8月12日晚上,我在群里发言,希望做一个基于metaRTC的H265网页版的播放器,以下是当时的热闹的聊天信息

    6328e3fe694147d6ac9afe0c381eb97c.png14fdc6944e1c46889ddf8719bdf5db59.png

     50a01fd6463a438a8f60651c36f450a2.png

        从当时的发言,看得出来,我的确是一脸懵逼,甚至连wasm不能硬解都不清楚,只是听说过。但是因为我认为实现过程比实现本身的价值更高,于是就义无反顾地出发了,惯用套路,各种baidu,github,也发现了前辈大佬们做了相当多的工作,于是在他们的基础上开始干活,直接在别人项目上动手测试,先看看是怎么运作的,效果是什么样的,然后一点一点的根据自己的想法实现文件传输,解码播放,测试、性能优化。中间各种花屏,解码不成功,不出图。让我都想要放弃。但事实证明实现的过程比实现本身更有价值,过程会让你将书本知识变成自己的理解,融入自己的知识体系。现在终于实现了一版好于我预期的播放器。初始源码开源在如下地址https://github.com/xiangxud/webrtc_H265player,欢迎大家star,fork 和提issue pr

       我先在我的go写的项目里面验证我的想法,于是写了datachannel h265视频编码发送的函数,并实现了帧的解析

    1. const (
    2. //H265
    3. // https://zhuanlan.zhihu.com/p/458497037
    4. NALU_H265_VPS = 0x4001
    5. NALU_H265_SPS = 0x4201
    6. NALU_H265_PPS = 0x4401
    7. NALU_H265_SEI = 0x4e01
    8. NALU_H265_IFRAME = 0x2601
    9. NALU_H265_PFRAME = 0x0201
    10. HEVC_NAL_TRAIL_N = 0
    11. HEVC_NAL_TRAIL_R = 1
    12. HEVC_NAL_TSA_N = 2
    13. HEVC_NAL_TSA_R = 3
    14. HEVC_NAL_STSA_N = 4
    15. HEVC_NAL_STSA_R = 5
    16. HEVC_NAL_BLA_W_LP = 16
    17. HEVC_NAL_BLA_W_RADL = 17
    18. HEVC_NAL_BLA_N_LP = 18
    19. HEVC_NAL_IDR_W_RADL = 19
    20. HEVC_NAL_IDR_N_LP = 20
    21. HEVC_NAL_CRA_NUT = 21
    22. HEVC_NAL_RADL_N = 6
    23. HEVC_NAL_RADL_R = 7
    24. HEVC_NAL_RASL_N = 8
    25. HEVC_NAL_RASL_R = 9
    26. )
    27. // int type = (NALU头第一字节 & 0x7E) >> 1
    28. // hvcC extradata是一种头描述的格式。而annex-b格式中,则是将VPS, SPS和PPS等同于普通NAL,用start code分隔,非常简单。Annex-B格式的”extradata”:
    29. // start code+VPS+start code+SPS+start code+PPS
    30. //格式详情参见以下博客
    31. // 作者:一川烟草i蓑衣
    32. // 链接:https://www.jianshu.com/p/909071e8f8c6
    33. func GetFrameTypeName(frametype uint16) (string, error) {
    34. switch frametype {
    35. case NALU_H265_VPS:
    36. return "H265_FRAME_VPS", nil
    37. case NALU_H265_SPS:
    38. return "H265_FRAME_SPS", nil
    39. case NALU_H265_PPS:
    40. return "H265_FRAME_PPS", nil
    41. case NALU_H265_SEI:
    42. return "H265_FRAME_SEI", nil
    43. case NALU_H265_IFRAME:
    44. return "H265_FRAME_I", nil
    45. case NALU_H265_PFRAME:
    46. return "H265_FRAME_P", nil
    47. default:
    48. return "", errors.New("frametype unsupport")
    49. }
    50. }
    51. func FindStartCode2(Buf []byte) bool {
    52. if Buf[0] != 0 || Buf[1] != 0 || Buf[2] != 1 {
    53. return false //判断是否为0x000001,如果是返回1
    54. } else {
    55. return true
    56. }
    57. }
    58. func FindStartCode3(Buf []byte) bool {
    59. if Buf[0] != 0 || Buf[1] != 0 || Buf[2] != 0 || Buf[3] != 1 {
    60. return false //判断是否为0x00000001,如果是返回1
    61. } else {
    62. return true
    63. }
    64. }
    65. func GetFrameType(pdata []byte) (uint8, uint16, error) {
    66. var frametype uint16
    67. destcount := 0
    68. // naluendptr := 0
    69. if FindStartCode2(pdata) {
    70. destcount = 3
    71. } else if FindStartCode3(pdata) {
    72. destcount = 4
    73. } else {
    74. return 0, 0, errors.New("not find")
    75. }
    76. temptype := (pdata[destcount] & 0x7E) >> 1
    77. bytesBuffer := bytes.NewBuffer(pdata[destcount : destcount+2])
    78. binary.Read(bytesBuffer, binary.BigEndian, &frametype)
    79. fmt.Printf("temptype :%02x type is 0x%04x", temptype, frametype)
    80. return temptype, frametype, nil
    81. }
    1. func H265DataChannelHandler(dc *webrtc.DataChannel, mediatype string) {
    2. fmt.Printf("H265DataChannelHandler\n")
    3. dc.OnOpen(func() {
    4. nInSendH265Track++
    5. fmt.Printf("dc.OnOpen %d\n", nInSendH265Track)
    6. sendH265ImportFrame(dc, utils.NALU_H265_SEI)
    7. sendH265ImportFrame(dc, utils.NALU_H265_VPS)
    8. sendH265ImportFrame(dc, utils.NALU_H265_SPS)
    9. sendH265ImportFrame(dc, utils.NALU_H265_PPS)
    10. sendH265ImportFrame(dc, utils.NALU_H265_IFRAME)
    11. if nInSendH265Track <= 1 {
    12. go func() {
    13. fmt.Println("read stdin for h265\n")
    14. fmt.Println("start thread for h265 ok\n")
    15. sig := make(chan os.Signal)
    16. signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL, syscall.SIGABRT, syscall.SIGQUIT)
    17. var file *os.File
    18. var err error
    19. if !USE_FILE_UPLOAD {
    20. rk1126.ResumeH264()
    21. } else {
    22. fileName := "./h265_high.mp4"
    23. file, err = os.Open(fileName)
    24. defer file.Close()
    25. if err != nil {
    26. fmt.Println("Open the file failed,err:", err)
    27. os.Exit(-1)
    28. }
    29. fmt.Println("open file ", fileName, " ok\n")
    30. }
    31. // // h264FrameDuration=
    32. timestart := time.Now().UnixMilli()
    33. ticker := time.NewTicker(h264FrameDuration)
    34. defer ticker.Stop()
    35. for {
    36. select {
    37. case <-sig:
    38. rk1126.PauseH264()
    39. break
    40. case <-sysvideochan:
    41. rk1126.PauseH264()
    42. fmt.Println("sysvideochan exit")
    43. nInSendH265Track = 0
    44. return
    45. case <-ticker.C:
    46. // default:
    47. if nInSendH265Track <= 0 {
    48. rk1126.PauseH264()
    49. fmt.Println("no dc channel exit")
    50. return
    51. }
    52. if USE_FILE_UPLOAD {
    53. var arr [256]byte
    54. var buf []byte
    55. bufflen := 0
    56. for {
    57. // var arr [MAXPACKETSIZE]byte
    58. n, err := file.Read(arr[:])
    59. if err == io.EOF {
    60. fmt.Println("file read finished")
    61. file.Seek(0, 0)
    62. continue
    63. //break
    64. }
    65. if err != nil {
    66. fmt.Println("file read failed", err)
    67. os.Exit(-1)
    68. }
    69. buf = append(buf, arr[:n]...)
    70. bufflen += n
    71. if bufflen >= MAXPACKETSIZE {
    72. break
    73. }
    74. }
    75. h265 := &rk1126.Mediadata{}
    76. h265.Data = buf
    77. h265.Len = bufflen
    78. timestart := time.Now().UnixMilli()
    79. for _, vdc := range H265dcmap {
    80. SendH265FrameData(vdc, h265, timestart)
    81. }
    82. time.Sleep(h264FrameDuration)
    83. } else {
    84. delayms := time.Now().UnixMilli() - timestart
    85. fmt.Println("send H265 delay ", delayms)
    86. // fmt.Println("GetH264Data start nInSendH264Track->", nInSendH264Track)
    87. timestart = time.Now().UnixMilli()
    88. h265 := rk1126.GetH264Data()
    89. // data := h264.Data[0 : h264.Len-1]
    90. if h265 != nil {
    91. for _, vdc := range H265dcmap {
    92. // fmt.Println("\r\nSendH265FrameData ", vdc)
    93. SendH265FrameData(vdc, h265, h265.Timestamp.Milliseconds())
    94. }
    95. rk1126.VideoDone(h265)
    96. // fmt.Println("\r\nh264 send ok")
    97. } else {
    98. fmt.Println("h265 data is nil")
    99. }
    100. }
    101. }
    102. }
    103. fmt.Println("h265 thread exit")
    104. nInSendH265Track = 0
    105. }()
    106. }
    107. })
    108. dc.OnMessage(func(msg webrtc.DataChannelMessage) {
    109. msg_ := string(msg.Data)
    110. fmt.Println(msg_)
    111. })
    112. dc.OnClose(func() {
    113. fmt.Println("hd265 dc close")
    114. nInSendH265Track--
    115. for k, v := range H265dcmap {
    116. if v == dc {
    117. delete(H265dcmap, k)
    118. }
    119. }
    120. if mediatype == "audio" {
    121. nInSendAudioTrack--
    122. if nInSendAudioTrack <= 0 {
    123. //用户全部退出就是让采集程序退出
    124. fmt.Println("sysaudiochan 退出")
    125. if sysaudiochan != nil {
    126. sysaudiochan <- struct{}{}
    127. }
    128. }
    129. }
    130. // syschan <- struct{}{}
    131. })
    132. }
    133. var vpsFrame rk1126.Mediadata
    134. var spsFrame rk1126.Mediadata
    135. var ppsFrame rk1126.Mediadata
    136. var seiFrame rk1126.Mediadata
    137. var keyFrame rk1126.Mediadata
    138. func SaveFrameKeyData(pdata *rk1126.Mediadata, frametype uint16) {
    139. switch frametype {
    140. case utils.NALU_H265_VPS:
    141. vpsFrame = *pdata
    142. case utils.NALU_H265_SPS:
    143. spsFrame = *pdata
    144. case utils.NALU_H265_PPS:
    145. ppsFrame = *pdata
    146. case utils.NALU_H265_SEI:
    147. seiFrame = *pdata
    148. case utils.NALU_H265_IFRAME:
    149. keyFrame = *pdata
    150. default:
    151. }
    152. }
    153. //重要帧发送
    154. func sendH265ImportFrame(dc *webrtc.DataChannel, frametype uint16) {
    155. start := time.Now().UnixMilli()
    156. switch frametype {
    157. case utils.NALU_H265_VPS:
    158. SendH265FrameData(dc, &vpsFrame, start)
    159. case utils.NALU_H265_SPS:
    160. SendH265FrameData(dc, &vpsFrame, start)
    161. case utils.NALU_H265_PPS:
    162. SendH265FrameData(dc, &ppsFrame, start)
    163. case utils.NALU_H265_SEI:
    164. SendH265FrameData(dc, &seiFrame, start)
    165. case utils.NALU_H265_IFRAME:
    166. SendH265FrameData(dc, &keyFrame, start)
    167. default:
    168. }
    169. }
    170. func SendH265FrameData(dc *webrtc.DataChannel, h265frame *rk1126.Mediadata, timestamp int64) {
    171. // fmt.Println("start SendH265FrameData ", dc)
    172. if h265frame.Len > 0 && dc != nil && dc.ReadyState() == webrtc.DataChannelStateOpen {
    173. var frametypestr string
    174. data := h265frame.Data[0:h265frame.Len]
    175. // data := base64.StdEncoding.EncodeToString(buf)
    176. glength := len(data)
    177. count := glength / MAXPACKETSIZE
    178. rem := glength % MAXPACKETSIZE
    179. temptype, frametype, err := utils.GetFrameType(data)
    180. if err != nil {
    181. } else {
    182. SaveFrameKeyData(h265frame, frametype)
    183. frametypestr, err = utils.GetFrameTypeName(frametype)
    184. }
    185. // string.split(",")
    186. // string.split(":")
    187. startstr := "h265 start ,FrameType:" + frametypestr + ",nalutype:" + strconv.Itoa(int(temptype)) + ",pts:" + strconv.FormatInt(timestamp, 10) + ",Packetslen:" + strconv.Itoa(glength) + ",packets:" + strconv.Itoa(count) + ",rem:" + strconv.Itoa(rem)
    188. // startstr := fmt.Sprintf("h265 start ,FrameType:%s,pts:%lld,Packetslen:%d,packets:%d,rem:%d", frametypestr, h265frame.Timestamp.Milliseconds(), glength, count, rem)
    189. dc.SendText(startstr)
    190. fmt.Println("SendH265FrameData start ", startstr)
    191. i := 0
    192. for i = 0; i < count; i++ {
    193. lenth := i * MAXPACKETSIZE
    194. // dc.SendText("jpeg ID:" + strconv.Itoa(i))
    195. dc.Send(data[lenth : lenth+MAXPACKETSIZE])
    196. //fmt.Println("send len ", lenth, " :", data[lenth:lenth+MAXPACKETSIZE])
    197. }
    198. if rem != 0 {
    199. // dc.SendText("jpeg ID:" + strconv.Itoa(i))
    200. dc.Send(data[glength-rem : glength])
    201. //fmt.Println("send len ", rem, " :", data[glength-rem:glength])
    202. }
    203. dc.SendText("h265 end")
    204. //fmt.Println("send h265 end ")
    205. }
    206. }

    在js里面实现了H265帧流的接收和处理

    1. //webrtc datachannel send h265 stream
    2. const START_STR="h265 start";
    3. const FRAME_TYPE_STR="FrameType";
    4. const PACKET_LEN_STR="Packetslen";
    5. const PACKET_COUNT_STR="packets";
    6. const PACKET_PTS="pts";
    7. const PACKET_REM_STR="rem";
    8. const KEY_FRAME_TYPE="H265_FRAME_I"
    9. var frameType="";
    10. var isKeyFrame=false;
    11. var pts=0;
    12. var h265DC;
    13. var bWorking=false;
    14. var h265dataFrame=[];
    15. var h265data;
    16. var dataIndex=0;
    17. var h265datalen=0;
    18. var packet=0;
    19. var expectLength = 4;
    20. var bFindFirstKeyFrame=false;
    21. // startstr := "h265 start ,FrameType:" + frametypestr + ",Packetslen:" + strconv.Itoa(glength) + ",packets:" + strconv.Itoa(count) + ",rem:" + strconv.Itoa(rem)
    22. function isString(str){
    23. return (typeof str=='string')&&str.constructor==String;
    24. }
    25. function hexToStr(hex,encoding) {
    26. var trimedStr = hex.trim();
    27. var rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr;
    28. var len = rawStr.length;
    29. if (len % 2 !== 0) {
    30. alert("Illegal Format ASCII Code!");
    31. return "";
    32. }
    33. var curCharCode;
    34. var resultStr = [];
    35. for (var i = 0; i < len; i = i + 2) {
    36. curCharCode = parseInt(rawStr.substr(i, 2), 16);
    37. resultStr.push(curCharCode);
    38. }
    39. // encoding为空时默认为utf-8
    40. var bytesView = new Uint8Array(resultStr);
    41. var str = new TextDecoder(encoding).decode(bytesView);
    42. return str;
    43. }
    44. function deepCopy(arr) {
    45. const newArr = []
    46. for(let i in arr) {
    47. console.log(arr[i])
    48. if (typeof arr[i] === 'object') {
    49. newArr[i] = deepCopy(arr[i])
    50. } else {
    51. newArr[i] = arr[i]
    52. }
    53. }
    54. console.log(newArr)
    55. return newArr
    56. }
    57. function dump_hex(h265data,h265datalen){
    58. // console.log(h265data.toString());
    59. var str="0x"
    60. for (var i = 0; i < h265datalen; i ++ ) {
    61. var byte =h265data.slice(i,i+1)[0];
    62. str+=byte.toString(16)
    63. str+=" "
    64. // console.log((h265datalen+i).toString(16)+" ");
    65. }
    66. console.log(str);
    67. }
    68. function appendBuffer (buffer1, buffer2) {
    69. var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
    70. tmp.set(new Uint8Array(buffer1), 0);
    71. tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
    72. return tmp.buffer;
    73. };
    74. function reportStream(size){
    75. }
    76. function stopH265(){
    77. if(h265DC!==null){
    78. h265DC.close();
    79. }
    80. }
    81. var receivet1=new Date().getTime();
    82. var bRecH265=false;
    83. function initH265DC(pc,player) {
    84. console.log("initH265DC",Date());
    85. h265DC = pc.createDataChannel("h265");
    86. // var ctx = canvas.getContext("2d");
    87. h265DC.onmessage = function (event) {
    88. // console.log(bRecH265,":",event.data)
    89. if(bRecH265){
    90. if(isString(event.data)) {
    91. console.log("reveive: "+event.data)
    92. if(event.data.indexOf("h265 end")!=-1){
    93. bRecH265=false;
    94. // console.log("frame ok",":",event.data," len:"+h265datalen)
    95. if(h265datalen>0){
    96. // const framepacket=new Uint8Array(h265data)
    97. const t2 = new Date().getTime()-receivet1;
    98. if(frameType==="H265_FRAME_VPS"||frameType==="H265_FRAME_SPS"||frameType==="H265_FRAME_PPS"||frameType==="H265_FRAME_SEI"||frameType==="H265_FRAME_P")
    99. console.log("receive time:"+t2+" len:"+h265datalen);
    100. if(frameType==="H265_FRAME_P"&&!bFindFirstKeyFrame){
    101. return
    102. }
    103. bFindFirstKeyFrame=true;
    104. // h265dataFrame.push(new Uint8Array(h265data))
    105. var dataFrame=new Uint8Array(h265data)//deepCopy(h265data)//h265dataFrame.shift()
    106. var data={
    107. pts: pts,
    108. size: h265datalen,
    109. iskeyframe: isKeyFrame,
    110. packet: dataFrame//
    111. // new Uint8Array(h265data)//h265data//new Uint8Array(h265data)
    112. };
    113. var req = {
    114. t: ksendPlayerVideoFrameReq,
    115. l: h265datalen,
    116. d: data
    117. };
    118. player.postMessage(req,[req.d.packet.buffer]);
    119. h265data=null;
    120. h265datalen=0;
    121. packet=0;
    122. receivet1=new Date().getTime();
    123. }
    124. return;
    125. }
    126. }else{
    127. if (h265data != null) {
    128. h265data=appendBuffer(h265data,event.data);
    129. } else if (event.data.byteLength < expectLength) {
    130. h265data = event.data.slice(0);
    131. } else {
    132. h265data=event.data;
    133. }
    134. h265datalen+=event.data.byteLength;
    135. packet++;
    136. console.log("packet: "+packet+": t len"+h265datalen)
    137. return;
    138. }
    139. }
    140. if(isString(event.data)) {
    141. let startstring = event.data
    142. // console.log("reveive: "+startstring)
    143. if(startstring.indexOf("h265 start")!=-1){
    144. console.log(event.data );
    145. const startarray=startstring.split(",");
    146. // startstr := "h265 start ,FrameType:" + frametypestr + ",Packetslen:" + strconv.Itoa(glength) + ",packets:" + strconv.Itoa(count) + ",rem:" + strconv.Itoa(rem)
    147. for(let i=0;i
    148. const parakv=startarray[i].split(":");
    149. if(parakv!==null){
    150. switch(parakv[0]){
    151. case START_STR:
    152. break;
    153. case PACKET_PTS:
    154. pts=parseInt(parakv[1])
    155. break;
    156. case FRAME_TYPE_STR:
    157. frameType=parakv[1]
    158. if(frameType.indexOf(KEY_FRAME_TYPE)!==-1){
    159. isKeyFrame=true;
    160. }else{
    161. isKeyFrame=false;
    162. }
    163. break;
    164. case PACKET_LEN_STR:
    165. break;
    166. case PACKET_COUNT_STR:
    167. break;
    168. case PACKET_REM_STR:
    169. break;
    170. }
    171. }
    172. }
    173. bRecH265=true;
    174. packet=0;
    175. return;
    176. }
    177. }
    178. };
    179. h265DC.onopen = function () {
    180. console.log("h265 datachannel open");
    181. bWorking = true;
    182. };
    183. h265DC.onclose = function () {
    184. console.log("h265 datachannel close");
    185. bWorking=false;
    186. };
    187. }
    188. function handleUpdates(canvas, dc) {
    189. setInterval(function () {
    190. if (bWorking){
    191. dc.send(JSON.stringify({ type: "h265" })); // frame update request
    192. }
    193. }, 500);
    194. };

    接下来就是在metaRTC中的移植,因信令和采集部分以前已经做好(详见metaRTC p2p自建信令系统_superxxd的博客-CSDN博客),实现起来也比较方便,datachannel的交互以前也做了一个实现(详见metaRTC datachannel 实现 reply_superxxd的博客-CSDN博客)应为go和c师出同门所以移植实现并不难

    1. //metaRTC 发送datachannel 的函数
    2. void g_ipc_rtcrecv_sendData(int peeruid,uint8* data,int len,int mediatype){
    3. if(len<=0 || data==null) return;
    4. YangFrame H265Frame;
    5. //H265Frame.payload=MEMCALLOC(1,len);
    6. //if(H265Frame.payload==NULL) {
    7. // printf("H265Frame.payload MEMCALLOC fail\n");
    8. // return;
    9. //}
    10. //IpcRtcSession* rtcHandle=(IpcRtcSession*)user;
    11. for(int32_t i=0;ipushs.vec.vsize;i++){
    12. YangPeerConnection* rtc=rtcHandle->pushs.vec.payload[i];
    13. //找到本peer
    14. if(rtc->peer.streamconfig.uid==peeruid){
    15. //memcpy(H265Frame.payload,data,len);
    16. H265Frame.payload=data;
    17. G265Frame.mediaType=mediatype;
    18. H265Frame.nb=len;
    19. H265Frame.pts=H265Frame.dts=GETTIME();
    20. printf("datachannel send out %s\n",(char*)H265Frame.payload);
    21. rtc->on_message(&rtc->peer,&H265Frame);
    22. break;
    23. }
    24. }
    25. //SAFE_MEMFREE(H265Frame.payload);
    26. }
    27. #define MAXPACKETSIZE 65536
    28. void SendH265FrameData(int peeruid, uint8* data,int len, int64 timestamp ) {
    29. if(data!=null &&len >0) {
    30. char frametypestr[20];
    31. char *endchar="h265 end";
    32. char startstr[200+1];
    33. int frametype=0;
    34. int count=0,rem=0;
    35. count = len/ MAXPACKETSIZE;
    36. rem = glength % MAXPACKETSIZE;
    37. if(GetFrameType(data,&frametype)==0){
    38. SaveFrameKeyData(h265frame, frametype);
    39. GetFrameTypeName(frametype,&frametypestr);
    40. }
    41. snprintf(startstr,200,"h265 start ,FrameType:%s,nalutype:%d,pts:%lld,Packetslen:%d,packets:%d,rem:%d",frametypestr,temptype,timestamp,len, count,rem);
    42. // YANG_DATA_CHANNEL_STRING = 51,
    43. // YANG_DATA_CHANNEL_BINARY = 53,
    44. g_ipc_rtcrecv_sendData(peeruid,startstr,strlen(startstr),YANG_DATA_CHANNEL_STRING );
    45. printf("SendH265FrameData start ", startstr);
    46. int i = 0,lenth=0;
    47. for i = 0; i < count; i++ {
    48. lenth = i * MAXPACKETSIZE
    49. g_ipc_rtcrecv_sendData(peeruid,data+lenth,MAXPACKETSIZE,YANG_DATA_CHANNEL_BINARY );
    50. }
    51. if rem != 0 {
    52. g_ipc_rtcrecv_sendData(peeruid,data+len-rem,rem,YANG_DATA_CHANNEL_BINARY );
    53. }
    54. g_ipc_rtcrecv_sendData(peeruid,endchar,strlen(endchar),YANG_DATA_CHANNEL_STRING );
    55. }
    56. }

          以上即为webrtc 播放器的核心实现,metaRTC是一款优秀的嵌入式webrtc 软件包,以后还会支持quic协议,我们的播放器软件也可以很快速的移植到这种传输协议。

     播放器解码等实现(详见基于webrtc的p2p H265播放器实现一_superxxd的博客-CSDN博客),实测效果可以见我的头条号

    用webrtc h265播放器体验新版《雪山飞孤》,打破传统,利-今日头条 (toutiao.com)

    906cec170ce24963a2ff11db710a1187.png

     

    195165614c214e82a2d8eae9b586f265.png

    3d35f08798704c17ae0b7374ba7de335.jpg 

    768f57991ecc4f2e849dd268aba95afb.jpg 

    6bfbc72249eb49d4822f3d69237920cd.jpg 

     

     

     

  • 相关阅读:
    MySQL中如何处理并发写入问题?
    Jenkins实践指南--pipeline概述
    【ESP32】10.PCF8591数模转换器实验(IIC总线/wire库)
    Java中Map的4种遍历方式
    水平直逼高级病理学家!清华团队提出AI基础模型ROAM,实现胶质瘤精准诊断
    [附源码]JAVA毕业设计健康生活网站(系统+LW)
    latex公式写法
    C#(三十八)之StreamWriter StreamWriter使用方法及与FileStream类的区别
    python 删除特定字符所在行
    CSS中如何实现一个自适应正方形(宽高相等)的元素?
  • 原文地址:https://blog.csdn.net/superxxd/article/details/126615220