跳到正文
版本:预览版

解远端视频流

简介

下图展示了mpegCoder.MpegClient背靠的理论。假设在远端有一个视频服务器正在连续不断地推送实时视频流,即使不读取这个视频流的内容,数据流也不会为了客户端暂停下来。因此,我们设计了以下的双线程工作模式。

当连接到远端服务器时,MpegClient就会创建一个用于提供后台服务的子线程(即图中的writer写者)。即使我们没有进行任何读取操作,该线程也会持续不断地从远端接收视频帧。所接收到的帧会被保存在一个循环缓存里(图中,缓存的大小是12)。读者和写者将各自通过维护一个读指针和一个写指针(即图中分别连接到这些线程的箭头)来控制读写同步。每当接收到新的一帧时,写指针向前步进一格。

读取事件都是由Python-C-API触发的。当一个新的读取事件来到主线程时,读者将会锁住读指针的当前位置,并且从这个位置开始读取需要的数帧。在需要的数据被取出后,读指针将会解锁,并且读指针会更新到读取结束的位置。写者在写新接收到帧的时候,也会通过写指针锁住正在写的位置。当缓存的某个位置被锁住时,从这个位置开始的数据将无法再更新,例如,在读取事件中,如果写指针的位置步进到了读指针锁住的位置,写者就会被阻塞,一直等待到读取事件结束。如果写者被阻塞的时间太长,对远端视频的解流可能失败。所以我们建议用户设置一个合理的缓存大小。例如,如果我们每次需要读5帧,那么建议将缓存大小设置为这个值的2倍,即10帧。

代码

要测试以下代码,我们建议用户使用VLCFFMpeg来推送远端视频流,因为mpegCoder不支持免编码地推送视频流,使用VLC或原生的FFMpeg来做这件事可以占用更少的系统资源。

以下代码接收远端视频流的帧,并缩放到480x360的尺寸,重采样到5 FPS。读取数目和缓存大小分别设为5和12。

client.py
import os, sysimport timeimport mpegCodermpegCoder.setGlobal(dumpLevel=2)  # 显示完整的日志。if __name__ == '__main__':    d = mpegCoder.MpegClient()  # 创建客户端句柄。    d.setParameter(widthDst=480, heightDst=360, dstFrameRate=(5,1), readSize=5, cacheSize=12)  # 进行基础设置。    success = d.FFmpegSetup('rtsp://localhost:8554/video')  # 连接到服务器。    print(d)    if not success:  # 如果没能连接上,就退出程序。用户可以自行删除这段代码,并观察会发生什么现象。        exit()    d.start()  # 启动解流子线程。    time.sleep(5)  # 在读取视频之前,等待几秒。    print('Get slept')    p = d.ExtractFrame()  # 从缓存里读取几帧。    print(p.shape)  # 展示所读取的帧的信息。    for i in range(10):  # 运行50秒。        time.sleep(5)        p = d.ExtractFrame()  # 从缓存里读取几帧。            d.terminate()  # 关闭当前子线程。可以通过start()让该线程重启。    d.clear()  # 但是我们在这里选择清除客户端并退出。

在上例里,设置好客户端后,接下来的代码将会执行以下关键步骤。

  1. MpegClient.FFmpegSetup()接收了一个视频流的地址。视频流的类型将会从协议自动检出。目前,支持httpftpsftprtsprtmp。注意只有rtsprtmp是用来提供实时视频流服务的。httpftpsftp协议主要用来传输数据。调用该方法会建立到远端服务器之间的连接。

  2. 调用MpegClient.start()之后,将会创建一个名为“写者”("writer")的子线程。

  3. 使用MpegClient.ExtractFrame()获取实时数据。返回的帧数由参数设置时给定的readSize决定。当然,用户也可以通过传参覆盖掉这个帧数设置,例如,ExtractFrame(4)就会强制读者返回4帧。

  4. 当远端视频流关闭时,d.ExtractFrame()就会返回None。不过,用户也可以选择在任意时间自行中断写者。调用方法MpegClient.terminate()会关闭子线程。但直到MpegClient.clear()调用的时候,连接才会被释放。

Github上的几个例子

本文的例子已经作为一个单独的分支,提供在了在Github上。

解流检查程序

另外,我们还提供了另一个例子。该例子是基于PyQt5mpegCoder实现的一个简易实时视频流播放器。

视频流播放器