opencore讲解

1 . 简介

       本文档为 PVPlayer 开发者提供详细的 SDK 开发信息 . 包括媒体信息的层次结构,流程的控制和 数据流图,状态机,错误处理,异步事件的处理,以及用例场景。该文档还包括在 android 平台下调试的相关介绍。

1.1 PVPlayer SDK 定义

     PVPlayer SDK is a set of components and modules that allows synchronized playback of multimedia presentations. A multimedia presentation is defined as a collection of various media that are rendered together in some sort of a synchronous manner. This could be in the form of a file encoded into a specific

format (like MP4, 3GPP), a live RTSP streaming session, or a SMIL presentation or any other form.

     In addition to standard playback features such as repositioning and volume control, PVPlayer SDK offers

more sophisticated features such as downloading of content and playback of content as it is being

downloaded. The amount of features contained in a particular PVPlayer SDK depends on the

requirements, design decisions, and limitations imposed by the platforms and chosen design.

1.2 PVPlayer SDK 使用范围

      PVPlayer SDK includes all components needed to satisfy the definition above but excludes the application (graphical or command-line) which uses the PVPlayer SDK, the operating system or platform that PVPlayer SDK runs on and data sources (e.g. multimedia file, streaming server) and sinks (e.g. audio device, display) for the multimedia presentation. The scope of PVPlayer SDK could be further reduced for particular platform with particular feature sets, but this document covers the largest extent of PVPlayer SDK. PVPlayer SDK is composed of and utilizes other components from PacketVideo (e.g. OSCL, PVMF nodes) so certain details might be referred to another document.

 

 

 

 

 

2. 高层次设计 (HLD)

2.1 简介和局限性

     PVPlayer SDK 包含所有必要的特点来支持这个要求先前的部分。这个特性的要求是被设计来处理的很全面的应用程序。这个模块结构和设计的扩展提供了便利的机制,不过由于 opencore 的复杂性,相对起 MPLAYER VLC 等多媒体播放器来说,扩展格式相对麻烦点。而且 PVPlayer 在新版本和升级 PVPlayer SDK , 可能会有不兼容的 BUG

2.2 开发环境和工具

     PVPlayer SDK c++ 语言实现的,所以它需要 ANSI C + + 开发工具支持平台。不过对于开发者不要求每个特征定义为 c++ 标准。例如 ,RTTI 异常处理。然而 ,C + + 编译环境是必需的,在 Android 环境中用的是 G++ 3.3.3 编译, PVPlayer SDK 也需要另一种类型的接口 ( 例如。 C,Java),PVPlayer SDK 提供了一种适配接口 , 但内部部件还需在 c++ 环境下编译 .

     PVPlayer SDK 源代码是基于 PacketVideo 的操作系统 (OSCL) PacketVideo 多媒体框架 (PVMF) PVPlayer SDK 依赖 OSCL 来提供系统的功能 , 是便携式在平台上 ( 例如 , 它作为一个操作系统的抽象层提供了平台的 API 来了 PVPlayer SDK) PVMF 框架是确定多媒体架构包 PVPlayer SDK OSCL 需要一个平台和服务的相对完整操作系统,如动态记忆、管理、多线程、文件 I / O 、网络、域名服务 , 时钟信息。

2.3 结构和组件

PVPlayer SDK 是一个标准架构,结构灵活的、可扩展的 , 轻巧的多媒体播放框架。 PVPlayer 引擎是播放器的核心。引擎使用 PVMF 节点和节点图表数据处理和内部注册时 , 节点的图形结构。下图是 PVPlayer SDK 简单框架图。

    图一    PVPlayer SDK 软件框架图

2.4 控制流

回放控制 PVPlayer SDK user( 这里一般是开发者 ) PVPlayer, 典型的 LINUX 应用程序,通过 BINDLE 一个服务给 JAVA 层调用。 PVPlayer SDK api 提供了如调用初使化、 setdatasourse prepare 、开始(停止)多媒体播放等等。在 PVPlayer SDK 、控制流量通常是自上而下的。这个应用程序请求被 PVPlayer 收到后经引擎适配。这个 PVPlayer 引擎根据先前注册的节点并通过相应的标识控制 PVMF 节点数据。有一些控制数据连接节点之间 , 但主要的控制数据 PVPlayer 引擎和 PVMF 节点之间。

