3

背景

我正在使用 Boost 在 C++ 中开发一个渐进式下载媒体流服务器。典型配置是运行 Android 4.2.2 的 Android 渲染设备,使用图库播放器作为媒体播放器,以及在 Windows 桌面上运行的媒体流服务器。Android 设备通过 HTTP URL 请求媒体文件,媒体服务器使用渐进式下载流式传输文件。

问题

当尝试以 20 Mbps 的内部比特率流式传输视频文件时,渲染器会多次停止。典型的流媒体体验基本上由多次出现的步骤组成:

  1. 平滑渲染 3-5 秒
  2. 视频停顿 5-10 秒
  3. 转到步骤 1

最合乎逻辑的解释是渲染器正在经历“缓冲区欠载”或“缓冲区下溢”

问题

有什么方法可以解决缓冲区欠载问题、提高媒体流输出速率并防止视觉停滞/阻塞?

技术信息

服务器代码如下所示:

void StreamFile (boost::asio::ip::tcp::socket *socket, const wchar_t *path)
{
    . . .
    for (long offset=startOffset; offset <= endOffset; offset+=streamingBlockSize)
    {
        long numBytesToRead = (std::min<long>) (endOffset - offset + 1, streamingBlockSize);
        fread (buffer, 1, numBytesToRead, f);
        if (RawSocketWrite (socket, buffer, numBytesToRead) == 0)
        {
            // RawSocketWrite() encountered a serious error, exit
            break;
        }
    }
    . . .
}

size_t RawSocketWrite (boost::asio::ip::tcp::socket *socket, const char *data, size_t len)
{
    size_t numCharsWritten = 0;

    try
    {
        numCharsWritten = boost::asio::write (socket, boost::asio::buffer (data, len));
    }
    catch (boost::system::system_error& e)
    {
        LOG_ERROR (("error", "write() failed in RawSocketWrite (socket %d) %s", socket->native (), e.what()));
        numCharsWritten = 0;
    }

    return numCharsWritten;
}

我正在尝试使用以下文件数据流式传输 39 MB、16 秒的视频文件(由 MediaInfo 提供):

Video information

General
Complete name                            : TestVideo.mp4
Format                                   : MPEG-4
Format profile                           : Base Media
Codec ID                                 : isom
File size                                : 38.6 MiB
Duration                                 : 16s 102ms
Overall bit rate                         : 20.1 Mbps

Video
ID                                       : 1
Format                                   : AVC
Format/Info                              : Advanced Video Codec
Format profile                           : High@L4.0
Format settings, CABAC                   : Yes
Format settings, ReFrames                : 1 frame
Format settings, GOP                     : M=1, N=61
Codec ID                                 : avc1
Codec ID/Info                            : Advanced Video Coding
Duration                                 : 15s 701ms
Bit rate                                 : 20.0 Mbps
Width                                    : 1 920 pixels
Height                                   : 1 080 pixels
Display aspect ratio                     : 16:9
Frame rate mode                          : Variable
Frame rate                               : 30.000 fps
Minimum frame rate                       : 29.732 fps
Maximum frame rate                       : 30.313 fps
Color space                              : YUV
Chroma subsampling                       : 4:2:0
Bit depth                                : 8 bits
Scan type                                : Progressive
Bits/(Pixel*Frame)                       : 0.322
Stream size                              : 37.5 MiB (97%)
Title                                    : VideoHandle
Language                                 : English
mdhd_Duration                            : 15701

Audio
ID                                       : 2
Format                                   : AAC
Format/Info                              : Advanced Audio Codec
Format profile                           : LC
Codec ID                                 : 40
Duration                                 : 16s 102ms
Source duration                          : 16s 131ms
Bit rate mode                            : Constant
Bit rate                                 : 192 Kbps
Nominal bit rate                         : 96.0 Kbps
Channel(s)                               : 2 channels
Channel positions                        : Front: L R
Sampling rate                            : 48.0 KHz
Compression mode                         : Lossy
Stream size                              : 374 KiB (1%)
Source stream size                       : 375 KiB (1%)
Title                                    : SoundHandle
Language                                 : English
mdhd_Duration                            : 16102

StreamFile() 函数将“streamingBlockSize”字节块在紧密循环中流式传输到输出套接字(“streamingBlockSize”通过配置文件设置,并在研究和调试当前缓冲区欠载问题时引入)。

使用 Wireshark 跟踪数据包会显示包含 1448 字节流数据的数据包以均匀的速度发送:

