3

Twilio 是否支持暂停和恢复内容的播放。换句话说,我有相当长的文件将播放给调用者,我正在尝试找到一种方法来实现暂停和恢复功能。在播放某些内容的过程中,我希望用户能够按一个数字暂停,然后再按一个数字从音频文件中暂停的同一点恢复播放。

Twilio 是否支持类似的东西?

4

2 回答 2

3

对于如何使用 Twilio 暂停和恢复播放,我确实找到了一个可行的但不完美的解决方案。

Gather其基本思想是计算生成播放命令与调用 URL的时间之间的时间差。区别(暂时假设完美世界)应该是内容在被呼叫者打断之前播放了多远。然后,当调用者准备好恢复时,生成Play命令,以使您的应用服务器交付的不是完整内容,而是从应该恢复播放的点开始的部分偏移内容(这可能意味着交付机制只需要实现部分音频文件内容)。这将基本上模拟暂停/恢复功能。

我已经实现了这个,它或多或少的工作。不完美的世界开始发挥作用,其中网络延迟、处理延迟(Twilio 接收Play命令、检索播放资源和实际开始播放之间的时间)以及按下按钮和实际接收Gather呼叫之间的延迟都会影响准确性。但是,如果您的要求不是太严格,那么对于大多数情况来说,准确性可能已经足够了。

这是我在 C# 中所做的概念验证(已经几个月了 - 希望它仍然可以像发布的那样工作)。它还包括快进和快退的实验,这只是调整恢复实际开始的位置(和跳过Pause命令)。


下面的代码用于 PausablePlayController.cs,它使用 、 和其他命令生成PlayTwiML Pause

Playaction(不是 TwiML 命令)生成用于播放内容的 TwiML。播放是可中断的,因为它被包裹在Gather指向Pause动作的地方。的 URLGather包含播放开始位置的时间戳(如果之前已经偏移,则及时计算回来)。

Pauseaction(不是 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;
    }
}
于 2014-04-20T22:32:38.813 回答
0

使用 TwiML 无法做到这一点。您只能在您的服务器上使用此逻辑,然后使用另一个 Play 标签重新启动音频文件,但我相信这会引入相当多的延迟,因为 Twilio 需要从您的服务器下载音频文件,然后在它之前对其进行转码可以播放(除了你的服务器需要重新生成新的音频文件的时间)

于 2013-11-25T00:16:38.177 回答