2.5 数据流

     PVPlayer SDK 多媒体数据流过程的通过一个或更多的 PVMF 节点连接在一起。 PVMF 节点类型使用和结构将取决于回放文件源参数的类型进行相应播放操作。文件类型主要是提取文件元数据的具体参数 , PVPlayer 引擎或 PVMF 提取节点 PVPlayer SDK 通过适当的接口返回给用户。

 

3 PVPlayer 引擎设计

       PVPlayer 引擎是 PVPlayer SDK 的心脏。它接收和处理所有 PVPlayer SDK 从用户和管理 PVMF 播放多媒体所需的组成及相关业务的请求。其任务应用和简化高级控制。这个 PVPlayer 引擎也侦测 , 处理 , 和过滤事件和信息生成多媒体播放操作控制。

3.1 PVPlayerInterface API

       PVPlayer 用户界面 PVPlayer 引擎通过 PVPlayerInterface 接口类来确定是否有一种适配接口用户和 PVPlayer 引擎。 PVPlayerInterface 是一种 OSCL-based 接口和遵循公共接口 , 除了多媒体播放特定 api,PVPlayerInterface 提供方法检索 SDK 信息、操纵和取消的命令。并描述 PVPlayerInterface API, 指的是一种 PVPlayerInterface API 文档生成的支持的标记。

3.2 异步操作

       The PVPlayer engine processes most commands initiated by API calls asynchronously. There are some commands that are processed synchronously and they can be differentiated by the return value. Synchronous commands return a PVMF status code which tells the user whether the command succeeded or not and if it did fail, what the error was. All asynchronous commands return a command ID. For the user to be notified of asynchronous command completion, the user must specify a callback handler when instantiating PVPlayer engine via the factory function. When the asynchronous command completes, PVPlayer engine calls the callback handler with the command ID for the command, command status, and any other relevant data. To process the command asynchronously, the PVPlayer engine is implemented as an active object, which gets to run according to the active scheduler running in the thread. The PVPlayer engine expects scheduler to be available when instantiated and the engine itself will not directly create a thread or scheduler.

With asynchronous commands, there is a possibility of commands not completing in expected time. To deal with this issue, PVPlayer engine provides standard PV SDK APIs to cancel a specific or all issued commands. The user of PVPlayer SDK can use these APIs to cancel any request that did not complete in time or are not needed due to changing circumstances. In PVPlayer engine, it might have to deal with lower level components that behave asynchronously. To prevent an unresponsive lower level component from blocking PVPlayer engine operation, PVPlayer engine has timeout handling for any asynchronous commands that it issues. When timeout does occur, the asynchronous command is canceled and is handled appropriately (e.g. command failure, error event).

3.3 事件处理

  The PVPlayer engine notifies the user of errors and other information not related to API calls as unsolicited events. The notification is handled by making a callback on handlers specified by the user of PVPlayer engine. There are two callback handlers, one for error events and one for informational events, that must be specified by the user when instantiating PVPlayer engine via the factory function.

3.4 引擎框架

       下面的图表说明了应用程序使用 PVPlayer 引擎的接口 PVPlayerInterface 时直接适配。 PVPlayerFactory 处理实例化组件和 destory PVPlayerEngine 对象。所有 PVPlayer 引擎的 api 提供 PVPlayerInterface PVPlayerEngine 采用三种回收处理过的应用 ,PVCommandStatusObserver,PVInformationalObserver,PVErrorEventObserver, 通知申请上述指令完成同步误差和信息的事件。

             

                            图二 类图

3.5 状态机

      

                     图三 状态机

       PVPlayer 引擎实例化后状态为 IDLE, IDLE 状态时 , 可以调用 AddDataSource() 来指定需要回放的多媒体数据 , 然后调用 init() 初始化数据并且状态转为 INITIALIZED, 在进入 INITIALIZED

 

