百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

webRTC 中 timing 信息的使用

toyiye 2024-06-06 22:12 15 浏览 0 评论

webRTC 是一个异步系统,通信的双方无需做时间同步。

本文主要探讨 webRTC 是怎样解决下面两个跟时间有关的问题:1. 音视频同步 2. 基于延时的带宽评估。

音视频同步(Lip-sync)

发送端采集-编码-发送,接收端解码-渲染,音频流和视频流的处理和网络传输是互相独立的,而且各自的采样/播放频率也是不同的。如何在接收端还原采集端的真实场景,从来都不是一件容易的事情。好在人的听觉/视觉系统本来就有一定容忍能力,ITU(国际电信联盟)给了一个建议:音频之于视频在这个范围内[-125ms,45ms],也就是落后 125ms 或早于 45ms,人类感觉上是可以接受的,我们认为这是音视频处于同步状态。

webRTC 解决这个问题的原理也比较简单,发送端给音频流和视频流的数据包都打上时间戳,这些时间戳都可以跟同一个时间基准对齐,接收端利用时间戳和缓存就可以调整每个流上音频/视频帧渲染时间,最终达到同步的效果。我们可以进一步了解一下实现的细节,以视频流为例。下图为视频处理的流水线,每个矩形框是一个线程实例。

实现上有三种时间信息:

1.本地系统时间:从操作系统启动计时至当前的时间差值

2.NTP(Network Time Protocol)时间:全局时间信息,从 1/1/1900-00:00h 计时到当前的时间差值

3.RTP 时间:帧时间戳,以视频采样 90k 频率为例,rtp_timestamp=ntp_timestamp*90

这三种时间坐标都是对时间的度量,只是描述时间的方式不同。比如当前绝对时间 2020-08-05T06:08:52+00:00, 它们是这样表达的。

本地时间 1919620051:表示开机计数起,过去 1919620051ms 了,大概 22.2days。

NTP 时间 3805596543795:表示距离 1/1/1900-00:00h,过去 3805596543795ms 了。

RTP 时间:RTP 时间由 NTP 时间计算而来,时间单位 1/90000s,u32 存储,计算过程会发送溢出,((u32)3805596543795)*90=1521922030。

