Я нашел работоспособное, хотя и не идеальное решение, как приостановить и возобновить воспроизведение с помощью Twilio.
Основная идея состоит в том, чтобы вычислить разницу во времени между созданием команды воспроизведения и временем, когда Gather
URL называется. Разница (при условии идеального мира на мгновение) должна заключаться в том, как далеко был воспроизведен контент, прежде чем его прервал вызывающий. Затем, когда вызывающий абонент готов возобновить работу, сгенерируйте команду Play
, чтобы сервер приложений доставлял не полный контент, а скорее контент с частичным смещением, который начинается прямо в точке, где воспроизведение должно возобновиться (что, вероятно, подразумевает, что механизм для доставлять только часть содержимого аудиофайла, необходимо будет реализовать). По сути, это имитирует функцию паузы / возобновления.
Я реализовал это, и это более или менее работает. В игру вступает несовершенный мир, в котором задержка в сети, задержки в обработке (время между получением Twilio команды Play
, извлечением ресурса воспроизведения и фактическим началом воспроизведения), а также задержка между нажатием кнопки и фактическим получением вызова Gather
- все это влияет на точность. Но если ваши требования не слишком строгие, точность, вероятно, будет достаточно приличной для большинства случаев.
Вот доказательство концепции, которое я сделал на C # (прошло несколько месяцев - надеюсь, он по-прежнему работает так, как было опубликовано). Он также включает в себя эксперименты с быстрой перемоткой вперед и назад, которые просто регулируют, где на самом деле начинается возобновление (и пропускают команду Pause
).
Приведенный ниже код предназначен для PausablePlayController.cs, который генерирует TwiML с Play
, Pause
и другими командами.
Play
действие (не команда TwiML) создает TwiML для воспроизведения содержимого. Воспроизведение можно прерывать, поскольку оно заключено в Gather
, что указывает на действие Pause
. URL-адрес Gather
содержит отметку времени начала воспроизведения (и в случае, если оно уже было смещено ранее, вычисляет его назад во времени).
Pause
действие (не команда 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;
}
}
person
LB2
schedule
20.04.2014