状态的时候 , 用户可以获取媒体的 tracks metadata, 并且可以调用 AddDataSink() 去指定具体的 data sinks 去回放 .

 

       在所有的 data sinks 添加后 , 用户调用 Prepare(), 使引擎建立相关的 PVMF 节点 , 并为数据流指定多媒体播放的数据源和数据接收器 , 建立需要播放的数据流队列 . 用户在 PREPARED 状态时调用 Start() 进入到 STARTED 状态 , 启动多媒体播放 . 在调用 Stop() 后回到 INITIALIZED 状态并且刷新多媒体数据流 .

 

       STARTED 状态时 , 用户也可以调用 Pause(). Stop(). 调用 Stop() 后停止回放 , 刷新所有数据流 , 并且使引擎回到初始化状态 . 调用 Pause(), 会停止回放 , 但不会刷新数据流 , 而且可以调用 Resume() 继续从暂停的地方播放 . PAUSED 状态时可以调用 Stop() 使引擎回到初始化状态 .

 

       调用 Stop() 回到 INITIALIZED 状态后 , 数据队列可以通过调用 AddDataSink() RemoveDataSink() 来添加和删除 . 在调用 Prepare()/Start() 重新回放 , 但是关闭回到 IDLE 状态时 , 或需要重新打开另一个媒体文件时 , 需要调用 Reset(). 因为在 IDLE 状态调用 RemoveDataSink() 不能删除所有数据队列 . 调用 Reset() , 又回到起始状态 , 流程如上一样 . 如果用户想退出 PVPlayer, 也可以调用 Reset(), PREPARED, STARTED,PAUSED 状态时都可以调用该函数 .

 

       如果 PVPlayer 引擎收到错误信息或从其它组件传来错误事件后 , 引擎会发送 ERROR 状态并且尝试恢复 . 如果这个错误是不可恢复的 , 刚引擎会清空所有状态和数据列回到 IDLE 状态 . 此时用户应该等待 PVMFInfoErrorHandlingComplete informational event.

 

       以上是这个状态机的简要描述 , 具体参考 OPENCORE 源码 .

 

 

4. 接口

4.1 默认接口

       PVPlayer 的接口是标准的 OSCL-based 接口 , 引擎的 API 都是通过这些接口来调用相对应的操作 , 但是像一些 OSCL 类型的组件和 PVMF 类型的组件里需要注意有些不同 .

 

 

 

4.2 适配层

 

       f the interface to PVPlayer SDK needs to be different than the OSCL-based interface, another interface layer needs to be created to “wrap” around the OSCL-based interface. This “wrapper” is referred to as an

 

adaptation layer for OSCL-based PVPlayer engine interface.One possible reason to create an adaptation layer would be to encapsulate the OSCL interface with types and components of a particular platform or operating system (e.g. ANSI C interface, Symbian interface).Another reason would be that the adaptation layer modifies the interface and behavior of PVPlayer SDK

 

to match the expectation of the application (e.g. legacy interface). The adaptation layer could also combine PVPlayer SDK with another SDK or component to provide a unified interface to the application.The block diagrams below illustrate how the adaptation layer relates to PVPlayer Engine and its OSCL-based interface. The diagram on the right shows the adaptation layer adding more functionality by including another engine.

PvPlayer

  Adapation Layer

 

 

pvPlayer

OSCL Interface

 

 

pvPlayer Engine

 

 

pvPlayer+ Adapation Layer

 

 

Another

Engine

 

 

pvPlayer OSCL

Interface

 

 

pvPlayer Engine

 

 
 

 

 

 

 

 

 

 

 

                                         图四     适配层

4.3 多线程支持

 

       默认的 OSCL-based 接口不支持多线程 , 所以在使用多线程的时候 , 适配层需要提供一个功能来实现 . 一种方法是使用 OSCL proxy 接口组件来提供多线程的支持 . 另一种方法是添加一个另外的 Platform Threading Support. 两种方法的框图如下 .

 

 

 

 

 

 

 

 

 

 

 

 

              图五        多线程支持

 

4.4 媒体数据输出到数据池

 

    PVPlayer 引擎利用 PVMF 节点指定数据输出的数据池 , 但大部份使用的时候 , 同步数据通过适合的渲染后再送到媒体输出设备 . 在输出视频流的时候 , 需要一个显示设备 , 音频流需要一个 PCM 音频输出设备 . 媒体输出设备是一个具体的特定平台 . 输出设备的创建有两个方法可以实现 :

 

       (1) PVMF 节点里封装一个 media device, 引擎可以直接调用 , 这种方法可以最大限度的减少 PVPlayer 和接口层的代码 , 但是需要创建一个新的 PVMF 节点 .

 

Platform-specific

  Output

Media PVMF

  Node

 

 

Output Media

  Device