|Time     | 192.168.0.197                         |
|         |                   | 192.168.0.199     |                   
|14.420722000|         SYN, ACK  |                   |Seq = 0 Ack = 1|         |(10243)  ------------------>  (58358)  |
|14.437750000|         PSH, ACK - Len: 266           |Seq = 1 Ack = 188|         |(10243)  ------------------>  (58358)  |
|14.437924000|         ACK - Len: 1448               |Seq = 267 Ack = 188|         |(10243)  ------------------>  (58358)  |
|14.437939000|         ACK - Len: 1448               |Seq = 1715 Ack = 188|         |(10243)  ------------------>  (58358)  |
|14.437950000|         ACK - Len: 1448               |Seq = 3163 Ack = 188|         |(10243)  ------------------>  (58358)  |
|14.442016000|         ACK - Len: 1448               |Seq = 4611 Ack = 188|         |(10243)  ------------------>  (58358)  |
|14.444269000|         ACK - Len: 1448               |Seq = 6059 Ack = 188|         |(10243)  ------------------>  (58358)  |
|14.444293000|         ACK - Len: 1448               |Seq = 7507 Ack = 188|         |(10243)  ------------------>  (58358)  |
|14.444358000|         ACK - Len: 1448               |Seq = 8955 Ack = 188|         |(10243)  ------------------>  (58358)  |
|14.444373000|         ACK - Len: 1448               |Seq = 10403 Ack = 188|         |(10243)  ------------------>  (58358)  |
|14.444389000|         ACK - Len: 1448               |Seq = 11851 Ack = 188|         |(10243)  ------------------>  (58358)  |
. . .
|72.768739000|         ACK - Len: 1448               |Seq = 40488067 Ack = 188|         |(10243)  ------------------>  (58358)  |
|72.768766000|         ACK - Len: 1448               |Seq = 40489515 Ack = 188|         |(10243)  ------------------>  (58358)  |
|72.772484000|         ACK - Len: 1448               |Seq = 40490963 Ack = 188|         |(10243)  ------------------>  (58358)  |
|72.772521000|         PSH, ACK - Len: 895           |Seq = 40492411 Ack = 188|         |(10243)  ------------------>  (58358) 

Wireshark 通过 Statistics>Summary 菜单项提供了有关上述数据包的非常有用的摘要信息:

Packets                        27997
Between first and last packet  58.352 sec
Avg. packets/sec               479.797
Avg. packet size               1513.586 bytes
Bytes                          42375867
Avg. bytes/sec                 726213.548
Avg. MBit/sec                  5.810

这告诉我们传输一个播放时间为 16.102 秒且渲染器经常遇到停顿的 39 MB 视频需要 58.352 秒。这听起来像是缓冲区欠载的经典案例。

此外,Wireshark 检测到的平均 Mbps 速率为 5.81 Mbps。根据定义,这永远无法满足需要以 20.1 Mbps 的比特率渲染视频的渲染器。

可能的修复

在研究该问题时,我遇到了许多可能导致该问题的技术问题,并希望您的想法。

增加传递给 write() 的缓冲区大小

我尝试改变传递给 write() 函数的字节数(例如,4096、8192、16384),看看增加数据大小是否可以加快传输速度。它似乎没有什么区别(有关可能的解释,请参阅 MTU 和 MSS 的讨论)。

增加以太网 MTU(最大传输单元)和/或 TCP MSS(最大段大小)

Wireshark 显示每个 TCP 数据包携带 1448 个视频原始数据。增加 MTU 或 MSS 会提高流传输吞吐量吗? http://www.stratus.com/blog/openvos/?p=1459在 MTU 和 MSS 之间进行了有趣的比较。

TCP_NODELAY

有几个页面讨论了套接字设置 TCP_NODELAY(请参阅http://en.wikipedia.org/wiki/Transmission_Control_Protocol)。我的理解是它会改善多文件传输,这通常会导致文件的最后一个字节没有填满输出缓冲区。默认情况下,TCP 将等待 200 毫秒等待缓冲区填满。使用 TCP_NODELAY 不会有延迟。在单个视频文件流式传输的情况下,我预计不会有改进。这个对吗?

网络负载可变性

正在使用的网络会导致数据流过慢吗?

boost::asio::write() 是阻塞写——非阻塞写有帮助吗?

最底部的 boost::asio::write() 是阻塞写入:

try
{
    numCharsWritten = boost::asio::write (socket, boost::asio::buffer (data, len));
}

与非阻塞 write() 相比,使用阻塞 write() 是否可能存在固有延迟?使用非阻塞写入会提高吞吐量吗?

非常感谢您的帮助。

4

1 回答 1

0

我试图只提供指点,没有解决方案。

正如您所猜测的那样,这个问题涉及多个方面,其中任何一个都可能导致这种情况 - 视频是如何编码的(它是否有 B 帧,只有 I 帧等......),然后是网络的带宽 android设备用于访问此 HTTP 服务器 - 在此测试期间有多拥塞;解码器库和解码器应用程序。不是解决方案,但您可以尝试查看相同测试的行为:

1)使用不同的解码器/渲染应用程序

2)在一天中的不同时间运行此测试(当网络负载可能较少时)

3)尝试在不同的操作系统(使用 mplayer 或其他东西的 Linux)或使用 Windows 媒体播放器的 Windows 上解码/渲染)只是为了看看它是否与 Android OS tcp/ip 堆栈实现有任何关系。

于 2013-05-29T10:41:39.070 回答