音视频开发视频教程:在线观看→2023年初版!C++音视频开发项目实战教程,少走弯路,避免盲目自学。教程包含(FFmpeg6.0/H265/H264/RTMP/RTSP/WebRTC/SRS_哔哩哔哩_bilibili

发送端

一帧视频画面在 caputer 线程就记录下了,这一帧对应的三个时间信息,尤其重要的是 RTP 时间。这个 rtp_timestamp 在 Packet pacer 模块会加一个提前设定的偏移量,作为最终的 rtp 时间发出去。这个偏移量加在了整个 rtp 时间坐标系内,所有的对外的 RTP 时间都加了。

视频流按照自己的 RTP 时间对每一个包做了标记,音频流也类似的根据自己的 RTP 时间对每一个音频包做了标记,但这两条流里的时间都是按照自己的步调在走,是独立的。如果要求接收端使这两条流同步渲染,就要想办法让这些时间统一跟同一个时间基准对齐。如下图示,逻辑上举例描述了两条流如何同步,其中的时间数字只做参考,非真实数据。

RTCP SR(sender report)的作用之一就是做时间对齐的,将该流中的 RTP 时间于 NTP 时间对齐。所有的流都对齐发送端的 NTP 时间,这样接收端就有了统一时间基准。

RTCP SR format 如下:

        0                   1                   2                   3
        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
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
header |V=2|P|    RC   |   PT=SR=200   |             length            |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                         SSRC of sender                        |
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
sender |              NTP timestamp, most significant word             |
info   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |             NTP timestamp, least significant word             |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                         RTP timestamp                         |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                     sender's packet count                     |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                      sender's octet count                     |
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
report |                 SSRC_1 (SSRC of first source)                 |
block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  1    | fraction lost |       cumulative number of packets lost       |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |           extended highest sequence number received           |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                      interarrival jitter                      |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                         last SR (LSR)                         |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                   delay since last SR (DLSR)                  |
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
report |                 SSRC_2 (SSRC of second source)                |
block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  2    :                               ...                             :
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
       |                  profile-specific extensions                  |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   The sender report packet consists of three sections, possibly
   followed by a fourth profile-specific extension section if defined.
   The first section, the header, is 8 octets long.  The fields have the
   following meaning:

   version (V): 2 bits
      Identifies the version of RTP, which is the same in RTCP packets
      as in RTP data packets.  The version defined by this specification
      is two (2).

接收端

上图中可以看到经过网络传输后,到达接收端的帧数据可能经过了 jitter(抖动),乱序,比如 stream1 的帧 2/3/4。接收端通过 RTCP SR 和 buffer 的设计,采用 pull 的模式,以渲染作为终点倒推从 frame queue 中取帧的延迟。从单条流处理过程中可以看到该延迟包含渲染+解码+抖动延迟,而多流之间的同步还需要考虑流之间的相对传输延迟(参考 RtpStreamsSynchronizer),最终得到每条流的取帧延迟。

接收端多流同步其实是包含两部分的含义:单流内的流畅播放和多流间的时间同步播放。音频和视频延迟处理原理类似,只是时延的计算方式有所不同,接下来以视频流的延迟处理来说明这个过程。

视频流流畅播放

视频解码线程执行 loop 不断从 frame_queue 中取下一帧解码渲染,通过严格控制每一帧画面的执行解码-渲染的时间起点来达到帧与帧之间最终渲染播放的时间间隔是大致相等的,视觉感受是流畅的。这个开始时间我们用 waitTime 来表达,即 thread 等待多久去取帧处理。

一些重要时间量的说明

waitTime = render_systime - current_systime - render_cost - decoder_cost;
render_systime =local_systime + max(min_playout_delay,target_delay); //计算渲染时间
local_systime = rtpToLocaltime(rtp_time)//把帧rtp时间转换为本地系统时间
target_delay = jitter_delay+render_cost+decoder_cost; //计算预估目标延迟
min_playout_delay //流间同步用的时延调节参数
jitter_delay //抖动延迟
render_cost //渲染延迟,固定10ms
decoder_cost //解码延迟

要得到待解码帧的 waitTime 首先要计算该帧的渲染时间(render_systime),先把帧附带的 rtp 时间转成本地系统时间(local_systime,转换方法应该容易理解,不展开),然后叠加一个时间延迟计算方法为 max(min_playout_delay,target_delay),min_playout_delay 为流间同步用的调节参数,这里讲流内的延迟处理,可以暂时略过。target_delay 为系统评估的三个延迟(抖动延迟+渲染延迟+解码延迟)之和,注意这里计算得到的 target_delay 延迟是统计累积得来的,实际参与到 render_systime 的计算时,还有些实现上的处理。这里也做了简化处理,便于理解原理。

有了渲染时间(render_systime)后,waitTime 很容易得出了。细心观察,每一帧的处理都多等待一个 jitter_delay,但帧间的解码间隔还是保持相同的。jitter_delay 的存在就是对抗网络传输的不确定性的。webRTC 动态计算它的取值,稳定的网络下它的值接近 0,造成的延迟比较小;弱网下延迟不稳定,这个值计算出来较大,增加等待时间换取渲染的平稳流畅,很好的做到了延迟与流畅的平衡。

音视频流同步

单条流内做到流畅播放的同时还需要做流之间的时间对齐,以下图为例。假设最近的一对音频+视频包在同一时间采样,(实际情况可以是不同时间点,这里做了简化)那我们也期望他们同一时间在接收端渲染。可以看出整个过程主要包含两个时间信息,传输时间延迟(xxx_transfer_delay)和接收方的处理延迟(xxx_current_delay)。这两个时间不同的流各自维护各自的延迟信息,如果希望音频和视频包经过相同的延迟后同时渲染,可以在两条流上各加一个延迟调整参数(xxx_min_playout_delay),通过增减调整这个参数使得两条流的延迟逼近相等。

webRTC 里面是周期性(1s)来计算调整这些延迟调整参数(xxx_min_playout_delay)的。如上图例子,伪代码大概如此。

//Time diff video vs audio
time_diff = (video_transfer_delay+video_current_delay)-(audio_transfer_delay+audio_current_delay)
if(time_diff>0){ //video is slower
		down(video_min_playout_delay);
    up(audio_min_playout_delay);
}
else{//video is faster
		up(video_min_playout_delay);
    down(audio_min_playout_delay);
}

基于延时的带宽估计

WebRTC 的成功之一在于其设计一套拥塞控制算法,基础数据来自于发送端的丢包统计和包接收时间的统计。这里只讲一下有关 timing 的 RTP 包接收时间的统计和反馈,不对拥塞算法展开讲述。

拥塞控制的逻辑现在默认都在发送端执行,有关时间延迟的计算包括发送时间 T 和接收时间 t,发送端自己可以保存每个包 T,接收端只需要反馈 t 即可。算法的逻辑是每个包的接收时间都要反馈,这涉及到交互数据和频次就会比较多,webRTC 对此也有精心设计。

webRTC 默认在发送端做传输带宽的估计,媒体流走的 RTP/UDP 协议栈,UDP 层没有带宽估计的功能,webRTC 通过扩展 RTP/RTCP 的传输格式使得可以在发送端做传输层的带宽估计。

RTP format

    0                   1                   2                   3
    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
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |V=2|P|X|  CC   |M|     PT      |       sequence number         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           timestamp                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           synchronization source (SSRC) identifier            |
   +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
   |            contributing source (CSRC) identifiers(if mixed)   |
   |                             ....                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           header extension (optional)                         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           payload header (format depended)                    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           payload data                                        |
   +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

在 header extension 域组织如下类型的扩展内容

     0                   1                   2                   3
      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |       0xBE    |    0xDE       |           length=1            |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |  ID   | L=1   |transport-wide sequence number | zero padding  |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Tips: 可以注意到 RTP 包里有两种 sequence number。

equence number:是 RTP 层的概念,用于 RTP stream 的重组解复用。比如多条流复用的场景,每条流有各自的自增序列。

transport-wide sequence number:是传输层概念,传输层包的标识,用于传输层的码率统计。该序列自增不受多流复用的影响,因为复用发生在 RTP 层。

发送端在发送的时候对每一个 RTP packet 都打上 transport-wide sequence number 的序号(PacketRouter::SendPacket),比如发送 seq=53,54,55。

接收端收到该包后,把该包的到达时间记录下来,记录时间为本地内部时间戳(单位 ms),即开机多久了。

packet_arrival_times_[53]=1819746010
packet_arrival_times_[54]=1819746020
packet_arrival_times_[55]=1819746026 

接收端 RemoteEstimatorProxy 模块负责传输层统计的反馈,周期性的把包接收的时间信息回馈到发送端。

     0                   1                   2                   3
     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
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |V=2|P| FMT=CCFB |   PT = 205   |          length               |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                  SSRC of packet sender                        |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                  SSRC of 1st media source                     |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |          begin_seq            |             end_seq           |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |L|ECN|  Arrival time offset    | ...                           .
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    .                                                               .
    .                                                               .
    .                                                               .
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                  SSRC of nth media source                     |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |          begin_seq            |             end_seq           |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |L|ECN|  Arrival time offset    | ...                           |
    .                                                               .
    .                                                               .
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                 Report Timestamp (32bits)                     |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

RTCP transport feedback 一般是 RTCP 通道上最频繁的传递内容,webRTC 对其传输也有特别的设计。关注以下几个参数

max_intervel = 250ms //feedback 最大周期
min_intervel =50ms   //feedback 最小周期
rtcp_ratio = 5%      //feedback占用带宽比例
Avg_feedback_size = 68bytes  //平均一个feedback包的大小

发送 RTCP transport feedback 的时间周期控制在[50ms,250ms]内,在这个范围内根据当前带宽动态调整,尽量把 RTCP transport feedback 的传输占用带宽比例控制在 5%。可以计算得到边界,单单传输 feedback 占用的带宽范围[2176bps,10880bps],也是一笔不小的开销了。

总结

本文总结了 webRTC 中三种 timing 类型,本地时间、NTP 时间、RTP 时间,同时分析了音视频同步和基于延迟带宽评估两个专题对时间信息的使用。

附录

附上 webRTC 工程上有关 timing 的几个关键数据结构

Capturer

class webrtc::VideoFrame{
...
  uint16_t id_;            //picture id
  uint32_t timestamp_rtp_; //rtp timestamp, (u32)ntp_time_ms_ *90
  int64_t ntp_time_ms_;    //ntp timestamp, capture time since 1/1/1900-00:00h
  int64_t timestamp_us_;   //internal timestamp, capture time since system started, round at 49.71days
}

VideoStreamEncoder::OnFrame // caluclate capture timing

VideoStreamEncoder::OnEncodedImage // fill capture timing

RtpVideoSender::OnEncodedImage // timestamp_rtp_+random value

class webrtc::EncodedImage{
...
  //RTP Video Timing extension
  //https://webrtc.googlesource.com/src/+/refs/heads/master/docs/native-code/rtp-hdrext/video-timing
  struct Timing {
    uint8_t flags = VideoSendTiming::kInvalid;
    int64_t encode_start_ms = 0;                //frame encoding start time, base on ntp_time_ms_
    int64_t encode_finish_ms = 0;               //frame encoding end time, base on ntp_time_ms_
    int64_t packetization_finish_ms = 0;        //encoded frame packetization time, base on ntp_time_ms_
    int64_t pacer_exit_ms = 0;                  //packet sent time when leaving pacer, base on ntp_time_ms_
    int64_t network_timestamp_ms = 0;           //reseved for network node
    int64_t network2_timestamp_ms = 0;          //reseved for network node
    int64_t receive_start_ms = 0;
    int64_t receive_finish_ms = 0;
  } timing_;
  uint32_t timestamp_rtp_;											//same as caputrer.timestamp_rtp_
  int64_t ntp_time_ms_;										      //same as caputrer.ntp_time_ms_
  int64_t capture_time_ms_;										  //same as caputrer.capture_time_ms_
}

RTPSenderVideo::SendVideo

class webrtc::RtpPacketToSend{
...
  // RTP Header.
  bool marker_;                //frame end marker
  uint16_t sequence_number_;   //RTP sequence number, start at random(1,32767)
  uint32_t timestamp_;         //capturer timestamp_rtp_ + u32.random()
  uint32_t ssrc_;              //Synchronization Source, specify media source
  
  int64_t capture_time_ms_;    //same as capturer.capture_time_ms_
}

===

receiver side

RtpTransport::DemuxPacket

class webrtc::RtpPacketReceived{
...
  NtpTime capture_time_;
  int64_t arrival_time_ms_; //RTP packet arrival time, local internal timestamp
  // RTP Header.
  bool marker_;                //frame end marker
  uint16_t sequence_number_;   //RTP sequence number, start at random(1,32767)
  uint32_t timestamp_;         //sender's rtp timestamp maintained by RTPSenderVideo 
  uint32_t ssrc_;              //Synchronization Source, specify media source
}

RtpVideoStreamReceiver::ReceivePacket /OnReceivedPayloadData

struct webrtc::RTPHeader{
  ...
  bool markerBit;
  uint16_t sequenceNumber;                //RTP sequence, set by sender per RTP packet
  uint32_t timestamp;                     //sender's RTP timestamp  
  uint32_t ssrc;
  RTPHeaderExtension extension;           //contains PlayoutDelay&VideoSendTiming if has
}
class webrtc::RtpDepacketizer::ParsedPayload{
    RTPVideoHeader video;

    const uint8_t* payload;
    size_t payload_length;
}

class webrtc::RTPVideoHeader{
  ...
  bool is_first_packet_in_frame;
  bool is_last_packet_in_frame;
  PlayoutDelay playout_delay;     //playout delay extension
  VideoSendTiming video_timing;   //Video Timing extension, align with sender's webrtc::EncodedImage::timing
}

class webrtc::VCMPacket{
...
  uint32_t timestamp;            //sender's RTP timestamp
  int64_t ntp_time_ms_;
  uint16_t seqNum;
  RTPVideoHeader video_header;
  RtpPacketInfo packet_info;
}
class webrtc::RtpPacketInfo{
  ...
  uint32_t ssrc_;
  uint32_t rtp_timestamp_;        //sender's rtp timestamp
  //https://webrtc.googlesource.com/src/+/refs/heads/master/docs/native-code/rtp-hdrext/abs-capture-time
  absl::optional<AbsoluteCaptureTime> absolute_capture_time_; //
  int64_t receive_time_ms_;       //packet receive time, local internal timestamp 
}

PacketBuffer::InsertPacket

class webrtc::video_coding::RtpFrameObject: public EncodedImage{
...
  RTPVideoHeader rtp_video_header_;
  uint16_t first_seq_num_;
  uint16_t last_seq_num_;
  int64_t last_packet_received_time_;
  int64_t _renderTimeMs;
  //inherit from webrtc::EncodedImage
  uint32_t timestamp_rtp_;											
  int64_t ntp_time_ms_;										     
  int64_t capture_time_ms_;										 
}

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码