Interface

 

 

pvPlayer

Engine

 

 

pvPlayer Engine

 

 

PV Media I/O to

Output

Media Device

Interface

Wrapper

 

 

PV Media I/O

PVMF Node

 

 

Output Media

Device

Interface

 

 
       (2) 直接调用 PV media I/O 接口 , I/O 接口中调用输出设备 . 这种方法相对于第一种来说并不需要创建新的 PVMF 节点 , 复杂性也小 . 但增加了代码的层次和和代码量 . 下图是两种方法的引擎 . 更多 PV I/O 接口的信息 , 参考 PV MEDIA I/O datasheet.

 

 

 

 

 

 

 

 

 

 

 

                               

                                             6 媒体输出的两种方法

5. PVMF Node

       本节给出 PVMF 简短的描述节点 , PVPlayer 引擎使用。只有 PVMF 节点 OSCL PVMF 基于组件都包括在内。没有特定于平台 PVMF 节点覆盖。欲了解更多有关特定节点( 1 ,低于或平台特定一)的详细信息,请参阅该节点文件。

5.1 数据池节点 (Data Sink Nodes)

 

       数据池节点是 PVPlayer 的引擎中输出数据前最后的数据终点 .

 

5.1.1 P VMFMediaOutputNode

 

       PVMFMediaOutputNode is a wrapper node around the PV media I/O interface to output data. The node translates node commands and incoming media data to appropriate media I/O actions and handles media I/O events. Using PVMFMediaOutputNode allows encapsulation of platform and device specific output

 

interface with PV media I/O interface.

 

5.1.2 P VMFFileOutputNode

 

       PVMFFileOutputNode accesses the file directly using OSCL file I/O to write media data coming in via the port. The node has some capability to understand format type and to write out data appropriately for the specified format type (AMR file header for AMR IETF format).

 

6. 同步机制

       OPENCORE 官方文档中对时间戳相关介绍 , 有些难懂 , 为了更利于理解 , 我在网上找了一篇简单介绍实时视频的时间戳和视频流畅的关系的文章 , 可以更好的理解 . 最后再附上原翻译

 

              媒体内容在播放时,最令人头痛的就是音视频不同步。从技术上来说,解决音视频同步问题的最佳方案就是时间戳:首先选择一个参考时钟(要求参考时钟上的时间是线性递增的);生成数据流时依据参考时钟上的时间给每个数据块都打上时间戳(一般包括开始时间和结束时间);在播放时,读取数据块上的时间戳,同时参考当前参考时钟上的时间来安排播放(如果数据块的开始时间大于当前参考时钟上的时间,则不急于播放该数据块,直到参考时钟达到数据块的开始时间;如果数据块的开始时间小于当前参考时钟上的时间,则 尽快 播放这块数据或者索性将这块数据 丢弃 ,以使播放进度追上参考时钟)。

 

       可见,避免音视频不同步现象有两个关键 —— 一是在生成数据流时要打上正确的时间戳。如果数据块上打的时间戳本身就有问题,那么播放时再怎么调整也于事无补。如图 2.8 ,视频流内容是从 0s 开始的,假设 10s 时有人开始说话,要求配上音频流,那么音频流的起始时间应该是 10s ,如果时间戳从 0s 或其它时间开始打,则这个混合的音视频流在时间同步上本身就出了问题。打时间戳时,视频流和音频流都是参考参考时钟的时间,而数据流之间不会发生参考关系;也就是说,视频流和音频流是通过一个中立的第三方(也就是参考时钟)来实现同步的。第二个关键的地方,就是在播放时基于时间戳对数据流的控制,也就是对数据块早到或晚到采取不同的处理方法。图 2.8 中,参考时钟时间在 0-10s 内播放视频流内容过程中,即使收到了音频流数据块也不能立即播放它,而必须等到参考时钟的时间达到 10s 之后才可以,否则就会引起音视频不同步问题。

 

    基于时间戳的播放过程中,仅仅对早到的或晚到的数据块进行等待或快速处理,有时候是不够的。如果想要更加主动并且有效地调节播放性能,需要引入一个反馈机制,也就是要将当前数据流速度太快或太慢的状态反馈给 ,让源去放慢或加快数据流的速度。熟悉 DirectShow 的读者一定知道, DirectShow 中的质量控制( Quality Control )就是这么一个反馈机制。 DirectShow 对于音视频同步的解决方案是相当出色的。但 WMF SDK 在播放时只负责将 ASF 数据流读出并解码,而并不负责音视频内容的最终呈现,所以它也缺少这样的一个反馈机制。

 

    为了更好地理解基于时间戳的音视频同步方案,下面举一个生活中的例子。假设你和你的一个朋友约好了今天 18:00 在沪上广场见面,然后一起吃饭,再去打游戏。实际上,这个 18:00 就是你和你朋友保持同步的一个时间点。结果你 17:50 就到了沪上广场,那么你必须等你的朋友。 10 分钟过后,你的朋友还没有到,这时他打来电话说有事耽搁了,要晚一点才能到。你没办法,因为你已经在旁边的餐厅预订了位置,如果不马上赶过去,预订就会被取消,于是你告诉你的朋友直接到餐厅碰头吧,要他加快点。于是在餐厅将来的某个时间点就成为你和你朋友的又一个同步点。虽然具体时间不定(要看你朋友赶过来的速度),但这样努力的方向是对的,你和你朋友肯定能在餐厅见到面。结果呢?你朋友终于在 18:30 赶过来了,你们最终 同步 了。吃完饭 19:30 了,你临时有事要处理一下,于是跟你朋友再约好了 20:00 在附近的一家游戏厅碰头。你们又不同步了,但在游戏厅将来的某个时间点你们还是会再次同步的。

 

    悟出什么道理了没有?其实,同步是一个动态的过程,是一个有人等待、有人追赶的过程。同步只是暂时的,而不同步才是常态。人们总是在同步的水平线上振荡波动,但不会偏离这条基线太远。

 

