文件流遇到缓冲区欠载/下溢?

时间:2013-05-28 11:33:18

标签: sockets boost tcp video-streaming

背景

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

问题

尝试以20 Mbps的内部比特率流式传输视频文件时,渲染器会一直停顿多次。典型的流媒体体验基本上包括多个步骤:

  1. 平稳渲染3-5秒
  2. 视频停顿5-10秒
  3. 转到第1步
  4. 最合乎逻辑的解释是渲染器遇到“缓冲区欠载”或“缓冲区下溢”

    问题

    有没有办法解决缓冲区欠载问题,提高媒体流媒体输出率,防止视觉拖延/阻塞?

    技术信息

    服务器代码如下所示:

    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'通过配置文件设置,并在研究和调试当前缓冲区欠载问题时引入)。 / p>

    使用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&gt; 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
    

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

    此外,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 ms以使缓冲区填满。使用TCP_NODELAY将没有延迟。在单个视频文件流媒体情况下,我不希望有任何改进。这是对的吗?

    网络负载可变性

    正在使用的网络会导致数据传输速度太慢吗?

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

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

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

    使用阻塞write()和非阻塞write()时是否存在内在延迟?使用非阻塞写入会提高吞吐量吗?

    非常感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

我试图只给出指针,没有解决方案。

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

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

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

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