LINUX.ORG.RU

Воспроизведение аудио буфера на клиенте

 ,


0

1

С вебом никак не связан, пишу для себя по настроению, поэтому могу не знать/плохо знаю какие-то, казалось бы, базовые вещи.

Проблема: когда пользователь запрашивает аудио файл для воспроизведения у сервера, он получает непосредственно сам буфер(string)/содержимое этого аудио файла. После получения буфера его необходимо как-то обработать и воспроизвести. Для решения этой проблемы я смог найти только способ с использованием объектов Blob и Audio, но в моем случае он не работает, выдавая в консоль браузера:

Не удалось декодировать медиаресурс blob:http://localhost:3000/8d214977-3b73-4efc-a917-b65693cacf72.

NotSupportedError: The media resource indicated by the src attribute or assigned media provider object was not suitable.

Не удалось декодировать медиаресурс blob:http://localhost:3000/8d214977-3b73-4efc-a917-b65693cacf72, ошибка: Error Code: NS_ERROR_DOM_MEDIA_METADATA_ERR (0x806e0006)
Пытался решить эту проблему, ничего толкового не смог найти или просто не понял сути проблемы, а значит и ее решения.

Вот обработчик, который при нажатии на кнопку отправляет серверу POST запрос, содержащий имя запрашиваемого аудио файла, а после ответа пытается его воспроизвести:

function handler(event) {
    fetch('http://localhost:3000/play', {
        method: 'POST', 
        headers: {
            'Content-Type': 'application/json'
        }, 
        body: JSON.stringify({path: event.explicitOriginalTarget.value}) // имя файла
    }).then(data => {
        data.text().then(audioBuffer => {
            const blob = new Blob([audioBuffer], {type: 'audio/mp3'});
            const urlBlob = URL.createObjectURL(blob);
            new Audio(urlBlob).play();
        });
    });
}

Сторона сервера, обрабатывающая запросы роута /play:

server.on('request', (req, res) => {
    const urlObj = url.parse(req.url, true);

    switch (urlObj.pathname) {
        // ...
        case '/play':
            let body = '';
            req.on('data', data => {
                body = data; // получаем данные о имени файла (путь к файлу) от клиента
            });
            req.on('end', () => {
                sendAudioFile(res, body); // когда получили данные, отправляем файл клиенту
            });
            break;
        // ...
    }
});

Содержимое функции sendAudioFile:

function sendAudioFile(response, body) {
    const path = JSON.parse(body.toString()).path; // что-то вроде D:/Music/Artist/Album/song.mp3
    const splitedPath = path.split('.');
    const format = splitedPath[splitedPath.length - 1]; // mp3
    const fileSize = fs.statSync(path).size;
    const header = {
        'Content-Length': fileSize,
        'Content-Type': `audio/${format}`
    };

    response.writeHead(200, header);
    fs.createReadStream(path).pipe(response);
}



Последнее исправление: kennydzzze (всего исправлений: 1)

Ну, это от браузера и набора установленных кодеков зависит. Можно попробовать использовать тип MIME «audio/mpeg».

sanwashere ★★
()
Ответ на: комментарий от sanwashere

Мне кажется, в данном случае дело вовсе не в кодеках никаких, а именно в обработке самого буфера. Но мне сложно однозначно сказать, иначе бы я не создавал эту тему. С «audio/mpeg» я тоже пробовал - не помогло.

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

const ctx = new AudioContext();
let audio = null;

function handler(event) {
    fetch('http://localhost:3000/play', {
        method: 'POST', 
        headers: {
            'Content-Type': 'application/json'
        }, 
        body: JSON.stringify({path: event.explicitOriginalTarget.value})
    }).then(response => {
        response.arrayBuffer()
            .then(arrayBuffer => ctx.decodeAudioData(arrayBuffer))
            .then(decodedAudio => {
                audio = decodedAudio;
                const playSound = ctx.createBufferSource();
                playSound.buffer = audio;
                playSound.connect(ctx.destination);
                playSound.start(ctx.currentTime);
            });
    });
}

kennydzzze
() автор топика
Ответ на: комментарий от kennydzzze

Сто лет не лазал в JS и браузеры, так что предположу, что для проигрывания буфера там должны быть несжатые данные. В первом случае (нерабочем) вы скармливаете закодированные данные (MP3). В последнем - предварительно раскодируете.
В первом случае скорее всего нужно пропустить исходный буфер через AudioContext#decodeAudioData().

sanwashere ★★
()
Последнее исправление: sanwashere (всего исправлений: 3)

когда пользователь запрашивает аудио файл для воспроизведения у сервера, он получает непосредственно сам буфер(string)/содержимое этого аудио файла.

Существуют ли определённые причины, по которым отдаётся содержимое файла, а не ссылка на файл?

Подобный подход я видел в системе, где файлы дробились на куски и по веб-сокетам отправлялись клиенту.

Adeptus-Mechanicus
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.