Page Multiplexing and Ordering in a Physical Ogg Stream原文
Page Multiplexing and Ordering in a Physical Ogg Stream 物理Ogg流中的页面多路复用和排序
The low-level mechanisms of an Ogg stream (as described in the Ogg Bitstream Overview) provide means for mixing multiple logical streams and media types into a single linear-chronological stream. This document specifies the high-level arrangement and use of page structure to multiplex multiple streams of mixed media type within a physical Ogg stream. Ogg流的低级机制(如Ogg位流概述中所述)提供了将多个逻辑流和媒体类型混合到单个线性时序流中的方法。该文档指定了页面结构的高级排列和使用,以在物理Ogg流中多路混合媒体类型的多个流。
Design Elements 设计元素
The design and arrangement of the Ogg container format is governed by several high-level design decisions that form the reasoning behind specific low-level design decisions. Ogg容器格式的设计和排列受几个高级设计决策支配,这些决策构成了特定的低级设计决策的依据。
Linear media 线性媒体
The Ogg bitstream is intended to encapsulate chronological, time-linear mixed media into a single delivery stream or file. The design is such that an application can always encode and/or decode a full-featured bitstream in one pass with no seeking and minimal buffering. Seeking to provide optimized encoding (such as two-pass encoding) or interactive decoding (such as scrubbing or instant replay) is not disallowed or discouraged, however no bitstream feature must require nonlinear operation on the bitstream. Ogg比特流旨在将按时间顺序,时间线性的混合媒体封装到单个传输流或文件中。这种设计使得应用程序始终可以在一次遍历中对功能齐全的比特流进行编码和/或解码,而无需寻找和最小化缓冲。寻求或建议不要提供优化的编码(例如两次通过编码)或交互式解码(例如擦洗或即时重放),但是,没有比特流功能必须对比特流进行非线性操作。
Multiplexing 多路复用
Ogg bitstreams multiplex multiple logical streams into a single physical stream at the page level. Each page contains an abstract time stamp (the Granule Position) that represents an absolute time landmark within the stream. After the pages representing stream headers (all logical stream headers occur at the beginning of a physical bitstream section before any logical stream data), logical stream data pages are arranged in a physical bitstream in strict non-decreasing order by chronological absolute time as specified by the granule position. Ogg位流在页面级别将多个逻辑流多路复用为单个物理流。每个页面都包含一个抽象时间戳(粒度位置),代表流中的绝对时间界标。在表示流头的页面(所有逻辑流头在任何逻辑流数据之前的物理位流部分的开头出现)之后,逻辑流数据页面按照严格的非降序按时间绝对绝对时间排列在物理位流中,如下所示:颗粒位置。 The only exception to arranging pages in strictly ascending time order by granule position is those pages that do not set the granule position value. This is a special case when exceptionally large packets span multiple pages; the specifics of handling this special case are described later under 'Continuous and Discontinuous Streams'. 按颗粒位置严格按时间升序排列页面的唯一例外是未设置颗粒位置值的页面。当异常大的数据包跨越多个页面时,这是一种特殊情况。稍后在“连续和不连续流”中介绍处理此特殊情况的细节。
Seeking
Ogg is designed to use an interpolated bisection search to implement exact positional seeking. Interpolated bisection search is a spec-mandated mechanism. Ogg设计为使用插值对分搜索来实现精确的位置搜索。内插二等分搜索是一种规范要求的机制。 An index may improve objective performance, but it seldom improves subjective performance outside of a few high-latency use cases and adds no additional functionality as bisection search delivers the same functionality for both one- and two-pass stream types. For these reasons, use of indexes is discouraged, except in cases where an index provides demonstrable and noticeable performance improvement. 索引可以提高目标性能,但是在一些高延迟用例之外,它很少提高主观性能,并且由于二等分搜索为一遍和两遍流类型提供相同的功能,因此几乎没有添加任何其他功能。由于这些原因,不鼓励使用索引,除非索引提供了明显且显着的性能改进。 Seek operations are by absolute time; a direct bisection search must find the exact time position requested. Information in the Ogg bitstream is arranged such that all information to be presented for playback from the desired seek point will occur at or after the desired seek point. Seek operations are neither 'fuzzy' nor heuristic. 搜寻操作是按绝对时间进行的;直接二等分搜索必须找到所需的确切时间位置。 Ogg比特流中的信息被安排成使得将要呈现以从期望的搜索点回放的所有信息将出现在期望的搜索点处或之后。搜寻操作既不“模糊”也不启发式。 Although key frame handling in video appears to be an exception to "all needed playback information lies ahead of a given seek", key frames can still be handled directly within this indexless framework. Seeking to a key frame in video (as well as seeking in other media types with analogous restraints) is handled as two seeks; first a seek to the desired time which extracts state information that decodes to the time of the last key frame, followed by a second seek directly to the key frame. The location of the previous key frame is embedded as state information in the granulepos; this mechanism is described in more detail later. 尽管视频中的关键帧处理似乎是“所有必需的播放信息都位于给定搜索之前”的例外,但关键帧仍可以在此无索引框架中直接处理。在视频中寻找关键帧(以及在具有类似限制的其他媒体类型中寻找)被视为两次寻找。首先,寻找所需时间,提取状态信息,该状态信息解码到最后一个关键帧的时间,然后进行第二次直接寻找关键帧的时间。前一个关键帧的位置作为状态信息嵌入到颗粒中;此机制将在后面详细介绍。
Continuous and Discontinuous Streams
Logical streams within a physical Ogg stream belong to one of two categories, "Continuous" streams and "Discontinuous" streams. Although these are discussed in more detail later, the distinction is important to a high-level understanding of how to buffer an Ogg stream. 物理Ogg流中的逻辑流属于以下两个类别之一:“连续”流和“不连续”流。尽管稍后将更详细地讨论这些内容,但是这种区别对于高级了解如何缓冲Ogg流非常重要。 A stream that provides a gapless, time-continuous media type with a fine-grained timebase is considered to be 'Continuous'. A continuous stream should never be starved of data. Clear examples of continuous data types include broadcast audio and video. 提供具有细粒度时基的无间隙,时间连续的媒体类型的流被认为是“连续的”。连续的数据流永远不会饿死数据。连续数据类型的清晰示例包括广播音频和视频。 A stream that delivers data in a potentially irregular pattern or with widely spaced timing gaps is considered to be 'Discontinuous'. A discontinuous stream may be best thought of as data representing scattered events; although they happen in order, they are typically unconnected data often located far apart. One possible example of a discontinuous stream types would be captioning. Although it's possible to design captions as a continuous stream type, it's most natural to think of captions as widely spaced pieces of text with little happening between. 以潜在的不规则模式或间隔较大的时间间隔传送数据的流被认为是“不连续的”。最好将不连续流视为代表分散事件的数据。尽管它们是按顺序发生的,但它们通常是通常彼此相距很远的未连接数据。不连续流类型的一个可能示例是字幕。尽管可以将字幕设计为连续流类型,但将字幕视为间隔很远的文本片段却很少发生是很自然的。 The fundamental design distinction between continuous and discontinuous streams concerns buffering. 连续流和不连续流之间的基本设计区别涉及缓冲。
Buffering
Because a continuous stream is, by definition, gapless, Ogg buffering is based on the simple premise of never allowing any active continuous stream to starve for data during decode; buffering proceeds ahead until all continuous streams in a physical stream have data ready to decode on demand. 根据定义,由于连续流是无间隙的,因此Ogg缓冲基于这样一个简单的前提,即在解码过程中绝不允许任何活动的连续流饿死数据。缓冲将继续进行,直到物理流中的所有连续流都准备好按需解码的数据为止。 Discontinuous stream data may occur on a fairly regular basis, but the timing of, for example, a specific caption is impossible to predict with certainty in most captioning systems. Thus the buffering system should take discontinuous data 'as it comes' rather than working ahead (for a potentially unbounded period) to look for future discontinuous data. As such, discontinuous streams are ignored when managing buffering; their pages simply 'fall out' of the stream when continuous streams are handled properly. 不连续的流数据可能会定期出现,但是在大多数字幕系统中,无法确定特定字幕的时间安排。因此,缓冲系统应“按需”获取不连续数据,而不是提前进行工作(可能无限制的时间)以查找将来的不连续数据。这样,在管理缓冲时,不连续的流将被忽略;如果正确地处理了连续流,它们的页面只会从流中“掉出来”。 Buffering requirements need not be explicitly declared or managed for the encoded stream; the decoder simply reads as much data as is necessary to keep all continuous stream types gapless (also ensuring discontinuous data arrives in time) and no more, resulting in optimum implicit buffer usage for a given stream. Because all pages of all data types are stamped with absolute timing information within the stream, inter-stream synchronization timing is always explicitly maintained without the need for explicitly declared buffer-ahead hinting. 无需为编码流明确声明或管理缓冲要求;解码器简单地读取所需的数据,以保持所有连续流类型无间隙(也确保不连续数据及时到达),并且不再读取更多数据,从而为给定流提供最佳的隐式缓冲区使用率。因为所有数据类型的所有页面在流中都标有绝对定时信息,所以流间同步定时始终被显式维护,而无需显式声明的提前缓冲提示。 Further details, mechanisms and reasons for the differing arrangement and behavior of continuous and discontinuous streams is discussed later. 稍后讨论连续流和不连续流的不同排列和行为的其他详细信息,机制和原因。
Whole-stream navigation
Ogg is designed so that the simplest navigation operations treat the physical Ogg stream as a whole summary of its streams, rather than navigating each interleaved stream as a separate entity. Ogg的设计使最简单的导航操作将物理Ogg流视为其流的整体摘要,而不是将每个交错的流作为单独的实体进行导航。 First Example: seeking to a desired time position in a multiplexed (or unmultiplexed) Ogg stream can be accomplished through a bisection search on time position of all pages in the stream (as encoded in the granule position). More powerful searches (such as a key frame-aware seek within video) are also possible with additional search complexity, but similar computational complexity. 第一个示例:通过对流中所有页面的时间位置进行二等分搜索(以颗粒位置编码),可以在多路复用(或非多路复用)Ogg流中寻找所需的时间位置。还可以进行更强大的搜索(例如,在视频内进行关键帧感知的搜索),同时增加搜索复杂度,但计算复杂度也相似。 Second Example: A bitstream section may consist of three multiplexed streams of differing lengths. The result of multiplexing these streams should be thought of as a single mixed stream with a length equal to the longest of the three component streams. Although it is also possible to think of the multiplexed results as three concurrent streams of different lengths and it is possible to recover the three original streams, it will also become obvious that once multiplexed, it isn't possible to find the internal lengths of the component streams without a linear search of the whole bitstream section. However, it is possible to find the length of the whole bitstream section easily (in near-constant time per section) just as it is for a single-media unmultiplexed stream. 第二个例子:一个比特流段可以由三个不同长度的复用流组成。多路复用这些流的结果应被视为长度等于三个分量流中最长的单个混合流。尽管也可以将多路复用结果视为三个不同长度的并发流,并且有可能恢复三个原始流,但也很明显,一旦多路复用,就不可能找到其内部长度。分量流而无需线性搜索整个位流部分。但是,可以像在单媒体非多路复用流中一样容易地找到整个位流段的长度(每段接近恒定的时间)。
Granule Position
Description
The Granule Position is a signed 64 bit field appearing in the header of every Ogg page. Although the granule position represents absolute time within a logical stream, its value does not necessarily directly encode a simple timestamp. It may represent frames elapsed (as in Vorbis), a simple timestamp, or a more complex bit-division encoding (such as in Theora). The exact encoding of the granule position is up to a specific codec. 颗粒位置是一个有符号的64位字段,出现在每个Ogg页的页眉中。尽管颗粒位置表示逻辑流中的绝对时间,但其值不一定直接编码简单的时间戳。它可以表示经过的帧(如在Vorbis中),简单的时间戳或较复杂的位分割编码(如在Theora中)。颗粒位置的确切编码取决于特定的编解码器。 The granule position is governed by the following rules: 颗粒位置受以下规则支配: Granule Position must always increase forward or remain equal from page to page, be unset, or be zero for a header page. The absolute time to which any correct sequence of granule position maps must similarly always increase forward or remain equal. (A codec may make use of data, such as a control sequence, that only affects codec working state without producing data and thus advancing granule position and time. Although the packet sequence number increases in this case, the granule position, and thus the time position, do not.) 粒度位置必须始终向前增加或在页面之间保持相等,未设置或对于标题页为零。类似地,任何正确位置的颗粒位置图序列必须始终绝对增加或保持相等的绝对时间。 (编解码器可以使用诸如控制序列之类的数据,这些数据仅影响编解码器的工作状态而不会产生数据,因此不会提前颗粒位置和时间。尽管在这种情况下,数据包序列号会增加,但是颗粒位置和时间都会增加位置,不要。) Granule position may only be unset if there no packet defining a time boundary on the page (that is, if no packet in a continuous stream ends on the page, or no packet in a discontinuous stream begins on the page. This will be discussed in more detail under Continuous and Discontinuous streams). A codec must be able to translate a given granule position value to a unique, deterministic absolute time value through direct calculation. A codec is not required to be able to translate an absolute time value into a unique granule position value. 仅当在页面上没有定义时间边界的数据包(即,连续流中没有数据包在页面上结束,或者不连续流中没有数据包在页面上开始)时,才可以设置颗粒位置。连续和不连续流下的更多详细信息)。 编解码器必须能够通过直接计算将给定的颗粒位置值转换为唯一的确定性绝对时间值。不需要编解码器就能将绝对时间值转换为唯一的颗粒位置值。 Codecs shall choose a granule position definition that allows that codec means to seek as directly as possible to an immediately decodable point, such as the bit-divided granule position encoding of Theora allows the codec to seek efficiently to key frame without using an index. That is, additional information other than absolute time may be encoded into a granule position value so long as the granule position obeys the above points. 编解码器应选择一种颗粒位置定义,以使该编解码器能够尽可能直接地寻找可立即解码的点,例如Theora的按位划分的颗粒位置编码允许编解码器在不使用索引的情况下有效地查找关键帧。即,可以将除绝对时间以外的附加信息编码为颗粒位置值,只要该颗粒位置遵守以上几点即可。
Example: timestamp
In general, a codec/stream type should choose the simplest granule position encoding that addresses its requirements. The examples here are by no means exhaustive of the possibilities within Ogg. 通常,编解码器/流类型应选择能够满足其要求的最简单的颗粒位置编码。这里的示例绝不是Ogg内的所有可能性的穷举。 A simple granule position could encode a timestamp directly. For example, a granule position that encoded milliseconds from beginning of stream would allow a logical stream length of over 100,000,000,000 days before beginning a new logical stream (to avoid the granule position wrapping). 一个简单的颗粒位置可以直接编码一个时间戳。例如,从流开始编码毫秒的颗粒位置将允许逻辑流长度超过100,000,000,000天,然后再开始新的逻辑流(以避免颗粒位置包裹)。
Example: framestamp
A simple millisecond timestamp granule encoding might suit many stream types, but a millisecond resolution is inappropriate to, eg, most audio encodings where exact single-sample resolution is generally a requirement. A millisecond is both too large a granule and often does not represent an integer number of samples. 简单的毫秒级时间戳粒度编码可能适合许多流类型,但是毫秒级分辨率不适用于例如通常要求精确的单样本分辨率的大多数音频编码。毫秒既太大,又不代表整数的样本。 In the event that audio frames are always encoded as the same number of samples, the granule position could simply be a linear count of frames since beginning of stream. This has the advantages of being exact and efficient. Position in time would simply be [granule_position] * [samples_per_frame] / [samples_per_second]. 在音频帧始终被编码为相同数量的样本的情况下,自流开始以来,颗粒位置可能只是帧的线性计数。这具有精确和有效的优点。时间位置只是[granule_position] * [samples_per_frame] / [samples_per_second]。
Example: samplestamp (Vorbis)
Frame counting is insufficient in codecs such as Vorbis where an audio frame [packet] encodes a variable number of samples. In Vorbis's case, the granule position is a count of the number of raw samples from the beginning of stream; the absolute time of a granule position is [granule_position] / [samples_per_second]. 在诸如Vorbis之类的编解码器中,帧计数不足,其中音频帧[packet]对可变数量的样本进行编码。在Vorbis的情况下,颗粒位置是从流的开始算起的原始样品数的计数。颗粒位置的绝对时间为[granule_position] / [samples_per_second]。
Example: bit-divided framestamp (Theora)
Some video codecs may be able to use the simple framestamp scheme for granule position. However, most modern video codecs introduce at least the following complications: 一些视频编解码器可能能够使用简单的帧标记方案进行颗粒定位。但是,大多数现代视频编解码器至少会带来以下复杂问题: video frames are relatively far apart compared to audio samples; for this reason, the point at which a video frame changes to the next frame is usually a strictly defined offset within the frame 'period'. That is, video at 50fps could just as easily define frame transitions <.015, .035, .055...> as at <.00, .02, .04...>. frame rates often include drop-frames, leap-frames or other rational-but-non-integer timings. 与音频样本相比,视频帧相距较远;因此,视频帧更改为下一帧的点通常是帧“期间”内严格定义的偏移。也就是说,以50fps的视频可以像定义<.00,.02,.04 ...>一样容易地定义帧过渡<.015,.035,.055 ...>。 帧速率通常包括丢帧,跳帧或其他有理但非整数的时序。 Decode must begin at a 'key frame' or 'I frame'. Keyframes usually occur relatively seldom. 解码必须从“关键帧”或“ I帧”开始。关键帧通常很少出现。 The first two points can be handled straightforwardly via the fact that the codec has complete control mapping granule position to absolute time; non-integer frame rates and offsets can be set in the codec's initial header, and the rest is just arithmetic. 通过编解码器具有将粒子位置完整映射到绝对时间的完整控制,就可以直接处理前两点。可以在编解码器的初始标头中设置非整数帧速率和偏移量,其余的只是算术运算。 The third point appears trickier at first glance, but it too can be handled through the granule position mapping mechanism. Here we arrange the granule position in such a way that granule positions of key frames are easy to find. Divide the granule position into two fields; the most-significant bits are an absolute frame counter, but it's only updated at each key frame. The least significant bits encode the number of frames since the last key frame. In this way, each granule position both encodes the absolute time of the current frame as well as the absolute time of the last key frame. 乍一看,第三点似乎比较棘手,但是也可以通过颗粒位置映射机制来处理。在这里,我们以易于找到关键帧的颗粒位置的方式排列颗粒位置。将颗粒位置分为两个字段;最重要的位是绝对帧计数器,但仅在每个关键帧处更新。最低有效位编码自上一个关键帧以来的帧数。这样,每个颗粒位置都可以编码当前帧的绝对时间以及最后一个关键帧的绝对时间。 Seeking to a most recent preceding key frame is then accomplished by first seeking to the original desired point, inspecting the granulepos of the resulting video page, extracting from that granulepos the absolute time of the desired key frame, and then seeking directly to that key frame's page. Of course, it's still possible for an application to ignore key frames and use a simpler seeking algorithm (decode would be unable to present decoded video until the next key frame). Surprisingly many player applications do choose the simpler approach. 然后,通过首先寻找到原始的所需点,检查所得视频页面的粒度,从该粒度中提取所需关键帧的绝对时间,然后直接寻找该关键帧的绝对时间,来寻找最近的关键帧。页。当然,应用程序仍然可以忽略关键帧并使用更简单的查找算法(解码将无法在下一个关键帧之前显示解码的视频)。令人惊讶的是,许多播放器应用程序确实选择了更简单的方法。
granule position, packets and pages颗粒位置,包装和页面
Although each packet of data in a logical stream theoretically has a specific granule position, only one granule position is encoded per page. It is possible to encode a logical stream such that each page contains only a single packet (so that granule positions are preserved for each packet), however a one-to-one packet/page mapping is not intended to be the general case. 尽管理论上逻辑流中的每个数据包都具有特定的粒度位置,但是每页仅编码一个粒度位置。可以对逻辑流进行编码,以使每个页面仅包含一个数据包(这样就可以为每个数据包保留颗粒位置),但是通常情况下并不打算采用一对一的数据包/页面映射。 Because Ogg functions at the page, not packet, level, this once-per-page time information provides Ogg with the finest-grained time information is can use. Ogg passes this granule positioning data to the codec (along with the packets extracted from a page); it is the responsibility of codecs to track timing information at granularities finer than a single page. 因为Ogg是在页面而不是在数据包级别上起作用,所以此每页一次的时间信息为Ogg提供了最细粒度的时间信息。 Ogg将此颗粒定位数据(连同从页面提取的数据包)传递给编解码器;编解码器有责任以比单个页面更精细的粒度跟踪计时信息。
start-time and end-time positioning
A granule position represents the instantaneous time location between two pages. However, continuous streams and discontinuous streams differ on whether the granulepos represents the end-time of the data on a page or the start-time. Continuous streams are 'end-time' encoded; the granulepos represents the point in time immediately after the last data decoded from a page. Discontinuous streams are 'start-time' encoded; the granulepos represents the point in time of the first data decoded from the page. 颗粒位置表示两页之间的瞬时时间位置。但是,连续流和不连续流在粒度表示页面上数据的结束时间还是开始时间方面有所不同。连续流是“结束时间”编码的; granulepos表示从页面解码的最后一个数据之后紧接的时间点。不连续的流是“开始时间”编码的; granulepos表示从页面解码的第一个数据的时间点。 An Ogg stream type is declared continuous or discontinuous by its codec. A given codec may support both continuous and discontinuous operation so long as any given logical stream is continuous or discontinuous for its entirety and the codec is able to ascertain (and inform the Ogg layer) as to which after decoding the initial stream header. The majority of codecs will always be continuous (such as Vorbis) or discontinuous (such as Writ). Ogg流类型由其编解码器声明为连续或不连续。给定的编解码器可以支持连续和不连续的操作,只要任何给定的逻辑流在整体上是连续的或不连续的,并且编解码器就能够确定(并通知Ogg层)在解码初始流头之后是哪个。大多数编解码器将始终是连续的(例如Vorbis)或不连续的(例如Writ)。 Start- and end-time encoding do not affect multiplexing sort-order; pages are still sorted by the absolute time a given granulepos maps to regardless of whether that granulepos represents start- or end-time. 开始时间和结束时间编码不影响多路复用排序顺序;仍然按照给定的granulepos映射到的绝对时间对页面进行排序,无论该granulepos代表开始时间还是结束时间。
Multiplex/Demultiplex Division of Labor多工/多工分工
The Ogg multiplex/demultiplex layer provides mechanisms for encoding raw packets into Ogg pages, decoding Ogg pages back into the original codec packets, determining the logical structure of an Ogg stream, and navigating through and synchronizing with an Ogg stream at a desired stream location. Strict multiplex/demultiplex operations are entirely in the Ogg domain and require no intervention from codecs. Ogg复用/解复用层提供了以下机制:将原始数据包编码为Ogg页面,将Ogg页面解码回原始编解码器数据包,确定Ogg流的逻辑结构,并在所需的流位置导航Ogg流并与之同步。严格的复用/解复用操作完全在Ogg域中,不需要编解码器的干预。 Implementation of more complex operations does require codec knowledge, however. Unlike other framing systems, Ogg maintains strict separation between framing and the framed bitstream data; Ogg does not replicate codec-specific information in the page/framing data, nor does Ogg blur the line between framing and stream data/metadata. Because Ogg is fully data-agnostic toward the data it frames, operations which require specifics of bitstream data (such as 'seek to key frame') also require interaction with the codec layer (because, in this example, the Ogg layer is not aware of the concept of key frames). This is different from systems that blur the separation between framing and stream data in order to simplify the separation of code. The Ogg system purposely keeps the distinction in data simple so that later codec innovations are not constrained by framing design. 但是,执行更复杂的操作确实需要编解码器知识。与其他成帧系统不同,Ogg在成帧和成帧的比特流数据之间保持严格的分隔。 Ogg不会在页面/框架数据中复制特定于编解码器的信息,也不会模糊框架和流数据/元数据之间的界限。因为Ogg对其帧数据完全不了解数据,所以需要特定位流数据的操作(例如“搜索关键帧”)也需要与编解码器层进行交互(因为在此示例中,Ogg层不知道关键帧的概念)。这与模糊框架和流数据之间的分隔以简化代码分隔的系统不同。 Ogg系统有目的地使数据之间的区别保持简单,从而使以后的编解码器创新不受框架设计的限制。 For this reason, however, complex seeking operations require interaction with the codecs in order to decode the granule position of a given stream type back to absolute time or in order to find 'decodable points' such as key frames in video. 但是,由于这个原因,复杂的搜索操作需要与编解码器进行交互,以便将给定流类型的颗粒位置解码回绝对时间,或者找到“可解码点”,例如视频中的关键帧。
Unsorted Discussion Points未分类的讨论点
flushes around key frames? RFC suggestion: repaginating or building a stream this way is nice but not required 在关键帧周围刷新? RFC建议:以这种方式重新分页或构建流很好,但不是必需的
Appendix A: multiplexing examples
附录A:复用示例