导语:
使用c 实现音频流过程中遇到的问题和解决过程
步骤一 :
在使用cgi编写输出音频流接口,前端同事无法拖动播放,于是查阅资料找到了一个关键词:断点续传 断点续传的解释: 断点续传:指的是在上传/下载时,将任务(一个文件或压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传/下载,如果碰到网络故障,可以从已经上传/下载的部分开始继续上传/下载未完成的部分,而没有必要从头开始上传/下载。可以节省时间,提高速度。 断点续传的用途: 有时用户上传/下载文件需要历时数小时,万一线路中断,不具备断点续传的 HTTP/FTP 服务器或下载软件就只能从头重传,比较好的 HTTP/FTP 服务器或下载软件具有断点续传能力,允许用户从上传/下载断线的地方继续传送,这样大大减少了用户的烦恼。 常见的支持断点续传的上传/下载软件:QQ 旋风、迅雷、快车、电驴、酷6、土豆、优酷、百度视频、新浪视频、腾讯视频、百度云等。 HTTP1.1 协议(RFC2616)开始支持获取文件的部分内容,这为并行下载以及断点续传提供了技术支持。它通过在 Header 里两个参数实现的,客户端发请求时对应的是 Range ,服务器端响应时对应的是 Content-Range。 解决方案: 在返回标头中新增两个参数 printf("Content-Length: %ldn", file_size); printf("Content-Range: bytes 0-%ld/%ldn", file_size - 1, file_size); 这里我将文件长度和范围都返回给前端,实现了拖动播放。
步骤二:
发现chrome和android机器都可以实现拖动,但iOS和safari中无法拖动播放,一度以为是前端同学播放组件有问题,后面发现,其实不然! 查阅资料: 通过比较Chrome和Safari的请求我们发现,Chrome请求头中range字段的值是bytes=0-,而Safari请求头中range字段的值是bytes=0-1。从此得知,浏览器请求音频时是使用的范围请求,Chrome是用一个HTTP请求请求了整个音频,即请求音频的第0个字节到最后一个字节,Chrome不强制要求服务端支持范围请求,服务端响应200或206,Chrome都能支持。但是Safari要求服务端必须支持范围请求,Safari会先请求音频的第0个字节到第1个字节,来测试服务端是否支持范围请求,如果服务端支持范围请求,则响应状态码206,响应头中有正确的Content-Range字段,响应体是音频的第一个字节,此时,Safari才会继续请求音频的其他字节,否则Safari会放弃该音频的请求。我们音频的服务端不支持范围请求,响应的是整个音频,状态码200,所以导致无法在Safari播放。 解决方案: 当收到请求表头有range的时候,返回部分文件流,否则返回全部。
参考代码:
代码语言:javascript复制 // 文件校验
size_t file_size;
string filename = file_path audio;
SysLog(INFO, "PID[%d] FILENAME: %s", PID, filename.c_str());
ifstream in(filename, ios::in | ios::binary | ios::ate);
in.seekg(0, std::ios::end);
file_size = in.tellg();
SysLog(INFO, "PID[%d] FILE_SIZE: %ld", PID, file_size);
if ((int)file_size < 0)
{
in.close();
SysLog(ERROR, "PID[%d] file error", PID);
write_msg_json(-4, "read file error", NULL, NULL);
return 0;
}
CallLibcurl libcurl;
verifyUrl(audioUrl);
string header = "";
int start = 0;
int end = file_size - 1;
char *HTTP_RANGE = getenv("HTTP_RANGE");
SysLog(INFO, "PID[%d] REQ_ENV: HTTP_RANGE:%s", PID, HTTP_RANGE);
//ios特殊处理 需要支持断点续传
if (HTTP_RANGE != NULL)
{
string HTTP_RANGE_STRING = HTTP_RANGE;
HTTP_RANGE_STRING = HTTP_RANGE_STRING.substr(6);
vector<string> list1;
list1 = split(HTTP_RANGE_STRING, "-");
start = atoi(list1[0].c_str());
if (list1.size() > 1)
{
end = atoi(list1[1].c_str());
}
else
{
end = start 99999999;
end = end > file_size - 1 ? file_size - 1 : end;
}
printf("Status:206n");
printf("Content-Length: %ldn", (end - start) 1);
printf("Content-Range: bytes %ld-%ld/%ldn", start, end, file_size);
}
else
{
printf("Status:200n");
printf("Content-Length: %ldn", file_size);
printf("Content-Range: bytes 0-%ld/%ldn", file_size - 1, file_size);
}
printf("Accept-Ranges: bytesn");
printf("Content-Type: audio/mpegnn");
int content_length = 0;
char *val;
in.seekg(start);
content_length = end - start 1;
char *buf = new char[content_length];
in.read(buf, content_length);
for(size_t i = 0; i < content_length;i ){
char temp = buf[i];
Content = temp;
}
in.close();
SysLog(INFO, "PID[%d] LEN: %ld", PID, content_length);
cout.write(Content.c_str(), content_length);