/// <summary> /// Decodes an audio stream through FFmpeg from an encoded file stream. /// Accepts an optional file name hint to help FFmpeg determine the format of /// the encoded data. /// </summary> /// <param name="stream">the stream to decode</param> /// <param name="fileName">optional file name hint for FFmpeg</param> public FFmpegSourceStream(Stream stream, string fileName) { sourceStream = stream; reader = new FFmpegReader(stream, FFmpeg.Type.Audio, fileName); if (reader.AudioOutputConfig.length == long.MinValue) { /* * length == FFmpeg AV_NOPTS_VALUE * * This means that for the opened file/format, there is no length/PTS data * available, which also makes seeking more or less impossible. * * As a workaround, an index could be created to map the frames to the file * position, and then seek by file position. The index could be created by * linearly reading through the file (decoding not necessary), and creating * a mapping of AVPacket.pos to the frame time. */ throw new FileNotSeekableException(); } properties = new AudioProperties( reader.AudioOutputConfig.format.channels, reader.AudioOutputConfig.format.sample_rate, reader.AudioOutputConfig.format.sample_size * 8, reader.AudioOutputConfig.format.sample_size == 4 ? AudioFormat.IEEE : AudioFormat.LPCM); readerPosition = 0; sourceBuffer = new byte[reader.AudioOutputConfig.frame_size * reader.AudioOutputConfig.format.channels * reader.AudioOutputConfig.format.sample_size]; sourceBufferPosition = 0; sourceBufferLength = -1; // -1 means buffer empty, >= 0 means valid buffer data // determine first PTS to handle cases where it is > 0 try { Position = 0; } catch (InvalidOperationException) { readerFirstPTS = readerPosition; readerPosition = 0; Console.WriteLine("first PTS = " + readerFirstPTS); } seekIndexCreated = false; }
/// <summary> /// Creates a Wave format proxy file in the same directory and with the same name as the specified file, /// if no storage directory is specified (i.e. if it is null). If a storage directory is specified, the proxy /// file will be stored in the specified directory with a hashed file name to avoid name collisions and /// file overwrites. The story directory option is convenient for the usage of temporary or working directories. /// </summary> /// <param name="fileInfo">the file for which a proxy file should be created</param> /// <param name="storageDirectory">optional directory where the proxy file will be stored, can be null</param> /// <returns>the FileInfo of the proxy file</returns> public static FileInfo CreateWaveProxy(FileInfo fileInfo, DirectoryInfo storageDirectory) { FileInfo outputFileInfo; if (storageDirectory == null) { // Without a storage directory, store the proxy file beside the original file outputFileInfo = new FileInfo(fileInfo.FullName + ".ffproxy.wav"); } else { // With a storage directory specified, store the proxy file with a hashed name // (to avoid name collision / overwrites) in the target directory (e.g. a temp or working directory) using (var sha256 = SHA256.Create()) { byte[] hash = sha256.ComputeHash(Encoding.Unicode.GetBytes(fileInfo.FullName)); string hashString = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); outputFileInfo = new FileInfo(Path.Combine(storageDirectory.FullName, hashString + ".ffproxy.wav")); } } if (outputFileInfo.Exists) { Console.WriteLine("Proxy already existing, using " + outputFileInfo.Name); return(outputFileInfo); } var reader = new FFmpegReader(fileInfo, FFmpeg.Type.Audio); var writer = new MemoryWriterStream(new AudioProperties( reader.AudioOutputConfig.format.channels, reader.AudioOutputConfig.format.sample_rate, reader.AudioOutputConfig.format.sample_size * 8, reader.AudioOutputConfig.format.sample_size == 4 ? AudioFormat.IEEE : AudioFormat.LPCM)); int output_buffer_size = reader.AudioOutputConfig.frame_size * writer.SampleBlockSize; byte[] output_buffer = new byte[output_buffer_size]; int samplesRead; long timestamp; FFmpeg.Type type; // sequentially read samples from decoder and write it to wav file while ((samplesRead = reader.ReadFrame(out timestamp, output_buffer, output_buffer_size, out type)) > 0) { int bytesRead = samplesRead * writer.SampleBlockSize; writer.Write(output_buffer, 0, bytesRead); } reader.Dispose(); writer.Position = 0; AudioStreamFactory.WriteToFile(writer, outputFileInfo.FullName); writer.Close(); return(outputFileInfo); }