背景
我正在使用Boost在C++中开发渐进式下载媒体流媒体服务器。典型的配置是运行Android 4.2.2的Android渲染设备,使用股票库播放器作为媒体播放器,以及运行在Windows桌面上的媒体流媒体服务器。 Android设备通过HTTP URL请求媒体文件,媒体服务器使用渐进式下载来传输文件。文件流遇到缓冲区不足/下溢?
的问题
当试图流式播放视频文件以20 Mbps的内部比特率,渲染器始终档无数次。一个典型的流媒体体验主要包括以下步骤:多次出现的:
- 顺利渲染3-5秒
- 视频摊位5-10秒
- 转到步骤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));
}
使用阻塞写(当有可能的固有延迟),而不是一个无阻塞写()?使用非阻塞写入会提高吞吐量吗?
非常感谢您的帮助。
你如何测试它?通过WiFi?你有没有测试下载带宽? –
道歉,花了这么长时间回应 - 我需要下载局域网速度测量软件。当通过Wi-Fi连接到路由器时,我以24 Mbps的速度测量了服务器上传速度,结果波涛汹涌。当通过以太网电缆连接到路由器时,我测量的服务器上传速度约为600 Mbps,并且渲染一直很完美。我相信这表明我的服务器软件正在以良好的速度提供视频。假设软件没问题,而网络可能是不稳定的原因,是否安全? –
这就是为什么你有缓冲区,以减少不平衡。如果你有足够大的缓冲区和足够的带宽,你应该不会有任何混乱。 –