{"id":125,"date":"2022-01-13T08:07:39","date_gmt":"2022-01-13T08:07:39","guid":{"rendered":"https:\/\/www.hfrtc.top\/wordpress\/?p=125"},"modified":"2022-02-07T22:04:47","modified_gmt":"2022-02-07T14:04:47","slug":"gcc-implementation-in-webrtc","status":"publish","type":"post","link":"https:\/\/www.fanyamin.com\/wordpress\/?p=125","title":{"rendered":"GCC Implementation in WebRTC"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\" id=\"overview\">Overview<\/h1>\n\n\n\n<ul class=\"wp-block-list\"><li>RTP header extension<\/li><li>RTCP feedback extension<\/li><\/ul>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>ProbeBitrateEstimator :<\/strong>\u00a0\u6839\u636efeedback\u8ba1\u7b97\u63a2\u6d4b\u7801\u7387\uff0cPacingController\u4e2d\u4f1a\u5c06\u5305\u6309\u7167cluster\u8fdb\u884c\u5212\u5206\uff0ctransport-CC\u62a5\u6587\u80fd\u5f97\u5230\u5305\u6240\u5c5e\u7684cluster\u4ee5\u53ca\u53d1\u9001\u548c\u63a5\u6536\u4fe1\u606f\uff0c\u901a\u8fc7\u53d1\u9001\u548c\u63a5\u6536\u7684\u6570\u636e\u5927\u5c0f\u6bd4\u5224\u65ad\u662f\u5426\u5230\u8fbe\u94fe\u8def\u4e0a\u9650\u4ece\u800c\u8fdb\u884c\u5e26\u5bbd\u63a2\u6d4b<\/li><\/ul>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>AcknowledgedBitrateEstimator :<\/strong>\u00a0\u4f30\u7b97\u5f53\u524d\u7684\u541e\u5410\u91cf<\/li><\/ul>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>BitrateEstimator :<\/strong>\u4f7f\u7528\u6ed1\u52a8\u7a97\u53e3 + \u5361\u5c14\u66fc\u6ee4\u6ce2\u8ba1\u7b97\u5f53\u524d\u53d1\u9001\u541e\u5410\u91cf<\/li><\/ul>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>DelayBasedBwe :<\/strong>\u00a0\u57fa\u4e8e\u5ef6\u8fdf\u9884\u4f30\u7801\u7387<\/li><\/ul>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>TrendlineEstimator :<\/strong>\u00a0\u4f7f\u7528\u7ebf\u6027\u56de\u5f52\u8ba1\u7b97\u5f53\u524d\u7f51\u7edc\u62e5\u5835\u60c5\u51b5<\/li><\/ul>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>AimdRateControl :<\/strong>\u00a0\u901a\u8fc7TrendLine\u9884\u6d4b\u51fa\u6765\u7684\u7f51\u7edc\u72b6\u6001\u5bf9\u7801\u7387\u8fdb\u884caimd\u65b9\u5f0f\u8c03\u6574<\/li><\/ul>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>SendSideBandwidthEstimation :<\/strong>\u00a0\u57fa\u4e8e\u4e22\u5305\u8ba1\u7b97\u9884\u4f30\u7801\u7387\uff0c\u7ed3\u5408\u5ef6\u8fdf\u9884\u4f30\u7801\u7387\uff0c\u5f97\u5230\u6700\u7ec8\u7684\u76ee\u6807\u7801\u7387<\/li><\/ul>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>ProbeController :<\/strong>\u00a0\u63a2\u6d4b\u63a7\u5236\u5668\uff0c\u901a\u8fc7\u76ee\u6807\u7801\u7387\u5224\u65ad\u4e0b\u6b21\u662f\u5426\u63a2\u6d4b\uff0c\u63a2\u6d4b\u7801\u7387\u5927\u5c0f<\/li><\/ul>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>CongestionWindowPushbackController :<\/strong>\u00a0\u57fa\u4e8e\u5f53\u524d\u7684rtt\u8bbe\u7f6e\u4e00\u4e2a\u65f6\u95f4\u7a97\u53e3\uff0c\u540c\u65f6\u57fa\u4e8e\u5f53\u524d\u7684\u7801\u7387\u8bbe\u7f6e\u5f53\u524d\u65f6\u95f4\u7a97\u53e3\u4e0b\u7684\u6570\u636e\u91cf\uff0c\u901a\u8fc7\u5224\u65ad\u5f53\u524d\u7a97\u53e3\u7684\u4f7f\u7528\u91cf\uff0c\u5982\u679c\u4f7f\u7528\u91cf\u8fc7\u5927\u7684\u65f6\u5019\uff0c\u964d\u4f4e\u7f16\u7801\u65f6\u4f7f\u7528\u7684\u76ee\u6807\u7801\u7387\uff0c\u52a0\u901f\u7a97\u53e3\u6d88\u9000\uff0c\u51cf\u5c11\u5ef6\u8fdf<\/li><\/ul>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>AlrDetector :<\/strong>\u00a0\u5e94\u7528(\u7801\u7387)\u53d7\u9650\u68c0\u6d4b\uff0c\u68c0\u6d4b\u5f53\u524d\u7684\u53d1\u9001\u7801\u7387\u662f\u5426\u548c\u76ee\u6807\u7801\u7387\u7531\u4e8e\u7f16\u7801\u5668\u7b49\u539f\u56e0\u76f8\u5dee\u8fc7\u5927\u53d7\u9650\u4e86\uff0c\u53d7\u9650\u60c5\u51b5\u4e0b\u4f1a\u89e6\u53d1\u5e26\u5bbd\u9884\u6d4b\u8fc7\u7a0b\u7684\u7279\u6b8a\u5904\u7406<\/li><\/ul>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>NetworkStateEstimator <\/strong><strong>\u3001<\/strong><strong> NetworkStatePredictor <\/strong><strong>\uff1a<\/strong>\u00a0\u6b64\u4e24\u8005\u5c5e\u4e8e\u5f85\u5f00\u53d1\u7c7b\uff0c\u8fd8\u6ca1\u7528\u4e0a.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"protocol\">Protocol<\/h2>\n\n\n\n<p>\u53c2\u8003\u534f\u8bae<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a rel=\"noreferrer noopener\" href=\"https:\/\/datatracker.ietf.org\/doc\/html\/draft-holmer-rmcat-transport-wide-cc-extensions-01\" target=\"_blank\">https:\/\/datatracker.ietf.org\/doc\/html\/draft-holmer-rmcat-transport-wide-cc-extensions-01 <\/a><\/li><li><a rel=\"noreferrer noopener\" href=\"https:\/\/datatracker.ietf.org\/doc\/html\/draft-ietf-rmcat-gcc-02\" target=\"_blank\">https:\/\/datatracker.ietf.org\/doc\/html\/draft-ietf-rmcat-gcc-02<\/a><\/li><\/ul>\n\n\n\n<p>\u53c2\u8003\u5b9e\u73b0<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"http:\/\/transport-wide-cc-02\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/webrtc.googlesource.com\/src\/+\/refs\/heads\/main\/docs\/native-code\/rtp-hdrext\/transport-wide-cc-02\/<\/a><\/li><\/ul>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"transport-wide-sequence-number\"><a href=\"https:\/\/walterfan.github.io\/webrtc_note\/3.media\/webrtc_tcc.html#id5\">Transport-wide Sequence Number<\/a><a href=\"https:\/\/walterfan.github.io\/webrtc_note\/3.media\/webrtc_tcc.html#transport-wide-sequence-number\"><\/a><\/h2>\n\n\n\n<p>\u5728\u6bcf\u4e2a\u8981\u53d1\u9001\u7684 RTP \u5305\u4e2d\u6dfb\u52a0\u4e00\u4e2a\u6269\u5c55\u5934\uff0c\u5305\u542b 16 bits \u7684\u5e8f\u53f7 sequence number. \u5728\u540c\u4e00\u4e2a\u4f20\u8f93\u901a\u9053\u4e2d\uff0c\u6bcf\u53d1\u4e00\u4e2a RTP \u5305\uff0c\u8fd9\u4e2a\u5e8f\u53f7\u5c31\u52a0\u4e00<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> 0                   1                   2                   3\n 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n|       0xBE    |    0xDE       |           length=1            |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n|  ID   | L=1   |transport-wide sequence number | zero padding  |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"transport-wide-rtcp-feedback-message\"><a href=\"https:\/\/walterfan.github.io\/webrtc_note\/3.media\/webrtc_tcc.html#id6\">Transport-wide RTCP Feedback Message<\/a><a href=\"https:\/\/walterfan.github.io\/webrtc_note\/3.media\/webrtc_tcc.html#transport-wide-rtcp-feedback-message\"><\/a><\/h2>\n\n\n\n<pre class=\"wp-block-preformatted\"> 0                   1                   2                   3\n 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n|V=2|P|  FMT=15 |    PT=205     |           length              |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n|                     SSRC of packet sender                     |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n|                      SSRC of media source                     |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n|      base sequence number     |      packet status count      |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n|                 reference time                | fb pkt. count |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n|          packet chunk         |         packet chunk          |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n.                                                               .\n.                                                               .\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n|         packet chunk          |  recv delta   |  recv delta   |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n.                                                               .\n.                                                               .\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n|           recv delta          |  recv delta   | zero padding  |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n<\/pre>\n\n\n\n<ul class=\"wp-block-list\"><li>version (V): 2 bits This field identifies the RTP version. The current version is 2.<br><\/li><li>padding (P): 1 bit If set, the padding bit indicates that the packet contains additional padding octets at the end that are not part of the control information but are included in the length field.<br><\/li><li>feedback message type (FMT): 5 bits This field identifies the type of the FB message. It must have the value 15.<br><\/li><li>payload type (PT): 8 bits This is the RTCP packet type that identifies the packet as being an RTCP FB message. The value must be RTPFB = 205.<br><\/li><li>SSRC of packet sender: 32 bits The synchronization source identifier for the originator of this packet.<br><\/li><li>SSRC of media source: 32 bits The synchronization source identifier of the media source that this piece of feedback<\/li><li>information is related to. <br>(TODO: This is transport wide, do we just pick any of the media source SSRCs?)<br><\/li><li><strong>base sequence number<\/strong>: 16 bits The transport-wide sequence number of the first packet in this feedback. This number is not necessarily increased for every feedback; in the case of reordering it may be decreased. <br>- \u8be5 fb \u5305\u9996\u4e2a rtp \u5305\u7684 transport seq\uff0c\u975e rtp \u5305\u5e8f\u5217\u53f7, \u800c\u662f\u4f20\u8f93\u901a\u9053\u7684\u5305\u53f7\u3002<br><\/li><li><strong>packet status count<\/strong>: 16 bits The number of packets this feedback contains status for, starting with the packet identified by the base sequence number. <br><strong>- \u8be5 feedback packet \u5305\u542b rtp \u5305\u4e2a\u6570<\/strong>\u3002<br><\/li><li><strong>reference time<\/strong>: 24 bits Signed integer indicating an absolute reference time in some (unknown) time base chosen by the sender of the feedback packets. <br>The value is to be interpreted in multiples of 64ms. <br>The first recv delta in this packet is relative to the reference time. <br>The reference time makes it possible to calculate the delta between feedbacks even if some feedback packets are lost, since it always uses the same time base. <br><strong>- \u53c2\u8003\u65f6\u95f4\uff0cfeedback \u5305\u9996\u4e2a rtp \u7684\u5230\u8fbe\u65f6\u95f4, \u5b83\u662f 64ms \u7684\u500d\u6570<\/strong><br><\/li><li><strong>feedback packet count<\/strong>: 8 bits A counter incremented by one for each feedback packet sent. Used to detect feedback packet losses. <br>- \u5df2\u53d1\u9001 feedback \u5305\u8ba1\u6570\u5668\uff0c\u53ef\u7528\u4e8e feedback packet \u4e22\u5931\u68c0\u6d4b<br><\/li><li><strong>packet chunk<\/strong>: 16 bits A list of packet status chunks. These indicate the status of a number of packets starting with the one identified by base sequence number. See below for details. <br>- \u63cf\u8ff0 rtp \u5305 4 \u79cd\u72b6\u6001\uff08\u89c1\uff1a4.2\uff09\uff0c\u6709 Run Length Chunk \u548c Status Vector Chunk \u4e24\u79cd\u683c\u5f0f<br><\/li><li><strong>recv delta<\/strong>: 8 bits For each \u201cpacket received\u201d status, in the packet status chunks, a receive delta block will follow. See details below. <br>- \u5f53 rtp \u5305\u7684\u72b6\u6001\u4e3a Packet received\uff0c\u901a\u8fc7 recv delta \u8bb0\u5f55\u5176\u4e0e\u524d\u4e00\u4e2a rtp \u5305\u5230\u8fbe\u7684\u65f6\u95f4\u95f4\u9694\u3002<\/li><\/ul>\n\n\n\n<p><\/p>\n\n\n\n<p>\u4e00\u4e2a Transport Feedback RTCP \u53cd\u9988\u5305\u5305\u542b\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>\u4e00\u4e2a\u8d77\u59cb\u5e8f\u53f7 base sequence number\uff0c <\/li><li>\u4e00\u4e2a\u5305\u7684\u72b6\u6001\u7684\u6570\u91cf packet status count, <\/li><li>\u4e00\u4e2a\u53c2\u8003\u65f6\u95f4 reference time, \u5b83\u662f\u53d1\u9001\u65b9\u9009\u5b9a\u7684\u4e00\u4e2a\u4ee564ms\u4e3a\u500d\u6570\u7684\u65f6\u95f4\u6233<\/li><li>\u4e00\u4e2a\u53cd\u9988\u5305\u8ba1\u6570 feedback packet count, \u6bcf\u53d1\u9001\u4e00\u4e2a RTCP \u53cd\u9988\u5305, \u8fd9\u4e2a\u8bfb\u6570\u5668\u5c31\u52a01<\/li><li>\u82e5\u5e72\u4e2a\u5305\u5757 packet chunk , \u5b83\u8868\u793a\u4e86\u6bcf\u4e2a\u5e94\u6536\u5230\u7684 RTP \u5305\u7684\u72b6\u6001<\/li><li>\u7d27\u968f packet chunks  \u7684 recv delta , \u8868\u793a\u63a5\u6536\u5230\u7684 RTP \u5305\u7684\u65f6\u95f4\u95f4\u9694\u53d8\u5316<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"packet-stats-symbols\">Packet Stats Symbols<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>00<\/td><td>\u5305\u672a\u6536\u5230<\/td><\/tr><tr><td>01<\/td><td>\u5305\u6536\u5230\u4e86\uff0c\u95f4\u9694\u5f88\u5c0f<\/td><\/tr><tr><td>10<\/td><td>\u5305\u6536\u5230\u4e86\uff0c\u95f4\u9694\u5f88\u5927\uff0c\u6216\u8005\u662f\u8d1f\u503c<\/td><\/tr><tr><td>11<\/td><td>\u4fdd\u7559<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"packet-chunk\">Packet Chunk<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"run-length-chunk\">Run Length Chunk<\/h3>\n\n\n\n<p>TBD.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"status-vector-chunk\">Status Vector Chunk<\/h3>\n\n\n\n<p>TBD.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"receive-delta\">Receive Delta<\/h2>\n\n\n\n<p>Delta \u8868\u793a\u4e3a 250 us \u7684\u500d\u6570<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"implementation\">Implementation<\/h1>\n\n\n\n<p>\u53c2\u89c1 https:\/\/xie.infoq.cn\/article\/2f944089023274ef0ac6eabd8<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/static001.geekbang.org\/infoq\/ac\/ace20a3edfb959151bc7c62e0095cdc9.png\" alt=\"\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"alr-detector\">ALR Detector<\/h2>\n\n\n\n<p>ALR\uff08Application limited region detector\uff09\u8be5\u6a21\u5757\u4e5f\u5c5e\u4e8e gcc \u7684\u4e00\u4e2a\u5b50\u6a21\u5757, \u5176\u5927\u6982\u539f\u7406\u5c31\u662f SentRate\/EstimatedRate \u7684\u767e\u5206\u6bd4\u4e0e kAlrStartUsagePercent\uff0860\uff09\u505a\u6bd4\u8f83\uff0c\u5f53\u5c0f\u4e8e\u8be5\u503c\u8ba4\u4e3a\u7f51\u7edc\u53d7\u9650\uff0c\u9700\u8981\u542f\u52a8 probe \u91cd\u65b0\u63a2\u6d4b\u5e26\u5bbd\uff0c\u5f53\u5927\u4e8e kAlrEndUsagePercent\uff0870\uff09\uff0c\u8ba4\u4e3a\u7f51\u7edc\u6062\u590d\u5219\u4e0d\u4f1a\u8fdb\u884c\u542f\u52a8\u4e0b\u6b21 probe \u63a2\u6d4b\u3002<\/p>\n\n\n\n<p>\u53c2\u89c1 https:\/\/xie.infoq.cn\/article\/2091ae4f237a7f89ca7ecfedd<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"overuse-detector-\u8fc7\u5ea6\u4f7f\u7528\u68c0\u6d4b\u5668\">Overuse Detector \u8fc7\u5ea6\u4f7f\u7528\u68c0\u6d4b\u5668<\/h2>\n\n\n\n<p>\u53c2\u89c1 <strong>https:\/\/developer.aliyun.com\/article\/781511<\/strong> <\/p>\n\n\n\n<p>\u7f51\u7edc\u5e26\u5bbd\u4f7f\u7528\u72b6\u6001<br>WebRTC \u5b9a\u4e49\u4e86\u4e09\u79cd\u7f51\u7edc\u5e26\u5bbd\u7684\u4f7f\u7528\u72b6\u6001\uff1aNormal\u3001Underuse\u3001Overuse\uff0c\u5373\u6b63\u5e38\u3001\u4f4e\u8f7d\u3001\u8fc7\u8f7d\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>enumclass BandwidthUsage {\n  kBwNormal = 0,\n  kBwUnderusing = 1,\n  kBwOverusing = 2,\n};<\/code><\/pre>\n\n\n\n<p>\u4e0b\u56fe\u5c55\u793a\u4e86\u8fc7\u8f7d\u68c0\u6d4b\u4e2d\u4e09\u79cd\u4fe1\u53f7\u7684\u4ea7\u751f\u673a\u5236\uff0c\u5176\u4e2d\uff0c\u4e0a\u4e0b\u4e24\u6761\u7ea2\u8272\u66f2\u7ebf\u8868\u793a\u52a8\u6001\u9608\u503c\uff1a<img decoding=\"async\" src=\"https:\/\/ucc.alicdn.com\/pic\/developer-ecology\/3b407a1d28c5454abdfed7a86f0fada0.png\" alt=\"image.png\"><br>\u84dd\u8272\u66f2\u7ebf\u8868\u793a\u8c03\u6574\u540e\u7684\u5ef6\u8fdf\u68af\u5ea6\u659c\u7387\u503c\uff1a<img decoding=\"async\" src=\"https:\/\/ucc.alicdn.com\/pic\/developer-ecology\/08f7204f962e4038bfbbc4ce50c86a12.png\" alt=\"image.png\"><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"796\" height=\"310\" src=\"https:\/\/www.hfrtc.top\/wordpress\/wp-content\/uploads\/2022\/01\/image.png\" alt=\"\" class=\"wp-image-160 img-fluid\" srcset=\"https:\/\/www.fanyamin.com\/wordpress\/wp-content\/uploads\/2022\/01\/image.png 796w, https:\/\/www.fanyamin.com\/wordpress\/wp-content\/uploads\/2022\/01\/image-300x117.png 300w, https:\/\/www.fanyamin.com\/wordpress\/wp-content\/uploads\/2022\/01\/image-768x299.png 768w\" sizes=\"(max-width: 796px) 100vw, 796px\" \/><figcaption><img decoding=\"async\" src=\"https:\/\/ucc.alicdn.com\/pic\/developer-ecology\/6a766369dee045dd85628f062d029f58.png\" alt=\"image.png\"><\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>\u5ef6\u8fdf\u68af\u5ea6\u4ee3\u8868\u5ef6\u8fdf\u53d8\u5316\u7684\u4e00\u4e2a\u8d8b\u52bf\u7684\u4e00\u4e2a\u659c\u7387\uff0c \u53d6\u503c\u8303\u56f4\u662f [-1, 1]<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/ucc.alicdn.com\/pic\/developer-ecology\/2b60268dbaf8404d800785ca953f910e.png\" alt=\"image.png\"\/><\/figure>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/ucc.alicdn.com\/pic\/developer-ecology\/1fdb85461077461e972405374d558fe2.png\" alt=\"image.png\"\/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>class TrendlineEstimator : public DelayIncreaseDetectorInterface {\n\n  void Detect(double trend, double ts_delta, int64_t now_ms);\n\n  void UpdateThreshold(double modified_offset, int64_t now_ms);\n\n}\n\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code><strong><span class=\"has-inline-color has-vivid-cyan-blue-color\">\/*\n <meta charset=\"utf-8\">trend \u662f<meta charset=\"utf-8\">\u5305\u95f4\u5ef6\u8fdf\u68af\u5ea6\u8d8b\u52bf\u7684\u659c\u7387\n <meta charset=\"utf-8\">num_of_deltas_ \u4ee3\u8868 \u5305\u95f4\u5ef6\u8fdf\u68af\u5ea6\u8ba1\u7b97\u7684\u6b21\u6570\uff0c\u53d6\u503c\u8303\u56f4\u662f &#91;2, 60]\n threshold_gain_ \u662f\u5bf9\u4e8e\u659c\u7387\u7684\u589e\u76ca\u53c2\u6570\uff0c\u9ed8\u8ba4\u4e3a 4\n \n*\/<\/span><\/strong>\nvoid TrendlineEstimator::Detect(double trend, double ts_delta, int64_t now_ms) {\n  if (num_of_deltas_ &lt; 2) {\n    hypothesis_ = BandwidthUsage::kBwNormal;\n    return;\n  }\n  \/\/ \u5c06 trend \u653e\u5927\n  const double modified_trend =\n      std::min(num_of_deltas_, kMinNumDeltas) * trend * threshold_gain_;\n  prev_modified_trend_ = modified_trend;\n  BWE_TEST_LOGGING_PLOT(1, \"T\", now_ms, modified_trend);\n  BWE_TEST_LOGGING_PLOT(1, \"threshold\", now_ms, threshold_);\n\n<span class=\"has-inline-color has-vivid-cyan-blue-color\">  \/*\n    \u5ef6\u8fdf\u68af\u5ea6\u659c\u7387 &gt; \u5f53\u524d\u9608\u503c\n    \u8fc7\u8f7d\u603b\u65f6\u957f &gt; \u8bbe\u5b9a\u65f6\u957f\n    \u8fc7\u8f7d\u6b21\u6570 &gt;= 1\n    \u5f53\u524d\u5ef6\u8fdf\u68af\u5ea6\u659c\u7387\u503c &gt; \u4e0a\u4e00\u6b21\u7684\u659c\u7387\u503c\uff0c \u60c5\u51b5\u5728\u6076\u5316\n  *\/<\/span>\n  if (modified_trend &gt; threshold_) {\n    if (time_over_using_ == -1) {\n      \/\/ Initialize the timer. Assume that we've been\n      \/\/ over-using half of the time since the previous\n      \/\/ sample.\n      time_over_using_ = ts_delta \/ 2;\n    } else {\n      \/\/ Increment timer\n      time_over_using_ += ts_delta;\n    }\n    overuse_counter_++;\n   <strong> if (time_over_using_ &gt; overusing_time_threshold_ &amp;&amp; overuse_counter_ &gt; 1) {<\/strong>\n      if (trend &gt;= prev_trend_) {\n        time_over_using_ = 0;\n        overuse_counter_ = 0;\n        hypothesis_ = BandwidthUsage::kBwOverusing; \/\/ \u5047\u5b9a\u4e3a\u8fc7\u5ea6\u4f7f\u7528\n      }\n    }\n  } else if (modified_trend &lt; -threshold_) {\n    time_over_using_ = -1;\n    overuse_counter_ = 0;\n    hypothesis_ = BandwidthUsage::kBwUnderusing;\n  } else {\n    time_over_using_ = -1;\n    overuse_counter_ = 0;\n    hypothesis_ = BandwidthUsage::kBwNormal;\n  }\n  prev_trend_ = trend;\n  UpdateThreshold(modified_trend, now_ms);\n}\n\n\/* \n\u53c2\u7167\u534f\u8bae\u7684\u516c\u5f0f\uff0c\u6839\u636e\u4e0a\u4e00\u6b21\u7684\u9608\u503c\uff0c\u8ddd\u4e0a\u4e00\u6b21\u66f4\u65b0\u7684\u65f6\u95f4\u5dee\uff0c\u9608\u503c\u7684\u589e\u957f\u7387\uff0c\u5f53\u524d\u659c\u7387\u4e0e\u4e0a\u4e00\u6b21\u9608\u503c\u7684\u5dee\u503c\uff0c\u6765\u81ea\u9002\u5e94\u5730\u66f4\u65b0\u9608\u503c \n*\/\n\nvoid TrendlineEstimator::UpdateThreshold(double modified_trend,\n                                         int64_t now_ms) {\n  if (last_update_ms_ == -1)\n    last_update_ms_ = now_ms;\n\n  if (fabs(modified_trend) &gt; threshold_ + kMaxAdaptOffsetMs) {\n    \/\/ Avoid adapting the threshold to big latency spikes, caused e.g.,\n    \/\/ by a sudden capacity drop.\n    last_update_ms_ = now_ms;\n    return;\n  }\n\n  const double k = fabs(modified_trend) &lt; threshold_ ? k_down_ : k_up_;\n  const int64_t kMaxTimeDeltaMs = 100;\n  int64_t time_delta_ms = std::min(now_ms - last_update_ms_, kMaxTimeDeltaMs);\n  threshold_ += k * (fabs(modified_trend) - threshold_) * time_delta_ms;\n  threshold_ = rtc::SafeClamp(threshold_, 6.f, 600.f);\n  last_update_ms_ = now_ms;\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"\u6570\u636e\u7ed3\u6784\">\u6570\u636e\u7ed3\u6784<\/h2>\n\n\n\n<p>\u4e0a\u8ff0\u7684 RTCP \u53cd\u9988\u6d88\u606f\u5bf9\u5e94\u4e8e class TransportFeedback<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-green-cyan-color\">\/\/ Header size:<\/mark>\n<mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-green-cyan-color\">\/\/ * 4 bytes Common RTCP Packet Header\n\/\/ * 8 bytes Common Packet Format for RTCP Feedback Messages\n\/\/ * 8 bytes FeedbackPacket header<\/mark>\nconstexpr size_t kTransportFeedbackHeaderSizeBytes = 4 + 8 + 8;\nconstexpr size_t kChunkSizeBytes = 2;\n\/\/ TODO(sprang): Add support for dynamic max size for easier fragmentation,\n\/\/ eg. set it to what's left in the buffer or IP_PACKET_SIZE.\n\/\/ Size constraint imposed by RTCP common header: 16bit size field interpreted\n\/\/ as number of four byte words minus the first header word.\nconstexpr size_t kMaxSizeBytes = (1 &lt;&lt; 16) * 4;\n<mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-green-cyan-color\">\/\/ Payload size:\n\/\/ * 8 bytes Common Packet Format for RTCP Feedback Messages\n\/\/ * 8 bytes FeedbackPacket header.\n\/\/ * 2 bytes for one chunk.<\/mark>\nconstexpr size_t kMinPayloadSizeBytes = 8 + 8 + 2;\nconstexpr int kBaseScaleFactor =\n    TransportFeedback::kDeltaScaleFactor * (1 &lt;&lt; 8);\nconstexpr int64_t kTimeWrapPeriodUs = (1ll &lt;&lt; 24) * kBaseScaleFactor;\n\n\/\/    Message format\n\/\/\n\/\/     0                   1                   2                   3\n\/\/     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n\/\/    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\/\/    |V=2|P|  FMT=15 |    PT=205     |           length              |\n\/\/    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\/\/  0 |                     SSRC of packet sender                     |\n\/\/    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\/\/  4 |                      SSRC of media source                     |\n\/\/    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\/\/  8 |      base sequence number     |      packet status count      |\n\/\/    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\/\/ 12 |                 reference time                | fb pkt. count |\n\/\/    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\/\/ 16 |          packet chunk         |         packet chunk          |\n\/\/    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\/\/    .                                                               .\n\/\/    .                                                               .\n\/\/    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\/\/    |         packet chunk          |  recv delta   |  recv delta   |\n\/\/    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\/\/    .                                                               .\n\/\/    .                                                               .\n\/\/    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\/\/    |           recv delta          |  recv delta   | zero padding  |\n\/\/    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>method<\/td><td>responsibility<\/td><td>Collaborator<\/td><td>Comment<\/td><\/tr><tr><td><strong>bool<\/strong> <strong>AddReceivedPacket<\/strong>(uint16_t sequence_number, int64_t timestamp_us);<\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>ReceivedPacket<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class ReceivedPacket {\n   public:\n    ReceivedPacket(uint16_t sequence_number, int16_t delta_ticks)\n        : sequence_number_(sequence_number),\n          delta_ticks_(delta_ticks),\n          received_(true) {}\n    explicit ReceivedPacket(uint16_t sequence_number)\n        : sequence_number_(sequence_number), received_(false) {}\n    ReceivedPacket(const ReceivedPacket&amp;) = default;\n    ReceivedPacket&amp; operator=(const ReceivedPacket&amp;) = default;\n\n    uint16_t sequence_number() const { return sequence_number_; }\n    int16_t delta_ticks() const { return delta_ticks_; }\n    int32_t delta_us() const { return delta_ticks_ * kDeltaScaleFactor; }\n    TimeDelta delta() const { return TimeDelta::Micros(delta_us()); }\n    bool received() const { return received_; }\n\n   private:\n    uint16_t sequence_number_;\n    int16_t delta_ticks_;\n    bool received_;\n  };<\/code><\/pre>\n\n\n\n<p>SentPacket<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>struct PacedPacketInfo {\n  PacedPacketInfo();\n  PacedPacketInfo(int probe_cluster_id,\n                  int probe_cluster_min_probes,\n                  int probe_cluster_min_bytes);\n\n  bool operator==(const PacedPacketInfo&amp; rhs) const;\n\n  \/\/ TODO(srte): Move probing info to a separate, optional struct.\n  static constexpr int kNotAProbe = -1;\n  int send_bitrate_bps = -1;\n  int probe_cluster_id = kNotAProbe;\n  int probe_cluster_min_probes = -1;\n  int probe_cluster_min_bytes = -1;\n  int probe_cluster_bytes_sent = 0;\n};\n\nstruct SentPacket {\n  Timestamp send_time = Timestamp::PlusInfinity();\n  \/\/ Size of packet with overhead up to IP layer.\n  DataSize size = DataSize::Zero();\n  \/\/ Size of preceeding packets that are not part of feedback.\n  DataSize prior_unacked_data = DataSize::Zero();\n  \/\/ Probe cluster id and parameters including bitrate, number of packets and\n  \/\/ number of bytes.\n  PacedPacketInfo pacing_info;\n  \/\/ True if the packet is an audio packet, false for video, padding, RTX etc.\n  bool audio = false;\n  \/\/ Transport independent sequence number, any tracked packet should have a\n  \/\/ sequence number that is unique over the whole call and increasing by 1 for\n  \/\/ each packet.\n  int64_t sequence_number;\n  \/\/ Tracked data in flight when the packet was sent, excluding unacked data.\n  DataSize data_in_flight = DataSize::Zero();\n};<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\nstruct <strong>PacketResult<\/strong> {\n  class ReceiveTimeOrder {\n   public:\n    bool operator()(const PacketResult&amp; lhs, const PacketResult&amp; rhs);\n  };\n\n  PacketResult();\n  PacketResult(const PacketResult&amp;);\n  ~PacketResult();\n\n  inline bool IsReceived() const { return !receive_time.IsPlusInfinity(); }\n\n  SentPacket sent_packet;\n  Timestamp receive_time = Timestamp::PlusInfinity();\n};\n\nstruct <strong>TransportPacketsFeedback<\/strong> {\n  TransportPacketsFeedback();\n  TransportPacketsFeedback(const TransportPacketsFeedback&amp; other);\n  ~TransportPacketsFeedback();\n\n  Timestamp feedback_time = Timestamp::PlusInfinity();\n  Timestamp first_unacked_send_time = Timestamp::PlusInfinity();\n  DataSize data_in_flight = DataSize::Zero();\n  DataSize prior_in_flight = DataSize::Zero();\n  std::vector&lt;PacketResult&gt; packet_feedbacks;\n\n  \/\/ Arrival times for messages without send time information.\n  std::vector&lt;Timestamp&gt; sendless_arrival_times;\n\n  std::vector&lt;PacketResult&gt; ReceivedWithSendInfo() const;\n  std::vector&lt;PacketResult&gt; LostWithSendInfo() const;\n  std::vector&lt;PacketResult&gt; PacketsWithFeedback() const;\n  std::vector&lt;PacketResult&gt; SortedByReceiveTime() const;\n};\n\nclass <strong><span class=\"has-inline-color has-luminous-vivid-orange-color\">PacketArrivalTimeMap<\/span><\/strong> {\n public:\n  \/\/ Impossible to request feedback older than what can be represented by 15\n  \/\/ bits.\n  static constexpr size_t kMaxNumberOfPackets = (1 &lt;&lt; 15);\n\n  \/\/ Indicates if the packet with `sequence_number` has already been received.\n  bool has_received(int64_t sequence_number) const;\n\n  \/\/ Returns the sequence number of the first entry in the map, i.e. the\n  \/\/ sequence number that a `begin()` iterator would represent.\n  int64_t begin_sequence_number() const { return begin_sequence_number_; }\n\n  \/\/ Returns the sequence number of the element just after the map, i.e. the\n  \/\/ sequence number that an `end()` iterator would represent.\n  int64_t end_sequence_number() const {\n    return begin_sequence_number_ + arrival_times.size();\n  }\n\n  \/\/ Returns an element by `sequence_number`, which must be valid, i.e.\n  \/\/ between &#91;begin_sequence_number, end_sequence_number).\n  int64_t get(int64_t sequence_number) {\n    int64_t pos = sequence_number - begin_sequence_number_;\n    RTC_DCHECK(pos &gt;= 0 &amp;&amp; pos &lt; static_cast&lt;int64_t&gt;(arrival_times.size()));\n    return arrival_times&#91;pos];\n  }\n\n  \/\/ Clamps `sequence_number` between &#91;begin_sequence_number,\n  \/\/ end_sequence_number].\n  int64_t clamp(int64_t sequence_number) const;\n\n  \/\/ Erases all elements from the beginning of the map until `sequence_number`.\n  void EraseTo(int64_t sequence_number);\n\n  \/\/ Records the fact that a packet with `sequence_number` arrived at\n  \/\/ `arrival_time_ms`.\n  void AddPacket(int64_t sequence_number, int64_t arrival_time_ms);\n\n  \/\/ Removes packets from the beginning of the map as long as they are received\n  \/\/ before `sequence_number` and with an age older than `arrival_time_limit`\n  void RemoveOldPackets(int64_t sequence_number, int64_t arrival_time_limit);\n\n private:\n  \/\/ Deque representing unwrapped sequence number -&gt; time, where the index +\n  \/\/ `begin_sequence_number_` represents the packet's sequence number.\n  <span class=\"has-inline-color has-vivid-purple-color\">\u5b58\u50a8\u4e86\u6bcf\u4e2a\u5305\u7684\u65f6\u95f4\uff0c\u5305\u53f7\u5c31\u662f <meta charset=\"utf-8\">begin_sequence_number_ \u52a0\u4e0a\u7d22\u5f15<\/span>\n  std::deque&lt;int64_t&gt; arrival_times;\n\n  \/\/ The unwrapped sequence number for the first element in\n  \/\/ `arrival_times`.\n  int64_t begin_sequence_number_ = 0;\n\n  \/\/ Indicates if this map has had any packet added to it. The first packet\n  \/\/ decides the initial sequence number.\n  bool has_seen_packet_ = false;\n};<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"\u6784\u9020\u65b9\u6cd5\">\u6784\u9020\u65b9\u6cd5<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>std::unique_ptr&lt;rtcp::TransportFeedback&gt;\nRemoteEstimatorProxy::MaybeBuildFeedbackPacket(\n    bool include_timestamps,\n    int64_t begin_sequence_number_inclusive, \/\/ \u8d77\u59cb\u7684\u5305\u5e8f\u53f7 (\u5305\u542b)\n    int64_t end_sequence_number_exclusive,   \/\/ \u7ed3\u675f\u7684\u5305\u5e8f\u53f7\uff08\u4e0d\u5305\u542b)\n    bool is_periodic_update) {\n  RTC_DCHECK_LT(begin_sequence_number_inclusive, end_sequence_number_exclusive);\n\n  int64_t start_seq =\n      packet_arrival_times_.clamp(begin_sequence_number_inclusive);\n\n  int64_t end_seq = packet_arrival_times_.clamp(end_sequence_number_exclusive);\n\n  \/\/ Create the packet on demand, as it's not certain that there are packets\n  \/\/ in the range that have been received. \n  <strong>\/\/ feedback_packet \u5c31\u662f\u6211\u4eec\u8981\u6784\u9020\u7684 RTCP \u6d88\u606f<\/strong>\n  std::unique_ptr&lt;rtcp::TransportFeedback&gt; feedback_packet = nullptr;\n\n  int64_t next_sequence_number = begin_sequence_number_inclusive;\n  <strong>\/\/ \u9010\u4e2a\u4ece <meta charset=\"utf-8\">packet_arrival_times_ \u4e2d\u53d6\u51fa\u5404\u4e2a RTP \u5305\u7684\u5230\u8fbe\u65f6\u95f4<\/strong>\n  for (int64_t seq = start_seq; seq &lt; end_seq; ++seq) {\n    int64_t arrival_time_ms = packet_arrival_times_.get(seq);\n    \/\/ 0 \u4ee3\u8868\u5305\u6ca1\u6536\u5230\n    if (arrival_time_ms == 0) {\n      \/\/ Packet not received.\n      continue;\n    }\n\n    if (feedback_packet == nullptr) {\n      feedback_packet =\n          std::make_unique&lt;rtcp::TransportFeedback&gt;(include_timestamps);\n      \/\/ TODO(sprang): Measure receive times in microseconds and remove the\n      \/\/ conversions below.\n      feedback_packet-&gt;SetMediaSsrc(media_ssrc_);\n      \/\/ Base sequence number is the expected first sequence number. This is\n      \/\/ known, but we might not have actually received it, so the base time\n      \/\/ shall be the time of the first received packet in the feedback.\n      feedback_packet-&gt;SetBase(\n          static_cast&lt;uint16_t&gt;(begin_sequence_number_inclusive &amp; 0xFFFF),\n          arrival_time_ms * 1000);\n      feedback_packet-&gt;SetFeedbackSequenceNumber(feedback_packet_count_++);\n    }\n\n    if (!feedback_packet-&gt;AddReceivedPacket(static_cast&lt;uint16_t&gt;(seq &amp; 0xFFFF),\n                                            arrival_time_ms * 1000)) {\n      \/\/ Could not add timestamp, feedback packet might be full. Return and\n      \/\/ try again with a fresh packet.\n      break;\n    }\n\n    next_sequence_number = seq + 1;\n  }\n  if (is_periodic_update) {\n    periodic_window_start_seq_ = next_sequence_number;\n  }\n  return feedback_packet;\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"receiver-side\">Receiver side<\/h2>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"558\" height=\"341\" src=\"https:\/\/www.hfrtc.top\/wordpress\/wp-content\/uploads\/2022\/01\/image-2.png\" alt=\"\" class=\"wp-image-225 img-fluid\" srcset=\"https:\/\/www.fanyamin.com\/wordpress\/wp-content\/uploads\/2022\/01\/image-2.png 558w, https:\/\/www.fanyamin.com\/wordpress\/wp-content\/uploads\/2022\/01\/image-2-300x183.png 300w\" sizes=\"(max-width: 558px) 100vw, 558px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>class<\/td><td>method<\/td><td>responsibility<\/td><td>Collaborators<\/td><\/tr><tr><td><a href=\"https:\/\/source.chromium.org\/chromium\/chromium\/src\/+\/main:third_party\/webrtc\/modules\/remote_bitrate_estimator\/inter_arrival.h;bpv=1;bpt=1;l=22?gsn=InterArrival&amp;gs=kythe%3A%2F%2Fchromium.googlesource.com%2Fchromium%2Fsrc%3Flang%3Dc%252B%252B%3Fpath%3Dsrc%2Fthird_party%2Fwebrtc%2Fmodules%2Fremote_bitrate_estimator%2Finter_arrival.h%23InterArrival%253Awebrtc%2523c%2523nb%2524dPiQRPG6&amp;gs=kythe%3A%2F%2Fchromium.googlesource.com%2Fchromium%2Fsrc%3Flang%3Dc%252B%252B%3Fpath%3Dsrc%2Fthird_party%2Fwebrtc%2Fmodules%2Fremote_bitrate_estimator%2Finter_arrival.h%23ZLllfXyMsb8DXeWrinSQmiLfMMMcuxXlaOSu6rTaZ78\">InterArrival<\/a><\/td><td><\/td><td>compute the inter-arrival time delta and the size delta&nbsp;between two timestamp groups<\/td><td><\/td><\/tr><tr><td>RemoteEstimatorProxy<\/td><td><\/td><td>process incoming packetsbuild Transport CC feedback RTCP message<\/td><td>NetworkStateEstimate<\/td><\/tr><tr><td><\/td><td>IncomingPacket(int64_t arrival_time_ms,size_t payload_size,const RTPHeader&amp; header)<\/td><td><\/td><td><\/td><\/tr><tr><td><\/td><td>MaybeBuildFeedbackPacket<\/td><td><\/td><td>rtcp::TransportFeedbackrtcp::TransportFeedback::ReceivedPacket<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"send-side\">Send side<\/h2>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"708\" height=\"669\" src=\"https:\/\/www.hfrtc.top\/wordpress\/wp-content\/uploads\/2022\/01\/image-1.png\" alt=\"\" class=\"wp-image-223 img-fluid\" srcset=\"https:\/\/www.fanyamin.com\/wordpress\/wp-content\/uploads\/2022\/01\/image-1.png 708w, https:\/\/www.fanyamin.com\/wordpress\/wp-content\/uploads\/2022\/01\/image-1-300x283.png 300w\" sizes=\"(max-width: 708px) 100vw, 708px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>class<\/td><td>method<\/td><td>responsibility<\/td><td>collaborators<\/td><\/tr><tr><td>DelayBasedBwe<\/td><td><\/td><td>Delay based Bandwidth Estimator<\/td><td><\/td><\/tr><tr><td><\/td><td>IncomingPacketFeedbackVector(const TransportPacketsFeedback&amp; msg,absl::optional&lt;DataRate&gt; acked_bitrate,absl::optional&lt;DataRate&gt; probe_bitrate,absl::optional&lt;NetworkStateEstimate&gt; network_estimate,bool in_alr)<\/td><td>Process the TransportFeedback RTCP message<\/td><td>PacketResultBandwidthUsageTrendlineEstimator<\/td><\/tr><tr><td><\/td><td>IncomingPacketFeedback(const PacketResult&amp; packet_feedback,Timestamp at_time)<\/td><td><\/td><td><\/td><\/tr><tr><td><a href=\"https:\/\/source.chromium.org\/chromium\/chromium\/src\/+\/main:third_party\/webrtc\/modules\/congestion_controller\/goog_cc\/trendline_estimator.h;bpv=1;bpt=1;l=53?gsn=TrendlineEstimator&amp;gs=kythe%3A%2F%2Fchromium.googlesource.com%2Fchromium%2Fsrc%3Flang%3Dc%252B%252B%3Fpath%3Dsrc%2Fthird_party%2Fwebrtc%2Fmodules%2Fcongestion_controller%2Fgoog_cc%2Ftrendline_estimator.h%23TrendlineEstimator%253Awebrtc%2523c%2523lurIhEQPYJ\">TrendlineEstimator<\/a>&nbsp;<\/td><td><\/td><td><\/td><td>parent interface&nbsp;:<a href=\"https:\/\/source.chromium.org\/chromium\/chromium\/src\/+\/main:third_party\/webrtc\/modules\/congestion_controller\/goog_cc\/delay_increase_detector_interface.h;drc=054e08864177603f17edbc111db7ebc8586906bd;l=20\">DelayIncreaseDetectorInterface<\/a>NetworkStatePredictor<\/td><\/tr><tr><td><\/td><td>Update(double recv_delta_ms,double send_delta_ms,int64_t send_time_ms,int64_t arrival_time_ms,size_t packet_size,bool calculated_deltas)<\/td><td><\/td><td><\/td><\/tr><tr><td><\/td><td>void Detect(double trend, double ts_delta, int64_t now_ms);<\/td><td><\/td><td><\/td><\/tr><tr><td><\/td><td>void UpdateThreshold(double modified_offset, int64_t now_ms);<\/td><td><\/td><td><\/td><\/tr><tr><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class RtpTransportControllerSend final\n    : public RtpTransportControllerSendInterface,\n      public RtcpBandwidthObserver,\n      public TransportFeedbackObserver,\n      public NetworkStateEstimateObserver\n{\n\n}\n\n\n\n\nclass GoogCcNetworkController : public NetworkControllerInterface {\n\/\/\u2026\n\n  const std::unique_ptr&lt;ProbeController&gt; probe_controller_;\n  const std::unique_ptr&lt;CongestionWindowPushbackController&gt;\n      congestion_window_pushback_controller_;\n\n  std::unique_ptr&lt;<strong>SendSideBandwidthEstimation<\/strong>&gt; bandwidth_estimation_;\n  std::unique_ptr&lt;AlrDetector&gt; alr_detector_;\n  std::unique_ptr&lt;ProbeBitrateEstimator&gt; probe_bitrate_estimator_;\n  std::unique_ptr&lt;NetworkStateEstimator&gt; network_estimator_;\n  std::unique_ptr&lt;NetworkStatePredictor&gt; network_state_predictor_;\n  std::unique_ptr&lt;<strong>DelayBasedBwe<\/strong>&gt; delay_based_bwe_;\n  std::unique_ptr&lt;<strong>AcknowledgedBitrateEstimatorInterface<\/strong>&gt;\n      acknowledged_bitrate_estimator_;\n\n}\n\n\nclass <strong>SendSideBandwidthEstimation<\/strong> {\n public:\n  SendSideBandwidthEstimation() = delete;\n  SendSideBandwidthEstimation(const WebRtcKeyValueConfig* key_value_config,\n                              RtcEventLog* event_log);\n  ~SendSideBandwidthEstimation();\n\n  void OnRouteChange();\n\n  DataRate target_rate() const;\n  uint8_t fraction_loss() const { return last_fraction_loss_; }\n  TimeDelta round_trip_time() const { return last_round_trip_time_; }\n\n  DataRate GetEstimatedLinkCapacity() const;\n  \/\/ Call periodically to update estimate.\n  void UpdateEstimate(Timestamp at_time);\n  void OnSentPacket(const SentPacket&amp; sent_packet);\n  void UpdatePropagationRtt(Timestamp at_time, TimeDelta propagation_rtt);\n\n  \/\/ Call when we receive a RTCP message with TMMBR or REMB.\n  void UpdateReceiverEstimate(Timestamp at_time, DataRate bandwidth);\n\n  \/\/ Call when a new delay-based estimate is available.\n  void UpdateDelayBasedEstimate(Timestamp at_time, DataRate bitrate);\n\n  \/\/ Call when we receive a RTCP message with a ReceiveBlock.\n  void UpdatePacketsLost(int64_t packets_lost,\n                         int64_t number_of_packets,\n                         Timestamp at_time);\n\n  \/\/ Call when we receive a RTCP message with a ReceiveBlock.\n  void UpdateRtt(TimeDelta rtt, Timestamp at_time);\n\n  void SetBitrates(absl::optional&lt;DataRate&gt; send_bitrate,\n                   DataRate min_bitrate,\n                   DataRate max_bitrate,\n                   Timestamp at_time);\n  void SetSendBitrate(DataRate bitrate, Timestamp at_time);\n  void SetMinMaxBitrate(DataRate min_bitrate, DataRate max_bitrate);\n  int GetMinBitrate() const;\n  void SetAcknowledgedRate(absl::optional&lt;DataRate&gt; acknowledged_rate,\n                           Timestamp at_time);\n  void IncomingPacketFeedbackVector(const TransportPacketsFeedback&amp; report);\n\n}\n\n\n\n\nclass <strong>DelayBasedBwe<\/strong> {\n public:\n  struct Result {\n    Result();\n    ~Result() = default;\n    bool updated;\n    bool probe;\n    DataRate target_bitrate = DataRate::Zero();\n    bool recovered_from_overuse;\n    bool backoff_in_alr;\n  };\n\n  explicit DelayBasedBwe(const WebRtcKeyValueConfig* key_value_config,\n                         RtcEventLog* event_log,\n                         NetworkStatePredictor* network_state_predictor);\n\n  DelayBasedBwe() = delete;\n  DelayBasedBwe(const DelayBasedBwe&amp;) = delete;\n  DelayBasedBwe&amp; operator=(const DelayBasedBwe&amp;) = delete;\n\n  virtual ~DelayBasedBwe();\n\n  Result IncomingPacketFeedbackVector(\n      const TransportPacketsFeedback&amp; msg,\n      absl::optional&lt;DataRate&gt; acked_bitrate,\n      absl::optional&lt;DataRate&gt; probe_bitrate,\n      absl::optional&lt;NetworkStateEstimate&gt; network_estimate,\n      bool in_alr);\n  void OnRttUpdate(TimeDelta avg_rtt);\n  bool LatestEstimate(std::vector&lt;uint32_t&gt;* ssrcs, DataRate* bitrate) const;\n  void SetStartBitrate(DataRate start_bitrate);\n  void SetMinBitrate(DataRate min_bitrate);\n  TimeDelta GetExpectedBwePeriod() const;\n  void SetAlrLimitedBackoffExperiment(bool enabled);\n  DataRate TriggerOveruse(Timestamp at_time,\n                          absl::optional&lt;DataRate&gt; link_capacity);\n  DataRate last_estimate() const { return prev_bitrate_; }\n\n\/\/\u2026\n\n\/\/ Alternatively, run two separate overuse detectors for audio and video,\n  \/\/ and fall back to the audio one if we haven't seen a video packet in a\n  \/\/ while.\n  BweSeparateAudioPacketsSettings separate_audio_;\n  int64_t audio_packets_since_last_video_;\n  Timestamp last_video_packet_recv_time_;\n\n  NetworkStatePredictor* network_state_predictor_;\n  std::unique_ptr&lt;InterArrival&gt; video_inter_arrival_;\n  std::unique_ptr&lt;InterArrivalDelta&gt; video_inter_arrival_delta_;\n  std::unique_ptr&lt;DelayIncreaseDetectorInterface&gt; video_delay_detector_;\n  std::unique_ptr&lt;InterArrival&gt; audio_inter_arrival_;\n  std::unique_ptr&lt;InterArrivalDelta&gt; audio_inter_arrival_delta_;\n  std::unique_ptr&lt;DelayIncreaseDetectorInterface&gt; audio_delay_detector_;\n  DelayIncreaseDetectorInterface* active_delay_detector_;\n\n  Timestamp last_seen_packet_;\n  bool uma_recorded_;\n  AimdRateControl rate_control_;\n  DataRate prev_bitrate_;\n  bool has_once_detected_overuse_;\n  BandwidthUsage prev_state_;\n  const bool use_new_inter_arrival_delta_;\n  bool alr_limited_backoff_enabled_;\n\n}\n\n\nclass <strong>DelayIncreaseDetectorInterface<\/strong> {\n public:\n  DelayIncreaseDetectorInterface() {}\n  virtual ~DelayIncreaseDetectorInterface() {}\n\n  \/\/ Update the detector with a new sample. The deltas should represent deltas\n  \/\/ between timestamp groups as defined by the InterArrival class.\n  virtual void Update(double recv_delta_ms,\n                      double send_delta_ms,\n                      int64_t send_time_ms,\n                      int64_t arrival_time_ms,\n                      size_t packet_size,\n                      bool calculated_deltas) = 0;\n\n  virtual BandwidthUsage State() const = 0;\n\n  RTC_DISALLOW_COPY_AND_ASSIGN(DelayIncreaseDetectorInterface);\n};\n\n\n\nclass <strong>TrendlineEstimator<\/strong> : public DelayIncreaseDetectorInterface {\n public:\n  TrendlineEstimator(const WebRtcKeyValueConfig* key_value_config,\n                     NetworkStatePredictor* network_state_predictor);\n\n  ~TrendlineEstimator() override;\n\n  \/\/ Update the estimator with a new sample. The deltas should represent deltas\n  \/\/ between timestamp groups as defined by the InterArrival class.\n  void Update(double recv_delta_ms,\n              double send_delta_ms,\n              int64_t send_time_ms,\n              int64_t arrival_time_ms,\n              size_t packet_size,\n              bool calculated_deltas) override;\n\n  void UpdateTrendline(double recv_delta_ms,\n                       double send_delta_ms,\n                       int64_t send_time_ms,\n                       int64_t arrival_time_ms,\n                       size_t packet_size);\n\n  BandwidthUsage State() const override;\n\n\/\/\u2026\n}\n\n\nclass <strong>AcknowledgedBitrateEstimator<\/strong>\n    : public AcknowledgedBitrateEstimatorInterface {\n public:\n  AcknowledgedBitrateEstimator(\n      const WebRtcKeyValueConfig* key_value_config,\n      std::unique_ptr&lt;BitrateEstimator&gt; bitrate_estimator);\n\n  explicit AcknowledgedBitrateEstimator(\n      const WebRtcKeyValueConfig* key_value_config);\n  ~AcknowledgedBitrateEstimator() override;\n\n  void IncomingPacketFeedbackVector(\n      const std::vector&lt;PacketResult&gt;&amp; packet_feedback_vector) override;\n  absl::optional&lt;DataRate&gt; bitrate() const override;\n  absl::optional&lt;DataRate&gt; PeekRate() const override;\n  void SetAlr(bool in_alr) override;\n  void SetAlrEndedTime(Timestamp alr_ended_time) override;\n\n private:\n  absl::optional&lt;Timestamp&gt; alr_ended_time_;\n  bool in_alr_;\n  std::unique_ptr&lt;BitrateEstimator&gt; bitrate_estimator_;\n};\n\n\/\/ Computes a bayesian estimate of the throughput given acks containing\n\/\/ the arrival time and payload size. Samples which are far from the current\n\/\/ estimate or are based on few packets are given a smaller weight, as they\n\/\/ are considered to be more likely to have been caused by, e.g., delay spikes\n\/\/ unrelated to congestion.\nclass <strong>BitrateEstimator<\/strong> {\n public:\n  explicit BitrateEstimator(const WebRtcKeyValueConfig* key_value_config);\n  virtual ~BitrateEstimator();\n  virtual void Update(Timestamp at_time, DataSize amount, bool in_alr);\n\n  virtual absl::optional&lt;DataRate&gt; bitrate() const;\n  absl::optional&lt;DataRate&gt; PeekRate() const;\n\n  virtual void ExpectFastRateChange();\n\n private:\n  float UpdateWindow(int64_t now_ms,\n                     int bytes,\n                     int rate_window_ms,\n                     bool* is_small_sample);\n  int sum_;\n  FieldTrialConstrained&lt;int&gt; initial_window_ms_;\n  FieldTrialConstrained&lt;int&gt; noninitial_window_ms_;\n  FieldTrialParameter&lt;double&gt; uncertainty_scale_;\n  FieldTrialParameter&lt;double&gt; uncertainty_scale_in_alr_;\n  FieldTrialParameter&lt;double&gt; small_sample_uncertainty_scale_;\n  FieldTrialParameter&lt;DataSize&gt; small_sample_threshold_;\n  FieldTrialParameter&lt;DataRate&gt; uncertainty_symmetry_cap_;\n  FieldTrialParameter&lt;DataRate&gt; estimate_floor_;\n  int64_t current_window_ms_;\n  int64_t prev_time_ms_;\n  float bitrate_estimate_kbps_;\n  float bitrate_estimate_var_;\n};<\/code><\/pre>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"reference\">Reference<\/h1>\n\n\n\n<p>Source codes of webrtc:&nbsp;<a href=\"https:\/\/source.chromium.org\/chromium\/chromium\/src\/+\/main:third_party\/webrtc\/modules\/\">https:\/\/source.chromium.org\/chromium\/chromium\/src\/+\/main:third_party\/webrtc\/modules\/<\/a><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>webrtc\/modules\/remote_bitrate_estimator<\/li><li>webrtc\/modules\/congestion_controller<\/li><li>webrtc\/modules\/rtp_rtcp\/source\/rtcp_packet\/<a href=\"http:\/\/transport_feedback.cc\/\">transport_feedback.cc<\/a><\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Overview RTP header extension RTCP feedback extension ProbeBitrateEstimator :\u00a0\u6839\u636efeedback\u8ba1\u7b97\u63a2\u6d4b\u7801\u7387\uff0cPacingController\u4e2d\u4f1a\u5c06\u5305\u6309\u7167cluster\u8fdb\u884c\u5212\u5206\uff0ctransport-CC\u62a5\u6587\u80fd\u5f97\u5230\u5305\u6240\u5c5e\u7684cluster\u4ee5\u53ca\u53d1\u9001\u548c\u63a5\u6536\u4fe1\u606f\uff0c\u901a\u8fc7\u53d1\u9001\u548c\u63a5\u6536\u7684\u6570\u636e\u5927\u5c0f\u6bd4\u5224\u65ad\u662f\u5426\u5230\u8fbe\u94fe\u8def\u4e0a\u9650\u4ece\u800c\u8fdb\u884c\u5e26\u5bbd\u63a2\u6d4b AcknowledgedBitrateEstimator :\u00a0\u4f30\u7b97\u5f53\u524d\u7684\u541e\u5410\u91cf BitrateEstimator :\u4f7f\u7528\u6ed1\u52a8\u7a97\u53e3 + \u5361\u5c14\u66fc\u6ee4\u6ce2\u8ba1\u7b97\u5f53\u524d\u53d1\u9001\u541e\u5410\u91cf DelayBasedBwe :\u00a0\u57fa\u4e8e\u5ef6\u8fdf\u9884\u4f30\u7801\u7387 TrendlineEstimator :\u00a0\u4f7f\u7528\u7ebf\u6027\u56de\u5f52\u8ba1\u7b97\u5f53\u524d\u7f51\u7edc\u62e5\u5835\u60c5\u51b5 AimdRateControl :\u00a0\u901a\u8fc7TrendLine\u9884\u6d4b\u51fa\u6765\u7684\u7f51\u7edc\u72b6\u6001\u5bf9\u7801\u7387\u8fdb\u884caimd\u65b9\u5f0f\u8c03\u6574 SendSideBandwidthEstimation :\u00a0\u57fa\u4e8e\u4e22\u5305\u8ba1\u7b97\u9884\u4f30\u7801\u7387\uff0c\u7ed3\u5408\u5ef6\u8fdf\u9884\u4f30\u7801\u7387\uff0c\u5f97\u5230\u6700\u7ec8\u7684\u76ee\u6807\u7801\u7387 ProbeController :\u00a0\u63a2\u6d4b\u63a7\u5236\u5668\uff0c\u901a\u8fc7\u76ee\u6807\u7801\u7387\u5224\u65ad\u4e0b\u6b21\u662f\u5426\u63a2\u6d4b\uff0c\u63a2\u6d4b\u7801\u7387\u5927\u5c0f CongestionWindowPushbackController :\u00a0\u57fa\u4e8e\u5f53\u524d\u7684rtt\u8bbe\u7f6e\u4e00\u4e2a\u65f6\u95f4\u7a97\u53e3\uff0c\u540c\u65f6\u57fa\u4e8e\u5f53\u524d\u7684\u7801\u7387\u8bbe\u7f6e\u5f53\u524d\u65f6\u95f4\u7a97\u53e3\u4e0b\u7684\u6570\u636e\u91cf\uff0c\u901a\u8fc7\u5224\u65ad\u5f53\u524d\u7a97\u53e3\u7684\u4f7f\u7528\u91cf\uff0c\u5982\u679c\u4f7f\u7528\u91cf\u8fc7\u5927\u7684\u65f6\u5019\uff0c\u964d\u4f4e\u7f16\u7801\u65f6\u4f7f\u7528\u7684\u76ee\u6807\u7801\u7387\uff0c\u52a0\u901f\u7a97\u53e3\u6d88\u9000\uff0c\u51cf\u5c11\u5ef6\u8fdf AlrDetector :\u00a0\u5e94\u7528(\u7801\u7387)\u53d7\u9650\u68c0\u6d4b\uff0c\u68c0\u6d4b\u5f53\u524d\u7684\u53d1\u9001\u7801\u7387\u662f\u5426\u548c\u76ee\u6807\u7801\u7387\u7531\u4e8e\u7f16\u7801\u5668\u7b49\u539f\u56e0\u76f8\u5dee\u8fc7\u5927\u53d7\u9650\u4e86\uff0c\u53d7\u9650\u60c5\u51b5\u4e0b\u4f1a\u89e6\u53d1\u5e26\u5bbd\u9884\u6d4b\u8fc7\u7a0b\u7684\u7279\u6b8a\u5904\u7406 NetworkStateEstimator \u3001 NetworkStatePredictor \uff1a\u00a0\u6b64\u4e24\u8005\u5c5e\u4e8e\u5f85\u5f00\u53d1\u7c7b\uff0c\u8fd8\u6ca1\u7528\u4e0a. Protocol \u53c2\u8003\u534f\u8bae https:\/\/datatracker.ietf.org\/doc\/html\/draft-holmer-rmcat-transport-wide-cc-extensions-01 https:\/\/datatracker.ietf.org\/doc\/html\/draft-ietf-rmcat-gcc-02 \u53c2\u8003\u5b9e\u73b0 https:\/\/webrtc.googlesource.com\/src\/+\/refs\/heads\/main\/docs\/native-code\/rtp-hdrext\/transport-wide-cc-02\/ Transport-wide Sequence Number \u5728\u6bcf\u4e2a\u8981\u53d1\u9001\u7684 RTP \u5305\u4e2d\u6dfb\u52a0\u4e00\u4e2a\u6269\u5c55\u5934\uff0c\u5305\u542b 16 bits \u7684\u5e8f\u53f7 sequence number. \u5728\u540c\u4e00\u4e2a\u4f20\u8f93\u901a\u9053\u4e2d\uff0c\u6bcf\u53d1\u4e00\u4e2a RTP \u5305\uff0c\u8fd9\u4e2a\u5e8f\u53f7\u5c31\u52a0\u4e00 0 1 [&hellip;] <a class=\"read-more\" href=\"https:\/\/www.fanyamin.com\/wordpress\/?p=125\" title=\"Permanent Link to: GCC Implementation in WebRTC\">&rarr;Read&nbsp;more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-125","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.fanyamin.com\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/125"}],"collection":[{"href":"https:\/\/www.fanyamin.com\/wordpress\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.fanyamin.com\/wordpress\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.fanyamin.com\/wordpress\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.fanyamin.com\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=125"}],"version-history":[{"count":20,"href":"https:\/\/www.fanyamin.com\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/125\/revisions"}],"predecessor-version":[{"id":319,"href":"https:\/\/www.fanyamin.com\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/125\/revisions\/319"}],"wp:attachment":[{"href":"https:\/\/www.fanyamin.com\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=125"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.fanyamin.com\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=125"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.fanyamin.com\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=125"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}