У меня есть корзина и файл cue (образ компакт-диска), и я создаю веб-сайт, на котором можно слушать мою музыку. Мне удалось разделить дорожки и создать wav-заголовок (44 байта), чтобы мой браузер мог читать аудио, но файлы wav слишком велики, чтобы их можно было читать при нестабильном соединении (это 1411 кбит/с, тогда как mp3 будет 320 кбит/с или ниже!).
Во-первых, есть ли способ конвертировать аудио в mp3 без создания временного файла на php? Может быть, ffmpeg или sox могут помочь?
Во-вторых, я хотел бы иметь возможность удовлетворять запросы на частичный контент (чтобы я мог пропустить, не загружая весь файл). Мне уже удалось это сделать с WAV-файлами, и мне нужно знать несколько вещей, чтобы сделать это с mp3:
Я попробовал поискать в Интернете, но не нашел того, на что надеялся. Я надеюсь, что вы можете мне помочь.






Для моего первого вопроса: чтобы конвертировать wav в mp3 в php без создания временного файла, я использовал:
passthru(dd if = "$bin_path" bs=1 skip=$skip_bytes count=$count_bytes | ffmpeg -ss 0 -f s16le -ar 44100 -ac 2 -i pipe:0 -codec:a libmp3lame -b:a 320k -ac 2 -joint_stereo 0 -compression_level 0 -write_id3v1 0 -id3v2_version none -f mp3 -);
где $bin_path — путь к вашему двоичному файлу (вам нужно экранировать ", если он есть в пути). $skip_bytes равно 44100 * 4 * song_start (с) $count_bytes равно 44100 * 4 * song_duration (с)
Вот разбивка команды FFmpeg:
Для ввода: -ss 0 начинать с 0,0 с, даже если нет звука (тишина) -f s16le -ar 44100 -ac 2 подскажите, что вход pcm_s16le (-f s16le) стерео (-ac 2) с частотой дискретизации 44100 Гц (-ar 44100) -i pipe:0 вход поступает из трубы
Для вывода: -codec:a libmp3lame кодирование mp3 -b:a 320k битрейт (320 кбит/с) -ac 2 -joint_stereo 0 2-канальное стерео -compression_level 0 используйте постоянный битрейт -id3v2_version none пропустите заголовок ID3 - вывод в sdin
Что касается второй части вашего вопроса,
mp3 не имеют ничего общего с wav-файлами, и предсказать их размер практически невозможно. Однако мне все равно удалось это сделать, сделав песню длительностью кратной 1,28 секунды (mp3 (версия 1, слой III) имеет внутреннюю структуру, повторяющуюся каждые 1,28 секунды).
Вот как я это сделал:
$bin_path = "/path/to/binary.bin";
$song_start = "182.12"; // song starts after 3m0.12s
$song_duration = "176.14"; // and lasts 2m56.02s
$skip_bytes = 44100 * 4 * $song_start;
$count_bytes = 44100 * 4 * $song_duration;
$chunk_size_mp3 = 51200; // Size (in bytes) for 1.28s of 320kbps MP3
$chunk_size_wav = 225792; // Size (in bytes) for 1.28s of WAV
$file_size = (floor($song_duration/1.28)+1) * $chunk_size_mp3;
$need_exit = false;
//--------------------HTTP HEADERS--------------------
ob_get_clean();
header('Content-Type: audio/mpeg');
header('Cache-Control: no-cache', true);
header('Expires: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT'); // Expire now!
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', @filemtime($file_path)) . ' GMT' );
$start_index = 0;
$end_index = $file_size - 1;
header('Accept-Ranges: bytes');
if (isset($_SERVER['HTTP_RANGE'])){
$c_start = $start_index;
$c_end = $end_index;
list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
if (strpos($range, ',') !== false){
header('HTTP/1.1 416 Requested Range Not Satisfiable');
header("Content-Range: bytes $start_index-$end_index/$file_size");
$need_exit = true;
}
if (!$need_exit){
if ($range == '-'){
$c_start = $file_size - substr($range, 1);
}else{
$range = explode('-', $range);
$c_start = $range[0];
$c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $c_end;
}
$c_end = ($c_end > $end_index) ? $end_index : $c_end;
if ($c_start > $c_end || $c_start > $file_size - 1 || $c_end >= $file_size){
header('HTTP/1.1 416 Requested Range Not Satisfiable');
header("Content-Range: bytes $start_index-$end_index/$file_size");
$need_exit = true;
}
if (!$need_exit){
if ($c_start <= $chunk_size_mp3){
$start_index = 0;
}else{
$start_index = $c_start;
}
if ($c_end <= $chunk_size_mp3){
$end_index = $chunk_size_mp3 - 1;
}else{
$end_index = $c_end;
}
$length = $end_index - $start_index + 1;
header('HTTP/1.1 206 Partial Content');
header('Content-Length: ' . $length);
header("Content-Range: bytes $start_index-$end_index/".$file_size);
}
}
}else{
header('Content-Length: ' . $file_size);
}
//--------------------HTTP HEADERS--------------------
//-----------------------STREAM-----------------------
if (!$need_exit){
$i = $start_index;
if ($start_index === 0){
$mp3_data = shell_exec("php /path/to/dd.php \"$bin_path\" $skip_bytes ".($chunk_size_wav*2)." 0 | ffmpeg -ss 0 -f s16le -ar 44100 -ac 2 -i pipe:0 -codec:a libmp3lame -b:a 320k -ac 2 -joint_stereo 0 -compression_level 0 -write_id3v1 0 -id3v2_version none -f mp3 -");
for($j = 0; $j < $chunk_size_mp3; $j++){
echo $mp3_data[(int)$j];
}
$i += $chunk_size_mp3;
}
while($i <= $end_index){
$start_chunk_number = floor($i/$chunk_size_mp3)-1;
$end_chunk_number = $start_chunk_number + 2;
$start_wav_byte = $skip_bytes + $start_chunk_number * $chunk_size_wav;
$end_wav_byte = $start_wav_byte + 3 * $chunk_size_wav - 1;
if ($end_wav_byte > $skip_bytes + $count_bytes - 1){
$empty_bytes = $end_wav_byte - ($skip_bytes + $count_bytes - 1);
$end_wav_byte = $skip_bytes + $count_bytes - 1;
}else{
$empty_bytes = 0;
}
$mp3_data = shell_exec("php /path/to/dd.php \"$bin_path\" $start_wav_byte ".($end_wav_byte - $start_wav_byte + 1)." $empty_bytes | ffmpeg -ss 0 -f s16le -ar 44100 -ac 2 -i pipe:0 -codec:a libmp3lame -b:a 320k -ac 2 -joint_stereo 0 -compression_level 0 -write_id3v1 0 -id3v2_version none -f mp3 -");
$ign_mp3_bytes = $i - $start_chunk_number * $chunk_size_mp3;
$j = $ign_mp3_bytes;
while($i <= $end_index && $j < 2*$chunk_size_mp3){
echo $mp3_data[(int)$j];
$i++;
$j++;
}
}
}
//-----------------------STREAM-----------------------
?>
дд.php:
<?php
if (isset($argv[3])){
$bin_file = $argv[1];
$skip_bytes = (int)$argv[2];
$count_bytes = (int)$argv[3];
if (!isset($argv[4])){
$empty_bytes = 0;
}else{
$empty_bytes = (int)$argv[4];
}
$file_size = $count_bytes;
$file_stream = fopen($bin_file, 'rb');
fseek($file_stream, $skip_bytes);
$i = 0;
$buffer = 102400;
set_time_limit(0);
while(!feof($file_stream) && $i < $count_bytes){
$bytesToRead = $buffer;
if (($i+$bytesToRead) > $count_bytes){
$bytesToRead = $count_bytes - $i;
}
echo fread($file_stream, $bytesToRead);
flush();
$i += $bytesToRead;
}
if ($empty_bytes > 0){
for($k = 0; $k < $empty_bytes; $k++){
echo "\x00";
}
}
}
?>
Несколько замечаний, которые следует сделать здесь:
Поскольку, как написано, речь не идет о программировании, вы с большей вероятностью получите ответ (как предложено в справке по тегу ffmpeg ) в Видеопроизводстве или Суперпользователе. Однако перед публикацией обязательно найдите существующие похожие вопросы и проверьте их справку. Спасибо!