5328 (1) [Avatar] Offline
#1
If you’re on Windows, it’s trickier because it doesn’t come with the
chmod command. One way you can solve this problem is by spinning up an Amazon
Linux machine in AWS, copying FFprobe over, changing permissions, and then copying
the file back.


I found copying the file alone did not work, as Windows does not preserve the changed permissions when the file is copied back. I copied the tar archive file containing ffprobe, changed its permissions in situ, and copied the archive file back. I then deployed the archive file as one of the function artefacts and extracted ffprobe to /bin/ (in the class constructor).

I've used C# rather than node.js. Below is the C# code for the three functions in Chapter 3 (without warranty!), as well as the code to extract ffprobe (in the constructor). I hope this is of help to anybody else using Windows and C#. And if there's a better way of handling ffprobe (other than deploying from Linux), please do post it!

using Amazon.Lambda.Core;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]

namespace AwsServerless
{
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Text.RegularExpressions;
    using System.Threading;
    using System.Threading.Tasks;
    using Amazon;
    using Amazon.ElasticTranscoder;
    using Amazon.ElasticTranscoder.Model;
    using Amazon.Lambda.SNSEvents;
    using Amazon.S3;
    using Amazon.S3.Model;
    using Amazon.S3.Util;
    using ICSharpCode.SharpZipLib.GZip;
    using ICSharpCode.SharpZipLib.Tar;

    public class Functions
    {
        /// <summary>
        /// Default constructor that Lambda will invoke.
        /// </summary>
        public Functions()
        {
            var ffmpegArchiveFile = new FileInfo(@"./ffmpeg.static.64bit.2014-07-16.tar.gz");

            using (Stream ffmpegStream = new GZipInputStream(ffmpegArchiveFile.OpenRead()))
            {
                using (TarArchive ffmpegArchive = TarArchive.CreateInputTarArchive(ffmpegStream))
                {
                    var tmpFolder = new DirectoryInfo(@"/tmp/");

                    ffmpegArchive.ExtractContents(tmpFolder.FullName);
                }
            }
        }

        public async Task TranscodeVideoAsync(S3EventNotification s3Event, ILambdaContext context)
        {
            string key = s3Event.Records[0].S3.Object.Key;
            var regex = new Regex(@"\+/g");
            string sourceKey = Uri.UnescapeDataString(regex.Replace(key, " "));
            string outputKey = sourceKey.Split('.')[0];

            context.Logger.LogLine($"key: {key} {sourceKey} {outputKey}");

            using (var elasticTranscoder = new AmazonElasticTranscoderClient(RegionEndpoint.EUWest1))
            {
                CreateJobResponse response = await elasticTranscoder.CreateJobAsync(
                    new CreateJobRequest
                    {
                        PipelineId = "1493990262706-xk1bcy",
                        OutputKeyPrefix = $"{outputKey}/",
                        Input = new JobInput { Key = sourceKey },
                        Outputs =
                                {
                                new CreateJobOutput
                                    {
                                        Key = $"{outputKey}-1080p.mp4",
                                        PresetId = "1351620000001-000001"
                                    },
                                new CreateJobOutput
                                    {
                                        Key = $"{outputKey}-720p.mp4",
                                        PresetId = "1351620000001-000010"
                                    },
                                new CreateJobOutput
                                    {
                                        Key = $"{outputKey}-web-720p.mp4",
                                        PresetId = "1351620000001-100070"
                                    }
                                }
                    });

                context.Logger.LogLine($"response: {response.HttpStatusCode}");
            }
        }

        public async Task SetPermissionsAsync(SNSEvent snsEvent, ILambdaContext context)
        {
            var message = S3EventNotification.ParseJson(snsEvent.Records[0].Sns.Message);
            string sourceBucket = message.Records[0].S3.Bucket.Name;
            var regex = new Regex(@"\+/g");
            string sourceKey = Uri.UnescapeDataString(regex.Replace(message.Records[0].S3.Object.Key, " "));

            context.Logger.LogLine($"bucket: {sourceBucket} key: {sourceKey}");

            var request = new PutACLRequest
                              {
                                  BucketName = sourceBucket,
                                  Key = sourceKey,
                                  CannedACL = S3CannedACL.PublicRead
                              };

            PutACLResponse response;

            using (var s3 = new AmazonS3Client())
            {
                response = await s3.PutACLAsync(request);
            }

            context.Logger.LogLine($"put acl response: {response.HttpStatusCode}");
        }

        public async Task ExtractMetadataAsync(SNSEvent snsEvent, ILambdaContext context)
        {
            var message = S3EventNotification.ParseJson(snsEvent.Records[0].Sns.Message);
            string sourceBucket = message.Records[0].S3.Bucket.Name;
            var regex = new Regex(@"\+/g");
            string sourceKey = Uri.UnescapeDataString(regex.Replace(message.Records[0].S3.Object.Key, " "));

            context.Logger.LogLine($"bucket: {sourceBucket} key: {sourceKey}");

            string filePath = await this.SaveFileToFileSystemAsync(sourceBucket, sourceKey, context);

            string metadata = ExtractMetadata(filePath, context);
            string metadataKey = $"{sourceKey.Split('.')[0]}.json";

            context.Logger.LogLine($"key: {metadataKey} metadata: {metadata}");

            await this.SaveMetadataToS3Async(metadataKey, metadata, sourceBucket, context);
        }

        private static string ExtractMetadata(string filePath, ILambdaContext context)
        {
            context.Logger.LogLine("Extracting metadata.");

            string executable = @"/tmp/ffprobe";
            string arguments = $@"-v quiet -print_format json -show_format ""{filePath}""";

            return Execute(executable, arguments);
        }

        private static string Execute(string executablePath, string arguments)
        {
            using (var process = new Process())
            {
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.FileName = executablePath;
                process.StartInfo.Arguments = arguments;
                process.Start();
                process.WaitForExit();

                return process.StandardOutput.ReadToEnd();
            }
        }

        private async Task<string> SaveFileToFileSystemAsync(string sourceBucket, string sourceKey, ILambdaContext context)
        {
            context.Logger.LogLine("Saving to file system.");

            string fileName = new FileInfo(sourceKey).Name;
            string filePath = $@"/tmp/{fileName}";

            context.Logger.LogLine($"filepath: {filePath}");

            using (var s3 = new AmazonS3Client())
            {
                GetObjectResponse response = await s3.GetObjectAsync(sourceBucket, sourceKey);

                await response.WriteResponseStreamToFileAsync(filePath, false, CancellationToken.None);
            }

            return filePath;
        }

        private async Task SaveMetadataToS3Async(string metadataKey, string metadata, string bucket, ILambdaContext context)
        {
            context.Logger.LogLine("Saving metadata to S3.");

            var s3 = new AmazonS3Client();
            var request = new PutObjectRequest { BucketName = bucket, Key = metadataKey, ContentBody = metadata };

            PutObjectResponse response = await s3.PutObjectAsync(request);

            context.Logger.LogLine($"response: {response.HttpStatusCode}");
        }
    }
}