Twilio 是否支持暂停和恢复内容的播放。换句话说,我有相当长的文件将播放给调用者,我正在尝试找到一种方法来实现暂停和恢复功能。在播放某些内容的过程中,我希望用户能够按一个数字暂停,然后再按一个数字从音频文件中暂停的同一点恢复播放。
Twilio 是否支持类似的东西?
Twilio 是否支持暂停和恢复内容的播放。换句话说,我有相当长的文件将播放给调用者,我正在尝试找到一种方法来实现暂停和恢复功能。在播放某些内容的过程中,我希望用户能够按一个数字暂停,然后再按一个数字从音频文件中暂停的同一点恢复播放。
Twilio 是否支持类似的东西?
对于如何使用 Twilio 暂停和恢复播放,我确实找到了一个可行的但不完美的解决方案。
Gather
其基本思想是计算生成播放命令与调用 URL的时间之间的时间差。区别(暂时假设完美世界)应该是内容在被呼叫者打断之前播放了多远。然后,当调用者准备好恢复时,生成Play
命令,以使您的应用服务器交付的不是完整内容,而是从应该恢复播放的点开始的部分偏移内容(这可能意味着交付机制只需要实现部分音频文件内容)。这将基本上模拟暂停/恢复功能。
我已经实现了这个,它或多或少的工作。不完美的世界开始发挥作用,其中网络延迟、处理延迟(Twilio 接收Play
命令、检索播放资源和实际开始播放之间的时间)以及按下按钮和实际接收Gather
呼叫之间的延迟都会影响准确性。但是,如果您的要求不是太严格,那么对于大多数情况来说,准确性可能已经足够了。
这是我在 C# 中所做的概念验证(已经几个月了 - 希望它仍然可以像发布的那样工作)。它还包括快进和快退的实验,这只是调整恢复实际开始的位置(和跳过Pause
命令)。
下面的代码用于 PausablePlayController.cs,它使用 、 和其他命令生成Play
TwiML Pause
。
Play
action(不是 TwiML 命令)生成用于播放内容的 TwiML。播放是可中断的,因为它被包裹在Gather
指向Pause
动作的地方。的 URLGather
包含播放开始位置的时间戳(如果之前已经偏移,则及时计算回来)。
Pause
action(不是 TwiML 命令)生成 TwiML 以进行暂停或搜索。在下面的代码中,4 次倒带,5 次从头重新开始,6 次快进,任何其他键都会暂停。
public class PausablePlayController : ApiController
{
private const int seekDeltaMilliseconds = 5000;
// GET api/pausableplay/5
[HttpGet]
public System.Xml.Linq.XElement Play(string audio, int millisecondsOffset)
{
TwilioResponse twiml = new TwilioResponse();
twiml.BeginGather(new { action = this.Url.Link("PausablePlayPause", new { audio = audio, playStart = DateTime.UtcNow.Subtract(new TimeSpan(0, 0, 0, 0, millisecondsOffset)).Ticks/*.ToString("o", System.Globalization.CultureInfo.InvariantCulture )*/ }), method = "GET", numDigits = "1" });
twiml.Play(this.Url.Link("OffsetPresentations", new { audio = audio, millisecondsOffset = millisecondsOffset }));
twiml.EndGather();
return twiml.Element;
}
[HttpGet]
public System.Xml.Linq.XElement Pause(string audio, long playStart, int digits)
{
DateTime playStartDate = new DateTime(playStart, DateTimeKind.Utc);
int millisecondsOffset = (int)DateTime.UtcNow.Subtract(playStartDate).TotalMilliseconds;
TwilioResponse twiml = new TwilioResponse();
switch(digits)
{
case 4:
millisecondsOffset -= (millisecondsOffset < seekDeltaMilliseconds) ? millisecondsOffset : seekDeltaMilliseconds;
return Play(audio, millisecondsOffset);
case 5:
return Play(audio, 0);
case 6:
millisecondsOffset += seekDeltaMilliseconds;
return Play(audio, millisecondsOffset);
default:
{
twiml.BeginGather(new { action = this.Url.Link("PausablePlayPlay", new { audio = audio, millisecondsOffset = millisecondsOffset }), method = "GET", numDigits = "1" });
twiml.Pause(120);
twiml.EndGather();
twiml.Say("Goodbye!");
}
break;
}
return twiml.Element;
}
}
其余的技巧是在下一个控制器中流式传输部分音频内容(不幸的是,我在其他一些帖子中找到了我不再参考的部分代码)。它所做的只是计算给定毫秒偏移量的音频内容从哪里开始,并从该点开始流式传输其余内容。
public class OffsetedContentController : ApplicationController
{
const int BufferSize = 32 * 1024;
// GET api/prompts/5
public Task<HttpResponseMessage> Get(string audio, [FromUri]int millisecondsOffset)
{
string contentFilePath = audio; // Build physical path for your audio content
if (!File.Exists(contentFilePath))
{
return Task.FromResult(Request.CreateResponse(HttpStatusCode.NotFound));
}
// Open file and read response from it. If read fails then return 503 Service Not Available
try
{
// Create StreamContent from FileStream. FileStream will get closed when StreamContent is closed
FileStream fStream = new FileStream(contentFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, useAsync: true);
fStream.Position = getPositionOffset(millisecondsOffset);
HttpResponseMessage response = Request.CreateResponse();
response.Content = new StreamContent(fStream);
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("audio/ulaw");
return Task.FromResult(response);
}
catch (Exception e)
{
return Task.FromResult(Request.CreateErrorResponse(HttpStatusCode.ServiceUnavailable, e));
}
}
private long getPositionOffset(int millisecondsOffset)
{
long bytePosition = millisecondsOffset * 4;
return bytePosition;
}
}
使用 TwiML 无法做到这一点。您只能在您的服务器上使用此逻辑,然后使用另一个 Play 标签重新启动音频文件,但我相信这会引入相当多的延迟,因为 Twilio 需要从您的服务器下载音频文件,然后在它之前对其进行转码可以播放(除了你的服务器需要重新生成新的音频文件的时间)