示例#1
0
        public static decimal GetDurationFromFfmpegLogOrMp3File(string logText, string filePath)
        {
            decimal result;
            var     parsed = GetDurationFromFfmpegLog(logText, out result);

            if (!parsed)
            {
                // Try to determine the duration directly by reading the file.
                using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
                {
                    result = RecordingUtils.GetMp3Duration(stream);
                }
            }
            return(result);
        }
示例#2
0
        public async Task <RecordingDetails> Transcode(IEnumerable <KeyValuePair <string, MemoryStream> > tracks, string userIdKey, string timeKey, string extension)
        {
            RecordingDetails result = null;

            // ffmpeg.exe works with files. Produce file paths.
            var workDirPath    = GeneralUtils.GetAppDataDir();
            var fileNamePrefix = userIdKey + "_" + timeKey;

            var trackItems = tracks
                             .Select(i => new TrackItem
            {
                Name             = i.Key,
                Stream           = i.Value,
                OriginalFile     = Path.Combine(workDirPath, String.Format("{0}_{1}.{2}", fileNamePrefix, i.Key, extension)),
                IntermidiateFile = Path.Combine(workDirPath, String.Format("{0}_{1}_intermidiate.mp3", fileNamePrefix, i.Key)),
            })
                             .ToList();

            var inputListFilePath = Path.Combine(workDirPath, fileNamePrefix + ".txt");
            var outputFilePath    = Path.ChangeExtension(inputListFilePath, ".mp3");

            const string resultFileName = "result";
            var          outputBlobName = ExerciseUtils.FormatBlobName(userIdKey, timeKey, resultFileName, "mp3");
            var          logBlobName    = ExerciseUtils.FormatBlobName(userIdKey, timeKey, resultFileName, "log");

            try
            {
                // Save the original tracks to the disk.
                foreach (var i in trackItems)
                {
                    using (FileStream stream = new FileStream(i.OriginalFile, FileMode.Create, FileAccess.Write))
                    {
                        i.Stream.WriteTo(stream);
                    }
                }

                // Convert to MP3.
                // ffmpeg fails to concatenate AMRs, the error text is misleading "mylistfile.txt: Input/output error". We convert each file separately, then concatenate MP3s.
                foreach (var i in trackItems)
                {
                    // Increase audio volume by 10dB, convert to MP3 CBR 32kbit/s.
                    var arguments = String.Format("-i \"{0}\" -af \"volume=10dB\" -b:a 32k \"{1}\"", i.OriginalFile, i.IntermidiateFile);
                    i.Log = RecordingUtils.RunFfmpeg(arguments);
                }

                // Pass the file names to concatenate to ffmpeg.exe in a text file.
                var inputListLines = trackItems.Select(i => String.Format("file '{0}'", i.IntermidiateFile));
                File.WriteAllLines(inputListFilePath, inputListLines);

                // Concatenate MP3s. Do not re-encode, copy existing streams as is.
                var concatArguments = String.Format("-f concat -i \"{0}\" -c copy \"{1}\"", inputListFilePath, outputFilePath);
                var resultLog       = RecordingUtils.RunFfmpeg(concatArguments);

                var separator = Environment.NewLine + "----------------------------------------" + Environment.NewLine;
                var logText   = String.Join(separator, trackItems.Select(i => i.Log))
                                + separator + String.Join(Environment.NewLine, inputListLines)
                                + separator + resultLog;

                var containerName = AzureStorageUtils.ContainerNames.Artifacts;
                var taskMp3       = AzureStorageUtils.UploadFromFileAsync(outputFilePath, containerName, outputBlobName, "audio/mpeg");
                var taskLog       = AzureStorageUtils.UploadTextAsync(logText, containerName, logBlobName, "text/plain");
                // Upload the blobs simultaneously.
                await Task.WhenAll(taskMp3, taskLog);

                // Get the recording durations.
                var trackDurations = trackItems
                                     .Select((i) =>
                {
                    var trackDuration = RecordingUtils.GetDurationFromFfmpegLogOrMp3File(i.Log, i.IntermidiateFile);
                    return(new KeyValuePair <string, decimal>(i.Name, trackDuration));
                })
                                     .ToDictionary(i => i.Key, i => i.Value)
                ;

                var duration = RecordingUtils.GetDurationFromFfmpegLogOrMp3File(resultLog, outputFilePath);

                // The JSON encoder with default settings doesn't make upper-case -> lower-case letter conversion of property names. The receiving side is case-sensitive.
                result = new RecordingDetails
                {
                    BlobName       = outputBlobName,
                    TotalDuration  = duration,
                    TrackDurations = trackDurations,
                };
            }
            finally
            {
                // Clean up the local disk.
                foreach (var i in trackItems)
                {
                    File.Delete(i.OriginalFile);
                    File.Delete(i.IntermidiateFile);
                }
                File.Delete(inputListFilePath);
                File.Delete(outputFilePath);
            }

            return(result);
        }