Как установить текущее время в аудиообъекте HTML5, когда аудиофайл находится в сети?

У меня есть аудиоплеер JavaScript с вторыми кнопками перехода вперед / назад 10. Я делаю это, устанавливая currentTime своего аудиоэлемента:

function Player(skipTime)
{
    this.skipTime = skipTime;
    this.waitLoad = false;

    // initialise main narration audio
    this.narration = new Audio(getFileName(dynamicNarration));
    this.narration.preload = "auto";
    this.narration.addEventListener('canplaythrough', () => { this.loaded();       });
    this.narration.addEventListener('timeupdate',     () => { this.seek();         });
    this.narration.addEventListener('ended',          () => { this.ended();        });
    this.narration.addEventListener('waiting',        () => { this.audioWaiting(); });
    this.narration.addEventListener('playing',        () => { this.loaded();       });
}

Player.prototype = {
    rew: function rew()
    {
        if (!this.waitLoad) {
            this.skip(-this.skipTime);
        }
    },

    ffw: function ffw()
    {
        if (!this.waitLoad) {
            this.skip(this.skipTime);
        }
    },

    skip: function skip(amount)
    {
        const curTime = this.narration.currentTime;
        const newTime = curTime + amount;
        console.info(`Changing currentTime (${curTime}) to ${newTime}`);
        this.narration.currentTime = newTime;
        console.info(`Result: currentTime = ${this.narration.currentTime}`);
    },

    loaded: function loaded()
    {
        if (this.waitLoad) {
            this.waitLoad = false;
            playButton.removeClass('loading');
        }
    },

    audioWaiting: function audioWaiting()
    {
        if (!this.waitLoad) {
            this.waitLoad = true;
            playButton.addClass('loading');
        }
    },
}

(Я включаю сюда некоторые из прослушивателей событий, которые я прикрепляю, потому что раньше я отлаживал аналогичную проблему, связанную с конфликтами в прослушивателях событий. Однако, на этот раз тщательно отладив прослушиватели событий, я не считать, который является корневым проблемы.)

Хотя все это отлично работает на моей локальной копии, когда я тестирую онлайн-версию, я получаю следующие результаты:

  • Хром: сбрасывает позицию воспроизведения на 0. Последняя строка консоли гласит Результат: currentTime = 0.
  • Сафари: вообще не меняет позицию воспроизведения. Конечная строка console.info дает значение для currentTime, равное newTime (хотя позиция воспроизведения фактически не меняется).
  • Fire Fox: работает с пропуском вперед; при пропуске назад звук прерывается на несколько секунд, затем он снова начинает воспроизводиться за пару секунд до того места, где находилась точка воспроизведения. В обоих случаях последняя строка console.info дает значение для currentTime, равное newTime.

Проблема должна быть связана со способом загрузки звука. Я попытался добавить еще одну строку журнала консоли, чтобы показать начальное и конечное значения для buffered.

В Chrome он увеличивается до 2 секунд после текущей позиции воспроизведения. В Safari он достигает ~170 секунд, а в Firefox он, кажется, буферизует полную длину звука.

Однако в каждом случае начало объекта buffered - это 0.

Кто-нибудь знает, что может пойти не так?

Игид, если вас устраивает мой ответ ниже, отметьте его как принятый слева от моего ответа или напишите мне отзыв.

Bharata 07.09.2018 21:53

@lgid Можете ли вы предоставить образец файла, который вы используете? Что это за тип? Я пробовал ваш код с файлом mp3, который где-то размещен, и он работает, как ожидалось.

Stavros Zavrakas 13.09.2018 11:40
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
7
2
1 842
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Если ваш браузер не загрузил звук, звук не может быть воспроизведен. Браузер не знает ваш аудиофайл и из-за этого пытается воспроизвести его с самого начала. Может быть, ваш звук будет длиться всего 1 секунду или даже короче.

Решение

Вам нужно дождаться события loadedmetadata, и после него вы можете играть свою аудиторию из любой временной позиции. После этого события ваш браузер узнает всю необходимую информацию о вашем аудиофайле.

Пожалуйста, измените свой код следующим образом:

function Player(skipTime)
{
    this.skipTime = skipTime;

    // initialise main narration audio
    this.narration = new Audio(getFileName(dynamicNarration));
    this.narration.preload = "auto";
    this.narration.addEventListener('canplaythrough', () => { this.loaded();       });
    this.narration.addEventListener('timeupdate',     () => { this.seek();         });
    this.narration.addEventListener('ended',          () => { this.ended();        });
    this.narration.addEventListener('waiting',        () => { this.audioWaiting(); });
    this.narration.addEventListener('playing',        () => { this.loaded();       });

    this.narration.addEventListener('loadedmetadata', () => {playButton.removeClass('loading');});

    playButton.addClass('loading');
}

Player.prototype =
{
    rew: function()
    {
        this.skip(-this.skipTime);
    },

    ffw: function()
    {
        this.skip(this.skipTime);
    },

    skip: function(amount)
    {
        var curTime = this.narration.currentTime;
        var newTime = curTime + amount;
        console.info(`Changing currentTime (${curTime}) to ${newTime}`);
        this.narration.currentTime = newTime;
        console.info(`Result: currentTime = ${this.narration.currentTime}`);
    }
};

