小さなファイルに最適な ffmpeg と Mediainfo を使用して、mp4 ビデオ ファイルからサムネイル画像を作成する Lambda 関数を作成しました。
これまでのところ、サイズが 372.5 KB と 73.4 KB のファイルのサムネイル イメージの作成には成功していますが、サイズが 2.9 MB と 7.9 MB のファイルではエラーが発生しました。
CloudWatch ログに次のエラーが表示されます。
https://s3-us-west-2.amazonaws.com/object-path, HTTP server doesn't seem to support byte ranges. Cannot resume.
Mediainfo を使用してビデオ メタデータを抽出しようとすると、エラーが発生します。EC2 環境に libcurl を使用して Mediainfo バイナリをインストールしました。
私は cURL、Mediainfo、および Lambda の比較的初心者なので、これを理解しようとして限界に達したと感じています。この特定のエラーが Lambda ノード環境が原因で発生しているのか、それとも Mediainfo と関係があるのかはわかりません。
これを解決するための助けをいただければ幸いです。必要に応じて、より明確な情報を提供できます。
参照用コード --
process.env.PATH = process.env.PATH + ":/tmp/";
var child_process = require("child_process");
child_process.exec(
"cp /var/task/ffmpeg /tmp/.; chmod 755 /tmp/ffmpeg;",
function (error, stdout, stderr) {
if (error) {
console.log(error);
}
}
);
var mediainfo = require("mediainfo-wrapper");
var async = require("async");
var AWS = require("aws-sdk");
var fs = require("fs");
var utils = {
decodeKey: function(key) {
return decodeURIComponent(key).replace(/\+/g, " ");
}
};
var s3 = new AWS.S3();
var thumbKeyPrefix = "thumbnails/",
thumbWidth = 300,
thumbHeight = 300,
allowedFileTypes = ["mp4"];
exports.handler = function(event, context) {
var tmpFile = fs.createWriteStream("/tmp/screenshot.jpg");
var srcKey = utils.decodeKey(event.Records[0].s3.object.key),
bucket = event.Records[0].s3.bucket.name,
dstKey = thumbKeyPrefix + srcKey.replace(/\.\w+$/, ".jpg"),
fileType = srcKey.match(/\.\w+$/),
target = s3.getSignedUrl("getObject",{Bucket:bucket, Key:srcKey, Expires: 900}),
metadata = {width: 0, height: 0, duration: 0};
if(srcKey.indexOf(thumbKeyPrefix) === 0) return;
if (fileType === null) {
context.fail("Invalid filetype found for key: " + srcKey);
return;
}
fileType = fileType[0].substr(1);
if (allowedFileTypes.indexOf(fileType) === -1) {
context.fail("Filetype " + fileType + " not valid for thumbnail, exiting");
return;
}
async.waterfall([
function createMetaData(next) {
console.log('creating metadata...');
mediainfo(target).then(function(data) {
metadata.width = data[0].video[0].width[0] * 1;
metadata.height = data[0].video[0].height[0] * 1;
metadata.duration = data[0].video[0].duration[0] * 1;
next(null);
}).catch(function(err) {console.error(err)}); // ERROR LOGGED HERE
},
function createThumbnail(next) {
console.log("creating thumbnail...");
// use ffmpeg and metadata to create thumbnail
// compute formattedTime, width, height ... cut for brevity
var ffmpeg = child_process.spawn("ffmpeg", [
"-ss", formattedTime, // time to take screenshot
"-i", target, // url to stream from
"-vf", "thumbnail,scale="+width+":"+height,
"-q:v", "2",
"-vframes", "1",
"-f", "image2",
"-c:v", "mjpeg",
"pipe:1"
]);
ffmpeg.on("error", function(err) {
console.log(err);
})
ffmpeg.on("close", function(code) {
if (code !== 0 ) {
console.log("child process exited with code " + code);
} else {
console.log("Processing finished! Code: ", code);
}
tmpFile.end();
next(code);
});
tmpFile.on("error", function(err) {
console.log("stream err: ", err);
});
ffmpeg.on("end", function() {
tmpFile.end();
});
ffmpeg.stdout.pipe(tmpFile)
.on("error", function(err) {
console.log("error while writing: ", err);
});
},
function uploadThumbnail(next) {
var tmpFile = fs.createReadStream("/tmp/screenshot.jpg");
child_process.exec("echo `ls -l -R /tmp`",
function (error, stdout, stderr) {
console.log("upload stdout: " + stdout)
});
var params = {
Bucket: bucket,
Key: dstKey,
Body: tmpFile,
ContentType: "image/jpg",
ACL: "public-read",
Metadata: {
thumbnail: "TRUE"
}
};
var uploadMe = s3.upload(params);
uploadMe.send(
function(err, data) {
if (err != null) console.log("error: " +err);
next(err);
}
);
}
],
function(err) {
if (err) {
console.error("Unable to generate thumbnail for '" + bucket + "/" + srcKey + "'" + " due to error: " + err);
context.fail(err);
} else {
context.succeed("Created thumbnail for '" + bucket + "/" + srcKey + "'");
}
}
);
};