这部份有人已经翻译过了,我就直接拿过来了,如果译者有意见,麻烦通知我,我会立即删除的

6.Android OpenCore A/V 同步机制

 

 

PVPlayer 在渲染 (render) 所有多媒体数据是都需要保持一个暂时的同步,也就是通常所说的 A/V 同步。为了达到同步,需要如下一些信息:媒体回放的时钟,媒体数据的时间戳,从 Sink 中获取的时间信息(比如从音频设备设定的特定的采样率来获取的播放速率)。图 1 描述了与同步相关的 PVPlayer 模块之间的关系。

1 与同步相关的模块及关系

一、媒体时钟

 

PVMFMediaClock ,媒体时钟主要负责维持一个时间的引用,从而保持媒体回放的节奏,获取和实现媒体播放的同步。

 

 

 

1 、媒体时钟的特点

 

1.1 时间源

 

媒体时钟可以作为一个时间源提供给多媒体,它本身可能来自于系统时钟或其他时间源(比如音频设备时钟)。它可以给多媒体提供一个时间基准,同时来维护该时间基准。

 

 

 

1.2 时钟观察者

 

媒体时钟可以把自己作为一个观察者,来通知对象时钟状态的改变。以下接口实现了其作为观察者的角色:

 

PVMFMediaClockObserver :用来通知时钟基值,时钟计数的更新,时钟的调整;

 

PVMFMediaClockStateObserver :用来通知时钟状态的改变;

 

PVMFMediaClockNotificationsObs :用来获取回调通知。

 

 

 

1.3NPT 映射

 

媒体时钟是一个单调递增的时钟,而媒体在播放时却可能需要 Seek 到任意位置,为了控制媒体的播放,使其正确 Render ,媒体时钟需要在媒体时钟时间和 NPT 之间维护一个 NPT normal play time )映射,任意对媒体播放位置的改变将会通知进行一次映射。图 2 描述了媒体时钟和 NPT 之间的映射。

 

图中的箭头和带颜色的区段描述了 Seek 的次序以及每次播放的时间段, Media clock NPT 之间相同的颜色区间即对应了相应的映射。由此可以看出其映射公式为(以第二段为例): NPT media_time - 5550 + 380)

 

 

 

1.4 时钟的回调

 

在媒体时钟上设置回调是组件采取动作的基础,这些回调可以减少在时钟发生 改变时,组件自己需要设置他们的时钟。媒体时钟采用输入特定时间窗口来取 代绝对时间,这样可以使得处于竞争状态的任务或线程可以尽可能早的得到响 应。

 

 

 

 

1.5 延迟处理

 