Но если вы не хотите долго ждать загрузки звука, у вас есть только один вариант: преобразовать все ваши аудиофайлы в формат dataURL, который выглядит следующим образом:

var data = "data:audio/mp3;base64,...

Но в этом случае придется ждать загрузки страницы даже больше, чем загрузки одного аудиофайла. А при загрузке аудиофайла это только метаданные, и это быстрее.

Я также попытался добавить прослушиватель событий loadedmetadata. Однако, поскольку это происходит, когда звук уже воспроизводится, метаданные уже давно загружены. Фактически, я использую продолжительность в других частях логики игрока.

Igid 09.09.2018 23:16

@Igid, я в твоем коде ничего не вижу с длительностью. Я правильно ответил на ваш вопрос или нет? Если вас устраивает мой ответ, отметьте его как принятый слева от моего ответа и / или проголосуйте за него.

Bharata 09.09.2018 23:27

Я имел в виду, что мой фактический производственный код также использует продолжительность, она не включена в мой упрощенный пример. Спасибо за ваш ответ, но нет, ожидание загрузки метаданных мне не помогает, поскольку они загружаются задолго до того, как я даже воспроизвожу файл.

Igid 09.09.2018 23:33

@Igid, но это единственный способ воспроизвести файлы в Интернете. Вашим локальным файлам это не нужно, и вы можете воспроизводить их сразу после загрузки страницы, но с онлайн-файлами - у вас есть только один способ - вам нужно дождаться события loadedmetadata. И это долго, потому что ваша страница уже загружена в браузере и метаданные из файлов должны быть загружены с вашего сервера после загрузки страницы.

Bharata 10.09.2018 00:01

Я проверил это, добавив прослушиватель событий с журналом консоли. Событие loadedmetadata срабатывает до того, как я нажму кнопку воспроизведения. Так что ожидание этого события не решит мою проблему.

Igid 10.09.2018 13:44
Ответ принят как подходящий

Я нашел решение своей проблемы, если не совсем объяснение.

Мой хостинг-провайдер использует CDN, для которого он должен заменить URL-адреса ресурсов на URL-адреса другого домена. URL-адреса моих аудиоресурсов динамически создаются JS, потому что в них есть случайный элемент; Таким образом, процесс развертывания, который заменяет URL-адреса, не улавливал их для моих аудиофайлов. Чтобы обойти это, я вручную исключил аудиофайлы из CDN, то есть я мог ссылаться на них, используя относительные пути к файлам.

Так было, когда у меня возникла эта проблема.

Затем, из-за отдельной проблемы, я применил другой подход: я получил аудиофайлы обратно в CDN и написал функцию для извлечения доменного имени, которое мне нужно было использовать для извлечения файлов. Когда я это сделал, я внезапно обнаружил, что все мои проблемы, связанные с настройкой currentTime, исчезли. Почему-то отсутствие файлов в CDN серьезно мешало браузеру загружать их упорядоченным образом.

Если кто-нибудь может добровольно объяснить, почему это могло быть, мне было бы очень любопытно это услышать ...

Редактировать

Я работал над другим проектом, который включает потоковую передачу звука, на этот раз также с поддержкой PWA, поэтому мне пришлось реализовать механизм кэширования в моем сервис-воркере для аудиофайлов. Благодаря это руководство я узнал все о подводных камнях диапазон запросов и теперь понимаю, что неспособность обслуживать правильные ответы на запросы с заголовками диапазонов нарушит поиск в некоторых браузерах.

Похоже, что в приведенном выше случае, когда я исключил свои файлы из CDN, они обслуживались откуда-то, что не поддерживало заголовки диапазонов. Когда я переместил их обратно в CDN, это было исправлено, так как он должен был быть построен с явной поддержкой потокового мультимедиа.

Здесь - хорошее объяснение правильных ответов на запросы диапазона. Но для тех, кто сталкивается с этой проблемой при использовании сторонней службы хостинга, достаточно знать, что они, вероятно, не поддерживают заголовки диапазонов для потокового мультимедиа. Если вы хотите убедиться, что это так, вы можете запросить продолжительность звукового объекта. По крайней мере, в случае Safari продолжительность установлена ​​на бесконечность, когда он не может успешно выполнить запрос диапазона, и в этот момент поиск будет отключен.

Это решило мою проблему ...

    private refreshSrc() {
      const src = this.media.src;
      this.media.src = '';
      this.media.src = src;
    }

Есть некоторые требования для правильной загрузки аудиофайла и использования свойств. Ваш ответ при обслуживании файла должен иметь следующие заголовки.

диапазоны приема: байты

Длина содержимого: BYTE_LENGTH_OF_YOUR_FILE

Content-Range: байты 0-BYTE_LENGTH_OF_YOUR_FILE / BYTE_LENGTH_OF_YOUR_FILE

тип содержимого: аудио / mp3

Мои коллеги и я боролись над этим несколько дней, и, наконец, это сработало.

Изображение заголовка ответа для аудиофайла

Какое совпадение, что вы добавляете этот ответ сейчас! Я только что погрузился в мир потокового аудио и, реализовав механизм кэширования сервис-воркеров для аудиофайлов на PWA, я узнал все о заголовках диапазонов за последние несколько дней. Я добавляю объяснение к своему собственному ответу и голосую за него.

Igid 23.07.2020 22:04

Другие вопросы по теме