2013-05-28 167 views
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       : [email protected] 
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的提供有关通过统计上面的分组非常有用的摘要信息>概要菜单项:

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 

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

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

可能的修复

虽然研究我遇到了许多技术问题,该问题可能导致的问题,并感谢您的想法。缓冲器的

增加大小传递给写()

我试图改变传递给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()是一个阻塞写入 - 会不会阻塞写入帮助?

升压:: ASIO ::写()在最底层是一个阻塞写:

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

使用阻塞写(当有可能的固有延迟),而不是一个无阻塞写()?使用非阻塞写入会提高吞吐量吗?

非常感谢您的帮助。

+0

你如何测试它?通过WiFi?你有没有测试下载带宽? –

+0

道歉,花了这么长时间回应 - 我需要下载局域网速度测量软件。当通过Wi-Fi连接到路由器时,我以24 Mbps的速度测量了服务器上传速度,结果波涛汹涌。当通过以太网电缆连接到路由器时,我测量的服务器上传速度约为600 Mbps,并且渲染一直很完美。我相信这表明我的服务器软件正在以良好的速度提供视频。假设软件没问题,而网络可能是不稳定的原因,是否安全? –

+0

这就是为什么你有缓冲区,以减少不平衡。如果你有足够大的缓冲区和足够的带宽,你应该不会有任何混乱。 –

回答

0

我想给只有指针,没有解决方案。

就像你猜测的那样,这个问题涉及多个方面,其中任何一个都可能导致这个问题 - 视频是如何编码的(是否有B帧,只有I帧等等),那么带宽android设备用来访问这个HTTP服务器的网络 - 在测试过程中它的拥塞程度如何;解码器库和解码器应用程序。没有一个解决方案,但是,你可以试试,看看同样的测试的行为:使用不同解码器

1)/渲染应用

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

3)尝试在不同的操作系统(使用mplayer或Linux的Linux上)或使用Windows媒体播放器的Windows上进行解码/渲染,以查看是否需要对Android OS tcp/ip堆栈执行任何操作。

+0

感谢指针。我曾经考虑过他们,但听到别人的声音很好。关于(1),我正在处理选择特定渲染器的渲染软件,因此没有选择其他渲染器的余地。有了(2),当网络不太繁忙时(例如,当我的同事回家时),我当然会看到改进。至于(3)在使用从Windows机器到渲染机器的DLNA进行渲染时,我获得了很好的效果。在这种情况下,我*可以*选择渲染器应用程序,但是我不测试我的服务器软件。 –

+1

那么根据你在上面的发现(3)的评论,它可能是你的服务器套接字代码。正如你指出的,尝试1)使用非阻塞write()2)如果可能,你可以尝试POSIX套接字而不是boost :: asio库。 (我知道它的新代码增加了,但是你可以在一些编译时的宏切换下将它添加到现有的版本中),但是如果你的asio套接字API调用是原因,那么这是一个测试来定位你的问题。我确信TCP_NODELAY不会解决你的问题,或者无论如何都无法解决问题。 Goodluck并不断发布关于你的进展。 – goldenmean