備忘錄_20160105(定位)
修改
回首頁
程式 2025-10-19 20:15:51 1760876151 100
一張圖片(jpg or png ......),一個音檔(mp3 or m4a ......),組成一個影片(mp4 [h.264+aac])
一張圖片(jpg or png ......),一個音檔(mp3 or m4a ......),組成一個影片(mp4 [h.264+aac])
前往試試
index.php
<!doctype html>
<html lang="zh-Hant-TW">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>圖片+聲音→影片</title>
<style>
*
{
font-size: 24pt;
}
.classTitle
{
font-size: 36pt;
}
#divProgress
{
white-space: pre-wrap; /* 保留空格和換行,長行自動換行 */
word-wrap: break-word; /* 避免超長單字溢出 */
font-family: monospace; /* 可選,看起來像 pre */
}
</style>
</head>
<body>
<div class="classTitle">圖片+聲音→影片</div>
<br>
<form class="classFormMaster" name="frm20251020102957" id="frm20251020102957" method="post" action="convert2mp4.php" enctype="multipart/form-data">
<div>
<label for="fileImage">圖片</label>
<input type="file" name="fileImage" id="fileImage" accept="image/*" required>
</div>
<div>
<label for="fileAudio">聲音</label>
<input type="file" name="fileAudio" id="fileAudio" accept="audio/*" required>
</div>
<input type="hidden" name="txtUniq" id="txtUniq" value="">
<button type="button" id="btnSubmit" onclick="checkAndSubmit_master();">送出</button>
</form>
<div id="divProgress"></div>
<script>
function gebi(strId)
{
return document.getElementById(strId);
}
function checkAndSubmit_master()
{
gebi('btnSubmit').setAttribute("disabled",true);
gebi('txtUniq').value="progress-"+Math.random().toString(36).substr(2, 16)+".txt";
while(true)
{
if(gebi('fileImage').files.length<1)
{
alert("抱歉,您還沒有選擇圖片喔!請修正後再上傳,謝謝!");
break;
}
if(gebi('fileAudio').files.length<1)
{
alert("抱歉,您還沒有選擇聲音喔!請修正後再上傳,謝謝!");
break;
}
gebi('frm20251020102957').submit();
gebi("divProgress").innerHTML="上傳中,請耐心等待,謝謝!";
showProgress();
return;
}
gebi('btnSubmit').removeAttribute("disabled");
}
async function showProgress()
{
let strFilename="tmp/"+gebi('txtUniq').value;
let oRes=await fetch(strFilename+"?rand=" + Math.random());
let strTxt = await oRes.text();
console.log((new Date()).toLocaleString());
if(strTxt.indexOf("progress=end")!=-1)
{
gebi('divProgress').innerHTML="";
gebi('btnSubmit').removeAttribute("disabled");
}
else if(strTxt.indexOf("404 Not Found")!=-1)
{
gebi('divProgress').innerHTML="上傳中,請稍候~~~"+(new Date()).toLocaleString();
window.setTimeout(showProgress, 1000);
}
else
{
var iPtr=strTxt.lastIndexOf('frame=');
if(iPtr!=-1)
{
strTxt=strTxt.substring(iPtr);
}
gebi('divProgress').textContent=strTxt;
window.scrollTo(0, document.body.scrollHeight);
window.setTimeout(showProgress, 2000);
}
}
</script>
</body>
</html>
convert2mp4.php
<?php
//$strTmpDir=sys_get_temp_dir();
$strTmpDir=rtrim(dirname(realpath(__FILE__)), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . "tmp";
$strExtImage=pathinfo($_FILES['fileImage']['name'], PATHINFO_EXTENSION);
$strExtAudio=pathinfo($_FILES['fileAudio']['name'], PATHINFO_EXTENSION);
$strImage=$strTmpDir . "/" . uniqid() . "." . $strExtImage;
$strAudio=$strTmpDir . "/" . uniqid() . "." . $strExtAudio;
$strProgressFilename=$strTmpDir . "/" . $_REQUEST['txtUniq'];
$booImage=move_uploaded_file($_FILES['fileImage']['tmp_name'], $strImage);
$booAudio=move_uploaded_file($_FILES['fileAudio']['tmp_name'], $strAudio);
$strVideo=$strTmpDir . "/" . uniqid() . ".mp4";
// 檢查檔案
if (!file_exists($strImage) || !file_exists($strAudio))
{
die("缺少檔案,終止執行!(上傳檔案上限為100MB)");
}
// ffmpeg 指令
$strCmd = sprintf(
'ffmpeg -loop 1 -i %s -i %s -vf "scale=\'trunc(min(iw\\,1280)/2)*2:trunc(min(ih\\,720)/2)*2\',format=yuv420p" -c:v libx264 -tune stillimage -c:a aac -b:a 192k -pix_fmt yuv420p -shortest -progress %s %s 2>&1',
escapeshellarg($strImage),
escapeshellarg($strAudio),
escapeshellarg($strProgressFilename),
escapeshellarg($strVideo)
);
file_put_contents($strTmpDir . '/log.log', $strCmd . "\r\n" , FILE_APPEND);
// 執行 ffmpeg
exec($strCmd, $strLog, $strRet);
// 清除原始檔
@unlink($strImage);
@unlink($strAudio);
// 檢查結果
if(file_exists($strVideo)==false)
{
header("Content-Type: text/plain; charset=utf-8");
echo "影片產生失敗。\r\n";
echo htmlspecialchars(implode("\r\n", $strLog));
exit;
}
// 回傳下載
header('Content-Description: File Transfer');
header('Content-Type: video/mp4');
header('Content-Disposition: attachment; filename="result.mp4"');
header('Content-Length: ' . filesize($strVideo));
readfile($strVideo);
// 刪除暫存影片,與進度
@unlink($strVideo);
@unlink($strProgressFilename);
?>