当集成了多个不同媒体流的 Sinks 来输出一个多媒体时,每个 Sink 都可能会有 不同程度的延迟,为了弥补不同媒体流之间的延迟从而同步播放,就需要进行 延迟处理。每个 Sink 都向媒体时钟注册自己的延迟,最后由媒体时钟来调整最 终的调度的延迟。

 

 

 

 

1.6 NPT 时钟转换

 

当一个新的 NPT 开始时,用户可以给媒体时间设置一个绝对时间。用户还可以 任意调整 NPT 的方向(比如向前,向后)。

 

 

 

二、时间戳

 

为了及时准确地输出媒体数据,就不得不考虑媒体数据中包含的时间戳信息以及媒体回放时钟。如果时间戳值等于当前回放时间,则媒体数据是同步的,需要进行 Render ;如果时间戳小于当前回放时间,则说明媒体数据到达时间晚了;反之,如果时间戳大于当前回放时间,则说明媒体数据到达时间早了。如何处理这些来早的或来晚的媒体数据则取决于 PVPlayer 引擎的配置,通常情况下,来早的数据需要等待,直到播放时间到达;来晚的数据则会被丢弃而不被 Render 。但有时候来晚的数据也会被 Render

 

 

 

三、同步音频

 

音频数据的 Render 通常不需要外部时钟来进行同步,因为音频设备通常会被配置一定的采样率来消化音频数据。因此,音频设备被配置的这个采样率通常也作为媒体回放时钟的速率。

 

 

 

1 Render 开始时的同步

 

一旦媒体时钟开始后,就必须要求媒体数据尽可能快地被 Render ,然而硬件在 Render 时很可能需要额外的时间,或者硬件需要等到更多的媒体数据被缓存。因此会导致媒体时钟的开始时间与媒体数据真正被输出的时间不一致,从而导致 PVPlayer 报告给应用程序的播放进度与真实播放进度产生误差。为了解决这一问题,采用了在硬件没有开始输出媒体数据时,媒体时钟就处于暂停状态,当硬件开始输出媒体数据时给媒体时钟发一个消息来通知媒体时钟也开始运行。这样就可以保证在媒体时钟开始的时候,媒体数据也开始进行输出。

 

 

3 开始输出的同步示例

3 描述了在有一定初始延迟时开始输出的同步例子。

 

作为一个主动态的 MIO 组件,无论时钟状态为暂停还是运行, PVPlayer 都向 MIO 组件传送数据,而 MIO 组件也应该继续接受和缓存数据,并决定何时把数据发送到硬件。

 

而作为一个被动态的 MIO 组件,只有当时钟状态为运行时, PVPlayer 才向 MIO 组件发送数据,而接收到数据的 MIO 组件也需要将收到的数据立即发送给硬件。

2 、重新定位后的同步

 

当应用程序请求重新定位媒体播放位置时, MIO 组件和硬件缓存的数据需要被立即释放,并且在新的位置开始播放。

 

4 描述了重新定位后如何实现媒体的同步的示例。

4 重新定位后的同步示例

3 、播放中的同步

尽管硬件消费数据的速率应该与媒体时钟的速率是一致的,但毕竟他们是单独运行的,因此不可避免的会有一些不同。通常情况下,这中差距是很小的,然而随着播放的进行,差距将会被积累,最终导致不同步。

5 播放中的同步示例

因此,为了控制在短时间内播放时钟与音频输出进程差距在很小的范围内,需要不时地调整两者之间的差,使之小于一个特定的阈值。

 

 

 

四、同步视频

 

与音频输出相比,视频的输出需要参考一个时钟来决定何时输出一个特定的视频帧,视频帧的输出要尽可能的与该帧的时间戳相一致。 PVPlayer 维护了一个与音频播放同步的播放时钟,因此,一旦视频输出与播放时钟同步,那么也就意味着视频输出与音频输出同步。

 

 

 

五、音视频同步

 

音视频同步是音频同步和视频同步的终极目标。在 PVPlayer 架构中,媒体时钟需要调整以便与音频输出过程相一致,而对于视频输出来说,在输出一个视频帧时要使得该帧的时间戳与媒体时钟同步。因此音频输出设备、视频帧的时间戳与媒体时钟的结合便造就了 A/V 同步。

 

6 描述了参与音视频同步模块之间的交互过程。

 

6 A /V 同步交互过程

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值