ffmpeg 输入是通过存储桶的公共 url 进行的,我想将输出保存在 tmp.mp4 文件中,并且想将此文件上传到存储桶。本地ffmpeg命令运行完美,持续时间不超过3秒,输出文件大小不到1MB
export const handler = async (event) => {
try {
const { m3u8, offset, duration, signedURL } = event;
const clipKey = `clip_output.mp4`;
const clipFilePath = path.join('/tmp', clipKey);
execSync(`/opt/ffmpeglib/ffmpeg -i ${process.env.URL_CLOUDFLARE}/${m3u8} -ss ${offset} -t ${duration} -c copy -f mp4 ${clipFilePath}`)
const fileContent = fs.readFileSync(clipFilePath);
const resSign = await fetch(signedURL, {
method: "PUT",
headers: {
"Content-Type": "application/octet-stream",
},
body: fileContent,
});
if (!resSign.ok) throw new Error(`Failed to upload file to S3: ${resSign.statusText}`);
fs.unlinkSync(clipFilePath);
return {
statusCode: 200,
body: JSON.stringify({
message: 'Clip procesado y subido correctamente',
clipKey: path.basename(clipFilePath)
}),
};
} catch (error) {
console.error("Error al procesar el clip:", error);
return {
statusCode: 500,
body: JSON.stringify({ error: error.message }),
};
}
};
它向我显示的错误如下
at genericNodeError (node:internal/errors:984:15)
at wrappedFn (node:internal/errors:538:14)
at checkExecSyncError (node:child_process:891:11)
at execSync (node:child_process:963:15)
at Runtime.handler (file:///var/task/index.js:16:3)
at Runtime.handleOnceNonStreaming (file:///var/runtime/index.mjs:1173:29) {
status: null,
signal: 'SIGSEGV',
output: [
null,
<Buffer >,
<Buffer 66 66 6d 70 65 67 20 76 65 72 73 69 6f 6e 20 4e 2d 37 31 30 36 34 2d 67 64 35 65 36 30 33 64 64 63 30 2d 73 74 61 74 69 63 20 68 74 74 70 73 3a 2f 2f ... 1194 more bytes>
],
pid: 13,
stdout: <Buffer >,
stderr: <Buffer 66 66 6d 70 65 67 20 76 65 72 73 69 6f 6e 20 4e 2d 37 31 30 36 34 2d 67 64 35 65 36 30 33 64 64 63 30 2d 73 74 61 74 69 63 20 68 74 74 70 73 3a 2f 2f ... 1194 more bytes>
}
ffmpeg version N-71064-gd5e603ddc0-static https://johnvansickle.com/ffmpeg/ Copyright (c) 2000-2024 the FFmpeg developers
built with gcc 8 (Debian 8.3.0-6)
configuration: --enable-gpl --enable-version3 --enable-static --disable-debug --disable-ffplay --disable-indev=sndio --disable-outdev=sndio --cc=gcc --enable-fontconfig --enable-frei0r --enable-gnutls --enable-gmp --enable-libgme --enable-gray --enable-libaom --enable-libfribidi --enable-libass --enable-libvmaf --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librubberband --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libvorbis --enable-libopus --enable-libtheora --enable-libvidstab --enable-libvo-amrwbenc --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libdav1d --enable-libxvid --enable-libzvbi --enable-libzimg
libavutil 59. 27.100 / 59. 27.100
libavcodec 61. 9.100 / 61. 9.100
libavformat 61. 4.100 / 61. 4.100
libavdevice 61. 2.100 / 61. 2.100
libavfilter 10. 2.102 / 10. 2.102
libswscale 8. 2.100 / 8. 2.100
libswresample 5. 2.100 / 5. 2.100
libpostproc 58. 2.100 / 58. 2.100
END RequestId: 2fc8c51e-66c6-4c74-aa9c-fa10c11207a0
REPORT RequestId: 2fc8c51e-66c6-4c74-aa9c-fa10c11207a0 Duration: 164.82 ms Billed Duration: 165 ms Memory Size: 2048 MB Max Memory Used: 88 MB Init Duration: 185.19 ms
请帮忙解决
将输入作为公共 URL 提供给 ffmpeg 时存在限制。
正如这里提到的ffmpeg/git-readme.txt
注意:静态链接 glibc 的一个限制是 DNS 解析的丢失。通过包管理器安装 nscd 将解决此问题。
这意味着您不能再在使用静态构建在 Lambda 上运行时进行 DNS 查询的任何场景中使用 FFMpeg,例如向其传递预签名 URL。解决此问题的一种方法是下载内容,然后将其传递给 FFMpeg 进行处理。
这就是我在
/tmp
目录中下载文件的方法。
async function downloadFileFromPresignedUrl(presignedUrl: string, fileName: string): Promise<string | null> {
try {
// Send a GET request to the presigned URL
const response = await axios.get(presignedUrl, {
responseType: 'arraybuffer'
});
// Construct the full path for the file in /tmp
const tmpFilePath = path.join('/tmp', fileName);
// Write the content to a file in /tmp
fs.writeFileSync(tmpFilePath, response.data);
logger.info(`File downloaded successfully to ${tmpFilePath}`);
return tmpFilePath;
} catch (error) {
logger.error('Error downloading file:', error as Error);
return null;
}
}
使用该路径作为 ffmpeg 的输入。
const fileName = objectKey.split('/').pop();
if (fileName)
await downloadFileFromPresignedUrl(presignedUrl, fileName)
else
throw new Error("Invalid file name")
// Create path for temporary screenshot
const videoFilePath = path.join('/tmp', fileName);
const screenshotFilePath = path.join('/tmp', `${fileId}.jpeg`);
const thumbnailS3Key = `drive/${userId}/thumb/${fileId}.jpeg`.toLowerCase();
await new Promise((resolve, reject) => {
ffmpeg(videoFilePath)
.inputOptions(['-ss 00:00:01'])
.outputOptions([
'-vframes 1',
'-vf scale=w=300:h=300:force_original_aspect_ratio=decrease,'
+ 'pad=300:300:(ow-iw)/2:(oh-ih)/2'
])
.output(screenshotFilePath)
.on('end', () => {
console.log('Screenshot taken successfully');
resolve(screenshotFilePath);
})
.on('error', (err) => {
console.error('Error taking screenshot:', err);
reject(err);
})
.run();
});