Pure front-end uses ffmpeg to achieve video compression

To realize the requirement that users upload videos and compress them, and can choose the degree of compression, I searched all major websites and finally chose ffmpeg for operation. This article contains specific instructions on how to implement it and various pitfalls encountered during the process.

 ffmpeg video compression and transcoding

The ffmpeg video compression code is very simple to use, just upload the code

html part

<h3>视频前端压缩</h3>
<video id="output-video" controls></video><br/>
<input type="file" id="uploader">
<p id="message"></p>

 js part

    // 引入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>

It took me a long time to find this CDN processed by the master of ffmpeg. The various versions I found before are not displayed here.

I used a few simple lines of code. When I ran the code and saw the printed results coming out line by line, I thought I was going to succeed. As expected, the first problem came.77f46c95b01148f0be26db3fc1cd72c6.png

Solve SharedArrayBuffer error:

background:

1036837cecef4b8999f5e96782f9381e.png

 After another search, I found the following options.

1.SharedArrayBuffer downgrade ArrayBuffer

if(!crossOriginIsolated) {
  SharedArrayBuffer = ArrayBuffer;
}

To check whether cross-origin isolation is in effect, you can check  whether the crossOriginIsolated  attribute  worker is available in the window and context: downgrade if it is not available.

Using this did solve the SharedArrayBuffer error, but it caused another error.

7ddcebaa4a2c497e997f52331318cb66.png

 error:bad memoryError     : insufficient memory

Then I searched for a solution again, but it was too troublesome to solve, so the method was mentioned as if it was not mentioned. waste time

 2. Add Chrome Origin Trials to Chrome browser

1) Obtain Token on the registration page

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

 

2) Token places the page meta tag or response header Origin-Trial

        http-equiv="origin-trial" content="Token obtained after registration"

        <meta http-equiv="origin-trial" content="Token obtained after registration">

 Finally like this:

eb6228c6641e43dab1426e0f1ee46549.png

This method is simple and crude, but it only supports the Chrome browser. Other browsers still report errors.

3. Set COOP and COEP headers

All the following content is about solving SharedArrayBuffer error reporting problem. There is a lot of content and a lot of nonsense. These are all problems I encountered, so I recorded them.

SharedArrayBuffer - JavaScript | MDN (mozilla.org) https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer The solution given by the official website:

a48be899da2846bbae616a9fe240e217.png

 Since our project is based on Think PHP and my skills are not very good, I really don’t know where to configure the header.

After searching for a while, I decided to test it locally first. I am using vue2, and this time I made another version locally, but it is basically similar. Pretty much the same

1.npm download ffmpeg resource package

2. Upload the code

<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. We have gone too far here, we still have to return to the topic and solve the problem of SharedArrayBuffer

We find the vue.config.js file in the root directory

a8554cc427714a9fbcf570254fd7d750.png

Here you can configure the configuration information for solving the SharedArrayBuffer mentioned earlier. 

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

Then we run the code npm run serve...

Nothing unexpected happened, it really didn't happen. Compression successful

420eec25f17c439eab7e9adf74421d4a.png

The edeg browser test was successful, and the error problem was solved. The video was compressed from 6M to 3M. The compression effect is still very good, and basically no difference can be seen. The compression resolution code can be adjusted through -b '2000000'. The maximum value is 2000000. The larger the value, the greater the compression rate. I don’t know the minimum value. You can try it yourself. The ffmpeg official website has many methods of use and is very powerful.

I went to test the Chrome browser with some trepidation. No surprise, it really worked fine. I cried to death...

In this way, all browsers can successfully compress the video. I was happy to think that I did it locally, and it was a Vue project. No one knows what else will go wrong after it goes online.

As I mentioned before, our project is based on think php. I am a novice at front-end and I don’t know how to combine locally written and think php. I really don’t understand it at all.

After thinking for a long time, shouldn't I solve the problem of SharedArrayBuffer before testing it locally? As long as it is solved, there should be no problem. Then I started searching how to configure header in think php. The following are the problems encountered when configuring headers

4. Configure header information

1ecd23ad95b5475aaec0992c7a15a0db.png

The first time it was configured in this configuration file, of course it was also searched.

Then I won’t talk about the various trials and errors in the process. The conclusion is that this method does not work

but! ! ! I tried a method today and it really works 

 

Solve SharedArrayBuffer error:

We found the page controller, and it succeeded directly. We are not the backend and we don’t understand it.

code show as below:

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

5c4b7806285d4f05abee3d725cdc0b90.png

 This time the error problem was really solved, but the compression was still unsuccessful. Because:

Affects the loading of cross-domain resources, such as iframe and script tag loading. All resources on your page will be inactive. And I use ffmpeg's CDN, so I can't use it directly. When I downloaded this file, there was a CDN link in ffmpeg.min.js.

Almost collapsed.

After talking so much nonsense, let’s go straight to the final solution.

 

**Key points, final implementation plan! ! ! ! **

That is to use npm to download the ffmpeg package in the same way as when testing locally.

Using npm in think php

  1. Make sure your development environment has Node.js and npm installed. You can check their installation by typing node -vand on the command line .npm -v
  2. In the root directory of the ThinkPHP 5 project, open a command line or terminal and make sure the current directory is in the root directory of the ThinkPHP project.
  3. Run the following command to install the Node.js package manager
    npm install
    
  4. If you need to install other specific npm packages, you can create a package.jsonfile in the root of your project and add the required dependencies in the dependenciesor field there.devDependencies

  5. I will test it locallypackage.json内容直接复制到项目根目录创建的package.json上

  6. Please note that ThinkPHP 5 itself does not interact directly with npm, but integrates with npm by using front-end resources. This means you need to create a directory related to your front-end project (for example public/static) in the root directory of your ThinkPHP project and place your front-end resources in that directory. You can then use these front-end resources in ThinkPHP templates.

The last step is to run npm install to download the package and use it in your code

<script type="text/javascript" src="/node_modules/@ffmpeg/ffmpeg/dist/ffmpeg.min.js"></script>

Complete code

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>

Finally solved, you still need to configure the header, no other configuration is required. Any browser will work

 

Guess you like

Origin blog.csdn.net/weixin_57667398/article/details/132686867