순수 프런트엔드는 ffmpeg를 사용하여 비디오 압축을 달성합니다.

사용자가 동영상을 업로드하고 압축해야 한다는 요구사항을 실현하고 압축 정도를 선택할 수 있도록 하기 위해 모든 주요 웹사이트를 검색한 후 최종적으로 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을 찾는데 오랜 시간이 걸렸습니다.. 이전에 찾은 다양한 버전은 여기에 표시되지 않습니다.

간단한 코드 몇 줄을 사용했는데 코드를 실행해서 한 줄씩 출력되는 결과를 보니 성공할 것 같았는데 예상대로 첫 번째 문제가 나왔습니다.77f46c95b01148f0be26db3fc1cd72c6.png

SharedArrayBuffer 오류 해결:

배경:

1036837cecef4b8999f5e96782f9381e.png

 또 다른 검색 끝에 다음 옵션을 찾았습니다.

1.SharedArrayBuffer 다운그레이드 ArrayBuffer

if(!crossOriginIsolated) {
  SharedArrayBuffer = ArrayBuffer;
}

교차 출처 격리가 적용되는지 확인하려면  창 및 컨텍스트에서 crossOriginIsolated  속성을  worker 사용할 수 있는지 확인하고, 사용할 수 없는 경우 다운그레이드하세요.

이를 사용하면 SharedArrayBuffer 오류가 해결되었지만 다른 오류가 발생했습니다.

7ddcebaa4a2c497e997f52331318cb66.png

 오류:메모리 불량오류     : 메모리 부족

그러다가 다시 해결책을 찾아보았으나 해결하기가 너무 번거로워서 언급되지 않은 것처럼 방법이 언급되었습니다. 시간 낭비

 2. Chrome 브라우저에 Chrome Origin Trials 추가

1) 등록 페이지에서 토큰 획득

        https://developer.chrome.com/origintrials/#/registration

 

2) 토큰은 페이지 메타 태그 또는 응답 헤더 Origin-Trial을 배치합니다.

        http-equiv="origin-trial" content="등록 후 토큰 획득"

        <meta http-equiv="origin-trial" content="등록 후 토큰 획득">

 마지막으로 다음과 같습니다.

eb6228c6641e43dab1426e0f1ee46549.png

이 방법은 간단하고 조잡하지만 Chrome 브라우저만 지원하며 다른 브라우저에서는 여전히 오류가 보고됩니다.

3. COOP 및 COEP 헤더 설정

다음 내용은 모두 SharedArrayBuffer 오류 보고 문제를 해결하는 내용으로, 내용도 많고 말도 안되는 내용이 많습니다. 이것들은 제가 겪은 모든 문제들입니다. 그래서 기록해 두었습니다.

SharedArrayBuffer - JavaScript | MDN (mozilla.org) https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer 공식 웹사이트에서 제공하는 솔루션:

a48be899da2846bbae616a9fe240e217.png

 저희 프로젝트는 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 파일을 찾습니다.

a8554cc427714a9fbcf570254fd7d750.png

여기에서는 앞서 언급한 SharedArrayBuffer를 해결하기 위한 구성 정보를 구성할 수 있습니다

    devServer: {
        headers: {
            "Cross-Origin-Opener-Policy": "same-origin",
            "Cross-Origin-Embedder-Policy": "require-corp",
        },
    }

그런 다음 npm run submit 코드를 실행합니다.

예상치 못한 일은 일어나지 않았고 실제로 일어나지 않았습니다. 압축 성공

420eec25f17c439eab7e9adf74421d4a.png

edeg 브라우저 테스트에 성공하여 오류 문제가 해결되었습니다. 영상은 6M에서 3M로 압축되었습니다. 압축 효과는 여전히 매우 좋으며 기본적으로 차이를 볼 수 없습니다. 압축 해상도 코드는 -b '2000000'을 통해 조절할 수 있으며 최대값은 2000000이다. 값이 클수록 압축률이 높아진다. 최소값은 모르겠다. 직접 해보면 된다. ffmpeg 공식 웹사이트에는 다양한 사용 방법이 있으며 매우 강력합니다.

