ユーザーがビデオをアップロードして圧縮し、圧縮の程度を選択できるという要件を実現するために、主要な Web サイトをすべて検索し、最終的に ffmpeg を操作に選択しました。この記事には、その実装方法に関する具体的な手順と、そのプロセス中に遭遇するさまざまな落とし穴が含まれています。
ffmpeg ビデオ圧縮とトランスコーディング
ffmpeg ビデオ圧縮コードは非常に簡単に使用でき、コードをアップロードするだけです。
html部分
<h3>视频前端压缩</h3>
<video id="output-video" controls></video><br/>
<input type="file" id="uploader">
<p id="message"></p>
js部分
// 引入ffmpeg.min.js
<script src="https://unpkg.com/@ffmpeg/[email protected]/dist/ffmpeg.min.js"></script>
<script>
const { createFFmpeg, fetchFile } = FFmpeg;
const message = document.getElementById('message');
const ffmpeg = createFFmpeg({
log: true,
progress: ({ ratio }) => {
message.innerHTML = `完成率: ${(ratio * 100.0).toFixed(2)}%`;
},
});
const transcode = async ({ target: { files } }) => {
const { name } = files[0];
message.innerHTML = '正在加载 ffmpeg-core.js';
await ffmpeg.load();
message.innerHTML = '开始压缩';
ffmpeg.FS('writeFile', name, await fetchFile(files[0]));
// '-b','2000000' 值越小 压缩率越大
await ffmpeg.run('-i', name,'-b','2000000','output.mp4');
message.innerHTML = '压缩完成';
const data = ffmpeg.FS('readFile', 'output.mp4');
const video = document.getElementById('output-video');
video.src = URL.createObjectURL(new Blob([data.buffer], {
type: 'video/mp4'
}));
}
document.getElementById('uploader').addEventListener('change', transcode);
</script>
ffmpeg のマスターによって処理されたこの CDN を見つけるのに長い時間がかかりました。以前に見つけたさまざまなバージョンはここには表示されていません。
数行の単純なコードを使用しました。コードを実行し、結果が 1 行ずつ出力されるのを見たとき、成功するだろうと思いました。予想通り、最初の問題が発生しました。
SharedArrayBuffer エラーを解決します。
背景:
再度検索した結果、次のオプションが見つかりました。
1.SharedArrayBuffer の ArrayBuffer のダウングレード
if(!crossOriginIsolated) {
SharedArrayBuffer = ArrayBuffer;
}
クロスオリジン分離が有効かどうかを確認するには、 crossOriginIsolated 属性 worker
がウィンドウとコンテキストで使用できるかどうかを確認します。使用できない場合はダウングレードします。
これを使用すると SharedArrayBuffer エラーは解決しましたが、別のエラーが発生しました。
エラー:メモリが不良ですエラー :メモリが不足しています
そこで再度解決方法を探したのですが、面倒なので記載されていなかったかのようにその方法が記載されていました。時間を無駄にする
2. Chrome Origin トライアル版を Chrome ブラウザに追加する
1) 登録ページでトークンを取得します
https://developer.chrome.com/origintrials/#/registration
2) トークンはページのメタタグまたは応答ヘッダーを配置します。
http-equiv="origin-trial" content="登録後に取得したトークン"
<meta http-equiv="origin-trial" content="登録後に取得したトークン">
最後に次のようになります。
この方法はシンプルで荒っぽいですが、Chrome ブラウザのみをサポートしており、他のブラウザでは引き続きエラーが報告されます。
3. COOP ヘッダーと COEP ヘッダーを設定する
以下の内容はすべて、SharedArrayBuffer エラー報告の問題の解決に関するもので、内容が多く、ナンセンスな部分も多く含まれています。これらはすべて私が遭遇した問題なので、記録しました。
SharedArrayBuffer - JavaScript | MDN (mozilla.org) https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer公式 Web サイトで提供される解決策:
私たちのプロジェクトは Think PHP をベースにしており、私のスキルがあまり高くないため、ヘッダーをどこに設定すればよいのかわかりません。
しばらく探した後、まずローカルでテストすることにしました。私は vue2 を使用しており、今回はローカルで別のバージョンを作成しましたが、基本的には同様です。ほぼ同じ
1.npm ffmpeg リソース パッケージをダウンロード
2. コードをアップロードする
<template>
<!-- tempalte部分 -->
<h3>视频前端压缩</h3>
<video id="output-video" controls :src="vedioSrc"></video><br/>
<input type="file" id="uploader" @change="initFfmpeg">
<h5 id="message">{
{ message }}</h5>
</template>
<script>
// @ is an alias to /src
//引入
import { createFFmpeg, fetchFile } from "@ffmpeg/ffmpeg";
export default {
name: "HelloWorld",
components: {},
data() {
return {
message: null,
vedioSrc: '',
};
},
methods: {
//初始化
initFfmpeg() {
let file = document.querySelector("#uploader").files[0];
console.log(file);
const ffmpeg = createFFmpeg({
corePath: "ffmpeg-core.js",
log: true,
});
//设置进度条
ffmpeg.setProgress(({ ratio }) => {
console.log(ratio);
this.percentage = Math.floor(ratio * 100);
});
//开始压缩
const transcode = async (file) => {
const { name } = file;
this.message = "Loading ffmpeg-core.js";
await ffmpeg.load();
ffmpeg.FS("writeFile", name, await fetchFile(file));
this.message = "Start transcoding";
// '-b','2000000' 值越小 压缩率越大
await ffmpeg.run("-i", name, "-b", "700000", "output.mp4");
this.message = "压缩完成";
const data = ffmpeg.FS("readFile", "output.mp4");
this.fileBytes = data.byteLength;
//把压缩后的视频进行回显
this.vedioSrc = URL.createObjectURL(
new Blob([data.buffer], { type: "video/mp4" })
);
};
transcode(file);
},
},
};
</script>
3. ここでは行き過ぎました。まだ本題に戻り、SharedArrayBuffer の問題を解決する必要があります。
ルート ディレクトリに vue.config.js ファイルが見つかります。
ここでは、前述の SharedArrayBufferを解決するための構成情報を構成できます。
devServer: {
headers: {
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp",
},
}
次に、コード npm runserve... を実行します。
予想外のことは何も起こらなかった、実際には何も起こらなかった。圧縮が成功しました
edeg ブラウザのテストは成功し、エラーの問題は解決されました。ビデオは 6M から 3M に圧縮されました。圧縮効果は依然として非常に優れており、基本的に違いは見られません。圧縮解像度コードは -b '2000000' で調整できます。最大値は 2000000 です。値が大きいほど、圧縮率は高くなります。最小値はわかりません。自分で試してみてください。ffmpeg の公式 Web サイトにはさまざまな使用方法があり、非常に強力です。
若干の不安を抱えながら Chrome ブラウザをテストしてみましたが、驚くことではありません。実際に問題なく動作しました。死ぬほど泣きました...
この方法では、すべてのブラウザでビデオを正常に圧縮できます。ローカルで実行したのは Vue プロジェクトだったと思うとうれしかったです。オンラインになった後に何が問題になるかは誰にもわかりません。
前にも述べたように、私たちのプロジェクトは think php をベースにしていますが、私はフロントエンドの初心者で、ローカルで書かれたものと think php を組み合わせる方法がわかりません。
いろいろ考えた結果、ローカルでテストする前に SharedArrayBuffer の問題を解決すべきではないでしょうか? それが解決していれば問題ありません。次に、think php でヘッダーを設定する方法を探し始めました。ヘッダーの構成時に発生する問題は次のとおりです。
4. ヘッダー情報の設定
この設定ファイルで初めて設定されたときも、当然ながら検索されます。
その過程でのさまざまな試行錯誤については話しません。結論から言うと、この方法は効果がありません
しかし!!!今日ある方法を試してみましたが、本当に効果がありました
SharedArrayBufferエラーを解決します。
ページ コントローラーが見つかり、直接成功しましたが、私たちはバックエンドではないので、それを理解していません。
コードは以下のように表示されます。
header('Cross-Origin-Opener-Policy: same-origin');
header('Cross-Origin-Embedder-Policy: require-corp');
今回、エラーの問題は実際に解決されましたが、圧縮は依然として失敗しました。なぜなら:
iframe やスクリプト タグの読み込みなど、クロスドメイン リソースの読み込みに影響します。ページ上のすべてのリソースが非アクティブになります。また、ffmpeg の CDN を使用しているため、直接使用することはできません。このファイルをダウンロードしたとき、ffmpeg.min.js に CDN リンクがありました。
ほぼ崩壊しました。
くだらない話をたくさんした後は、最終的な解決策に直行しましょう。
**重要なポイント、最終的な実装計画! !!!**
つまり、ローカルでテストする場合と同じ方法で、npm を使用して ffmpeg パッケージをダウンロードします。
think php での npm の使用
- 開発環境に Node.js と npm がインストールされていることを確認してください。コマンド ラインに
node -v
と を入力すると、npm -v
インストールを確認できます。- ThinkPHP 5 プロジェクトのルート ディレクトリでコマンド ラインまたはターミナルを開き、現在のディレクトリが ThinkPHP プロジェクトのルート ディレクトリにあることを確認します。
- 次のコマンドを実行して、Node.js パッケージ マネージャーをインストールします。
npm install
他の特定の npm パッケージをインストールする必要がある場合は、
package.json
プロジェクトのルートにファイルを作成し、そこにあるdependencies
またはフィールドにdevDependencies
必要な依存関係を追加できます。ローカルでテストしてみます
package.json内容直接复制到项目根目录创建的package.json上
ThinkPHP 5 自体は npm と直接対話せず、フロントエンド リソースを使用して npm と統合することに注意してください。これは、ThinkPHP プロジェクトのルート ディレクトリにフロントエンド プロジェクトに関連するディレクトリ (例:
public/static
) を作成し、そのディレクトリにフロントエンド リソースを配置する必要があることを意味します。これらのフロントエンド リソースを ThinkPHP テンプレートで使用できます。最後のステップは、npm install を実行してパッケージをダウンロードし、それをコードで使用することです。
<script type="text/javascript" src="/node_modules/@ffmpeg/ffmpeg/dist/ffmpeg.min.js"></script>
完全なコード
html
<h3>视频前端压缩</h3> <video id="output-video" controls></video><br/> <input type="file" id="uploader"> <p id="message"></p>
js
<script type="text/javascript" src="/node_modules/@ffmpeg/ffmpeg/dist/ffmpeg.min.js"></script> <script> const { createFFmpeg, fetchFile } = FFmpeg; const message = document.getElementById('message'); const ffmpeg = createFFmpeg({ log: true, progress: ({ ratio }) => { message.innerHTML = `完成率: ${(ratio * 100.0).toFixed(2)}%`; }, }); const transcode = async ({ target: { files } }) => { const { name } = files[0]; message.innerHTML = '正在加载 ffmpeg-core.js'; await ffmpeg.load(); message.innerHTML = '开始压缩'; ffmpeg.FS('writeFile', name, await fetchFile(files[0])); // '-b','2000000' 值越小 压缩率越大 await ffmpeg.run('-i', name,'-b','2000000','output.mp4'); message.innerHTML = '压缩完成'; const data = ffmpeg.FS('readFile', 'output.mp4'); const video = document.getElementById('output-video'); video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' })); } document.getElementById('uploader').addEventListener('change', transcode); </script>
最終的に解決しました。ヘッダーを設定する必要がありますが、他の設定は必要ありません。どのブラウザでも動作します