Я создал сервлет, который возвращает поток (из файла MP3), начиная с любой позиции байта, запрошенной клиентом. Это позволяет клиенту мгновенно начать воспроизведение в любой заданной позиции байта, не выполняя локального поиска.
Теперь у меня есть слайдер, который визуализирует прогресс. Я использую текущую позицию байта, чтобы обновить ползунок. Однако я также хотел бы показать текущую позицию в секундах.
Для этого требуется, чтобы сервер мог «преобразовывать» текущую позицию в байтах в позицию в миллисекундах. Затем сервер может просто предоставить начальную позицию потока в миллисекундах в качестве заголовка ответа.
Кто-нибудь знает, как можно вычислить текущую позицию в байтах в миллисекундах?
UPDATE
Из комментариев ясно, что не существует точного способа получить преобразованные байты в миллисекунды и наоборот без декодирования файла MP3 до точки (в миллисекундах или байтах), а затем определить, сколько байтов читать или миллисекунды, которые были воспроизведены. Однако этот подход, очевидно, не будет работать слишком хорошо, если учесть сервер, где, скажем, 100 пользователей будут запрашивать файлы одновременно. Затем сервер должен будет декодировать файлы MP3 до требуемой позиции, а затем вернуть потоки из этой точки. Я выбрал обмен точности с производительностью и использовал подход, который дает мне приблизительные позиции и который более чем хорош для игрока, цель которого - просто воспроизвести трек (а не синхронизировать звук с другими источниками до миллисекунды).
Что я сделал, так это то, что игрок (на стороне клиента) теперь заботится только о миллисекундах (мс). То есть текущее значение и максимальное значение индикатора выполнения указывается в MS, а не в байтах, как это было сначала. Чтобы начать воспроизведение с любой заданной позиции, клиент запрашивает у сервера (сервлета) предоставить аудиопоток, начинающийся с любой заданной позиции в MS. Сервлет использует JAudioTagger для получения подробной информации о файле, а затем производит приблизительный расчет относительно того, какой позиции байта соответствует позиция MS. Я проверил это, и он хорошо работает с файлами CBR (постоянный битрейт). Этот подход не будет работать с файлами VBR (переменная скорость передачи данных), поскольку размер кадра может варьироваться. Обратите внимание, что это просто плеер, целью которого является воспроизведение музыкальных файлов. Он не предназначен для синхронизации звука с некоторыми другими носителями вплоть до MS. Отрезанный код, который преобразует из MS в байты, представлен ниже.
ОБНОВЛЕНИЕ (3 июля 2012 г.)
Сервлет уже давно работает с приведенным ниже кодом, и все работает очень хорошо. Были воспроизведены тысячи MP3, и приближение от мс до байтов работает отлично.
ОБНОВЛЕНИЕ (3 января 2017 г.)
Сервлет все еще работает с тем же самым кодом, и сотни тысяч MP3 были воспроизведены великолепно. За время производства не было подано ни одной жалобы относительно воспроизведения и времени.
/**
* Returns the approximate byte position for any given position in
* milliseconds.
*
* http://www.java2s.com/Open-Source/Android/Mp3/needletagger/org/jaudiotagger/audio/mp3/MP3AudioHeader.java.htm
* http://www.autohotkey.com/forum/topic29420.html
*
* @param file the <code>File</code> for which the byte position for the
* provided position in milliseconds is to be returned.
* @param ms a <code>long</code> being the position in milliseconds for
* which the corresponding byte position is to be returned.
* @return a <code>long</code> being the byte position, or <b>-1</b> if the
* position in bytes could not be obtained.
*/
public static long getApproximateBytePositionForMilliseconds(File file, long ms) {
long bytePosition = -1;
try {
AudioFile audioFile = AudioFileIO.read(file);
AudioHeader audioHeader = audioFile.getAudioHeader();
if (audioHeader instanceof MP3AudioHeader) {
MP3AudioHeader mp3AudioHeader = (MP3AudioHeader) audioHeader;
long audioStartByte = mp3AudioHeader.getMp3StartByte();
long audioSize = file.length() - audioStartByte;
long frameCount = mp3AudioHeader.getNumberOfFrames();
long frameSize = audioSize / frameCount;
double frameDurationInMs = (mp3AudioHeader.getPreciseTrackLength() / (double) frameCount) * 1000;
double framesForMs = ms / frameDurationInMs;
long bytePositionForMs = (long) (audioStartByte + (framesForMs * frameSize));
bytePosition = bytePositionForMs;
}
return bytePosition;
} catch (Exception e) {
return bytePosition;
}
}