약간의 떨림을 안고 Chrome 브라우저를 테스트하러 갔는데, 놀랍지 않게도 정말 잘 작동했습니다. 울어서 죽었는데...

이렇게 하면 모든 브라우저에서 성공적으로 영상을 압축할 수 있는데, 로컬에서 했다는 생각에 기뻤고, Vue 프로젝트였다고 생각합니다. 온라인에 접속한 후에 또 어떤 문제가 발생할지 아무도 모릅니다.

앞서 언급했듯이 우리 프로젝트는 Think PHP를 기반으로 하고 있습니다. 저는 프론트엔드 초보자이고 로컬에서 작성한 것과 Think PHP를 결합하는 방법을 모릅니다. 전혀 이해가 되지 않습니다.

오랜 고민 끝에 SharedArrayBuffer 문제를 로컬에서 테스트하기 전에 먼저 해결해야 하지 않을까요 ?만 해결되면 문제가 없을 것 같습니다. 그런 다음 Think PHP에서 헤더를 구성하는 방법을 검색하기 시작했습니다. 헤더를 구성할 때 발생하는 문제는 다음과 같습니다.

4. 헤더 정보 구성

1ecd23ad95b5475aaec0992c7a15a0db.png

이 구성 파일에 처음 구성되었을 때 당연히 검색도 됐습니다.

그러면 그 과정에서 겪게 되는 다양한 시행착오에 대해서는 이야기하지 않겠습니다. 결론은 이 방법은 효과가 없다는거네요

하지만! ! ! 오늘 한 방법을 시도해봤는데 정말 효과가 있어요 

 

SharedArrayBuffer 오류 해결 :

페이지 컨트롤러를 찾았는데 바로 성공했습니다. 우리는 백엔드가 아니며 이해하지 못합니다.

코드는 아래와 같이 표시됩니다.

        header('Cross-Origin-Opener-Policy: same-origin');
        header('Cross-Origin-Embedder-Policy: require-corp');

5c4b7806285d4f05abee3d725cdc0b90.png

 이번에는 정말 오류 문제는 해결됐지만 여전히 압축에 실패했습니다. 왜냐하면:

iframe 및 스크립트 태그 로드와 같은 도메인 간 리소스 로드에 영향을 줍니다. 페이지의 모든 리소스가 비활성화됩니다. 그리고 ffmpeg의 CDN을 사용하고 있어서 직접 사용할 수는 없습니다. 이 파일을 다운로드했더니 ffmpeg.min.js에 CDN 링크가 있었습니다.

거의 무너졌습니다.

말도 안되는 이야기를 많이 한 후에 최종 해결 방법으로 바로 이동하겠습니다.

 

**핵심사항, 최종 시행계획! ! ! ! **

즉, 로컬에서 테스트할 때와 동일한 방식으로 npm을 사용하여 ffmpeg 패키지를 다운로드하는 것입니다.

Think PHP에서 npm 사용하기

  1. 개발 환경에 Node.js 및 npm이 설치되어 있는지 확인하세요. 명령줄에 node -v및 를 입력하여 npm -v설치를 확인할 수 있습니다.
  2. ThinkPHP 5 프로젝트의 루트 디렉터리에서 명령줄이나 터미널을 열고 현재 디렉터리가 ThinkPHP 프로젝트의 루트 디렉터리에 있는지 확인하세요.
  3. 다음 명령을 실행하여 Node.js 패키지 관리자를 설치하세요.
    npm install
    
  4. 다른 특정 npm 패키지를 설치해야 하는 경우 package.json프로젝트 루트에 파일을 만들고 거기에 있는 dependencies또는 필드에 devDependencies필요한 종속성을 추가할 수 있습니다.

  5. 로컬에서 테스트해보겠습니다package.json内容直接复制到项目根目录创建的package.json上

  6. 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>

마지막으로 문제가 해결되었습니다. 여전히 헤더를 구성해야 하며 다른 구성은 필요하지 않습니다. 모든 브라우저가 작동합니다.

 

추천

출처blog.csdn.net/weixin_57667398/article/details/132686867