mirror of
https://github.com/flutter/holobooth.git
synced 2025-05-17 21:36:00 +08:00
fix: making the correct image adjustments on the cloud functions (#299)
* fix: making the correct image adjustments on the cloud functions * fixing lint and timeout issue
This commit is contained in:
1343
functions/package-lock.json
generated
1343
functions/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -27,6 +27,7 @@
|
|||||||
"firebase-functions": "^4.1.0",
|
"firebase-functions": "^4.1.0",
|
||||||
"fluent-ffmpeg": "^2.1.2",
|
"fluent-ffmpeg": "^2.1.2",
|
||||||
"fs": "^0.0.1-security",
|
"fs": "^0.0.1-security",
|
||||||
|
"jimp": "^0.16.2",
|
||||||
"mustache": "^4.2.0",
|
"mustache": "^4.2.0",
|
||||||
"uuid": "^9.0.0"
|
"uuid": "^9.0.0"
|
||||||
},
|
},
|
||||||
|
@ -120,7 +120,6 @@ function setUpReadable(currentEvent: string) {
|
|||||||
return readable;
|
return readable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
jest.mock('busboy', () => () => {
|
jest.mock('busboy', () => () => {
|
||||||
return {
|
return {
|
||||||
end: jest.fn(),
|
end: jest.fn(),
|
||||||
@ -135,6 +134,20 @@ jest.mock('busboy', () => () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
jest.mock('jimp', () => {
|
||||||
|
return {
|
||||||
|
read: jest.fn().mockImplementation((name) => {
|
||||||
|
const _name = name || '';
|
||||||
|
return {
|
||||||
|
bitmap: {
|
||||||
|
width: _name.indexOf('odd') != -1 ? 501 : 600,
|
||||||
|
height: _name.indexOf('odd') != -1 ? 301 : 400,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
jest.mock('fluent-ffmpeg', () => () => {
|
jest.mock('fluent-ffmpeg', () => () => {
|
||||||
return {
|
return {
|
||||||
addInput: jest.fn().mockReturnThis(),
|
addInput: jest.fn().mockReturnThis(),
|
||||||
@ -340,6 +353,33 @@ describe('convertToVideo', () => {
|
|||||||
).resolves.toBe(`${tempDir}/video.mp4`);
|
).resolves.toBe(`${tempDir}/video.mp4`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when the dimensions of the image are even', () => {
|
||||||
|
it('keeps the dimension on the scale flag', async () => {
|
||||||
|
setUpFfmpeg('end');
|
||||||
|
await convert.convertToVideo(ffmpeg, [ `${tempDir}/frame_1.png` ], tempDir);
|
||||||
|
|
||||||
|
expect(ffmpeg.addOptions).toHaveBeenCalledWith([
|
||||||
|
'-codec:v libx264',
|
||||||
|
'-s 600x400',
|
||||||
|
'-pix_fmt yuv420p',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the dimensions of the image are odd', () => {
|
||||||
|
it('uses the nearest (down) even number', async () => {
|
||||||
|
setUpFfmpeg('end');
|
||||||
|
|
||||||
|
await convert.convertToVideo(ffmpeg, [ `${tempDir}/frame_odd.png` ], tempDir);
|
||||||
|
|
||||||
|
expect(ffmpeg.addOptions).toHaveBeenCalledWith([
|
||||||
|
'-codec:v libx264',
|
||||||
|
'-s 500x300',
|
||||||
|
'-pix_fmt yuv420p',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('throws error when unable to convert frames.', async () => {
|
it('throws error when unable to convert frames.', async () => {
|
||||||
setUpFfmpeg('error');
|
setUpFfmpeg('error');
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ import * as path from 'path';
|
|||||||
|
|
||||||
import { UPLOAD_PATH, ALLOWED_HOSTS } from '../config';
|
import { UPLOAD_PATH, ALLOWED_HOSTS } from '../config';
|
||||||
import ffmpeg from 'fluent-ffmpeg';
|
import ffmpeg from 'fluent-ffmpeg';
|
||||||
|
import Jimp from 'jimp';
|
||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import _busboy from 'busboy';
|
import _busboy from 'busboy';
|
||||||
@ -33,7 +35,7 @@ export const convert = functions.https.onRequest(async (req, res) => {
|
|||||||
export async function convertImages(
|
export async function convertImages(
|
||||||
req: functions.https.Request,
|
req: functions.https.Request,
|
||||||
): Promise<{
|
): Promise<{
|
||||||
status: number;
|
status: number,
|
||||||
videoUrl: string,
|
videoUrl: string,
|
||||||
gifUrl: string,
|
gifUrl: string,
|
||||||
}> {
|
}> {
|
||||||
@ -59,8 +61,10 @@ export async function convertImages(
|
|||||||
const videoPath = await convertToVideo(ffmpeg(), frames, tempDir);
|
const videoPath = await convertToVideo(ffmpeg(), frames, tempDir);
|
||||||
const gifPath = await convertVideoToGif(ffmpeg(), videoPath, tempDir);
|
const gifPath = await convertVideoToGif(ffmpeg(), videoPath, tempDir);
|
||||||
|
|
||||||
const videoUrl = await uploadFile(userId + '.mp4', videoPath);
|
const [ videoUrl, gifUrl ] = await Promise.all([
|
||||||
const gifUrl = await uploadFile(userId + '.gif', gifPath);
|
uploadFile(userId + '.mp4', videoPath),
|
||||||
|
uploadFile(userId + '.gif', gifPath),
|
||||||
|
]);
|
||||||
|
|
||||||
return { status: 200, videoUrl, gifUrl };
|
return { status: 200, videoUrl, gifUrl };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -132,16 +136,35 @@ export async function proceedFile(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function adjustDimensions(width: number, height: number): {
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
} {
|
||||||
|
const w = width % 2 == 0 ? width : width - 1;
|
||||||
|
const h = height % 2 == 0 ? height : height - 1;
|
||||||
|
|
||||||
|
return { width: w, height: h };
|
||||||
|
}
|
||||||
|
|
||||||
export async function convertToVideo(
|
export async function convertToVideo(
|
||||||
ffmpeg: ffmpeg,
|
ffmpeg: ffmpeg,
|
||||||
frames: string[],
|
frames: string[],
|
||||||
folder: string
|
folder: string
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const videoPath = `${folder}/video.mp4`;
|
const videoPath = `${folder}/video.mp4`;
|
||||||
|
|
||||||
|
const image = await Jimp.read(frames[0]);
|
||||||
|
const adjustedDimensions = adjustDimensions(
|
||||||
|
image.bitmap.width,
|
||||||
|
image.bitmap.height,
|
||||||
|
);
|
||||||
|
|
||||||
|
const scaleArg = `${adjustedDimensions.width}x${adjustedDimensions.height}`;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
ffmpeg
|
ffmpeg
|
||||||
.addInput(folder + '/frame_%d.png')
|
.addInput(folder + '/frame_%d.png')
|
||||||
.addOptions([ '-codec:v libx264', '-s 980x620', '-pix_fmt yuv420p' ])
|
.addOptions([ '-codec:v libx264', `-s ${scaleArg}`, '-pix_fmt yuv420p' ])
|
||||||
.inputFPS(frames.length / 5)
|
.inputFPS(frames.length / 5)
|
||||||
.mergeToFile(videoPath)
|
.mergeToFile(videoPath)
|
||||||
.on('end', () => {
|
.on('end', () => {
|
||||||
|
Reference in New Issue
Block a user