使用FFmpeg增加/减少音量

我目前正在使用C#调用来调用FFmpeg API来处理video和音频。 我有以下代码从video中提取音频并将其写入文件。

while (ffmpeg.av_read_frame(formatContext, &packet) >= 0) { if (packet.stream_index == streamIndex) { while (packet.size > 0) { int frameDecoded; int frameDecodedResult = ffmpeg.avcodec_decode_audio4(codecContext, frame, &frameDecoded, packet); if (frameDecoded > 0 && frameDecodedResult >= 0) { //writeAudio.WriteFrame(frame); packet.data += totalBytesDecoded; packet.size -= totalBytesDecoded; } } frameIndex++; } Avcodec.av_free_packet(&packet); } 

这一切都正常。 我目前正在使用FFmpeg.AutoGen项目进行API访问。

我希望能够在写入文件之前增加/减少音频的音量,但我似乎无法找到命令或任何帮助。 是否必须手动完成?

更新1:

收到一些帮助后,这是我的课程布局:

 public unsafe class FilterVolume { #region Private Member Variables private AVFilterGraph* m_filterGraph = null; private AVFilterContext* m_aBufferSourceFilterContext = null; private AVFilterContext* m_aBufferSinkFilterContext = null; #endregion #region Private Constant Member Variables private const int EAGAIN = 11; #endregion public FilterVolume(AVCodecContext* codecContext, AVStream* stream, float volume) { CodecContext = codecContext; Stream = stream; Volume = volume; Initialise(); } public AVFrame* Adjust(AVFrame* frame) { AVFrame* returnFilteredFrame = ffmpeg.av_frame_alloc(); if (m_aBufferSourceFilterContext != null && m_aBufferSinkFilterContext != null) { int bufferSourceAddFrameResult = ffmpeg.av_buffersrc_add_frame(m_aBufferSourceFilterContext, frame); if (bufferSourceAddFrameResult < 0) { } int bufferSinkGetFrameResult = ffmpeg.av_buffersink_get_frame(m_aBufferSinkFilterContext, returnFilteredFrame); if (bufferSinkGetFrameResult sample_fmt, CodecContext->channel_layout, CodecContext->sample_rate, Stream->time_base.num, Stream->time_base.den); AVFilterContext* aBufferSourceFilterContext = CreateFilter("abuffer", m_filterGraph, aBufferFilterArguments); AVFilterContext* volumeFilterContext = CreateFilter("volume", m_filterGraph, string.Format("volume={0}", Volume)); AVFilterContext* aBufferSinkFilterContext = CreateFilter("abuffersink", m_filterGraph); LinkFilter(aBufferSourceFilterContext, volumeFilterContext); LinkFilter(volumeFilterContext, aBufferSinkFilterContext); SetFilterGraphConfiguration(m_filterGraph, null); m_aBufferSourceFilterContext = aBufferSourceFilterContext; m_aBufferSinkFilterContext = aBufferSinkFilterContext; } #endregion #region Private Cleanup Helper Functions private static void Cleanup(AVFilterGraph* filterGraph) { if (filterGraph != null) { ffmpeg.avfilter_graph_free(&filterGraph); } } #endregion #region Provate Helpers private AVFilterGraph* GetAllocatedFilterGraph() { AVFilterGraph* filterGraph = ffmpeg.avfilter_graph_alloc(); if (filterGraph == null) { } return filterGraph; } private AVFilter* GetFilterByName(string name) { AVFilter* filter = ffmpeg.avfilter_get_by_name(name); if (filter == null) { } return filter; } private void SetFilterGraphConfiguration(AVFilterGraph* filterGraph, void* logContext) { int filterGraphConfigResult = ffmpeg.avfilter_graph_config(filterGraph, logContext); if (filterGraphConfigResult < 0) { } } private AVFilterContext* CreateFilter(string filterName, AVFilterGraph* filterGraph, string filterArguments = null) { AVFilter* filter = GetFilterByName(filterName); AVFilterContext* filterContext; int aBufferFilterCreateResult = ffmpeg.avfilter_graph_create_filter(&filterContext, filter, filterName, filterArguments, null, filterGraph); if (aBufferFilterCreateResult < 0) { } return filterContext; } private void LinkFilter(AVFilterContext* source, AVFilterContext* destination) { int filterLinkResult = ffmpeg.avfilter_link(source, 0, destination, 0); if (filterLinkResult < 0) { } } #endregion } 

在解码帧之后调用Adjust()函数。 调用av_buffersrc_add_frame()时,我当前收到-22错误。 这表示参数无效,但在调试后,我看不到任何可能导致此问题的参数。

这是代码的调用方式:

 while (ffmpeg.av_read_frame(formatContext, &packet) >= 0) { if (packet.stream_index == streamIndex) { while (packet.size > 0) { int frameDecoded; int frameDecodedResult = ffmpeg.avcodec_decode_audio4(codecContext, frame, &frameDecoded, packet); if (frameDecoded > 0 && frameDecodedResult >= 0) { AVFrame* filteredFrame = m_filterVolume.Adjust(frame); //writeAudio.WriteFrame(filteredFrame); packet.data += totalBytesDecoded; packet.size -= totalBytesDecoded; } } frameIndex++; } Avcodec.av_free_packet(&packet); } 

更新2:

破了。 filter参数字符串中的“channel_layout”选项应该是hex的。 这就是字符串格式应该是这样的:

 string aBufferFilterArguments = string.Format("sample_fmt={0}:channel_layout=0x{1}:sample_rate={2}:time_base={3}/{4}", (int)CodecContext->sample_fmt, CodecContext->channel_layout, CodecContext->sample_rate, Stream->time_base.num, Stream->time_base.den); 

您需要做的是构建filter图并通过该图处理音频流。 在您的情况下,图形只是INPUT(“abuffer”) – > VOLUME – > OUTPUT(“abuffersink”)。 这是一个示例控制台应用程序,用于演示。 它基于ffmpeg样本filtering_audio , filter_audio和remuxing 。

你可以像这样使用它:

 ChangeVolume.exe http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4 bunny_half.mp4 0.5 

这是代码:

 class Program { static unsafe void Main(string[] args) { Console.WriteLine(@"Current directory: " + Environment.CurrentDirectory); Console.WriteLine(@"Running in {0}-bit mode.", Environment.Is64BitProcess ? @"64" : @"32"); // adapt this to your context var ffmpegPath = string.Format(@"../../../FFmpeg/bin/{0}", Environment.Is64BitProcess ? @"x64" : @"x86"); InteropHelper.SetDllDirectory(ffmpegPath); int ret, i; if (args.Length < 3) { Console.WriteLine("usage: ChangeVolume input output "); return; } string in_filename = args[0]; string out_filename = args[1]; double ratio = double.Parse(args[2]); ffmpeg.av_register_all(); ffmpeg.avfilter_register_all(); // open input file AVFormatContext* ifmt_ctx = null; InteropHelper.Check(ffmpeg.avformat_open_input(&ifmt_ctx, in_filename, null, null)); // dump input ffmpeg.av_dump_format(ifmt_ctx, 0, in_filename, 0); // get streams info to determine audio stream index InteropHelper.Check(ffmpeg.avformat_find_stream_info(ifmt_ctx, null)); // determine input decoder AVCodec* dec; int audio_stream_index = ffmpeg.av_find_best_stream(ifmt_ctx, AVMediaType.AVMEDIA_TYPE_AUDIO, -1, -1, &dec, 0); AVCodecContext* dec_ctx = ifmt_ctx->streams[audio_stream_index]->codec; // open input decoder InteropHelper.Check(ffmpeg.avcodec_open2(dec_ctx, dec, null)); // build a filter graph AVFilterContext* buffersrc_ctx; AVFilterContext* buffersink_ctx; AVFilterGraph* filter_graph = init_filter_graph(ifmt_ctx, dec_ctx, audio_stream_index, &buffersrc_ctx, &buffersink_ctx, ratio); // prepare output AVFormatContext* ofmt_ctx = null; InteropHelper.Check(ffmpeg.avformat_alloc_output_context2(&ofmt_ctx, null, null, out_filename)); InteropHelper.Check(ofmt_ctx); // create output streams AVCodecContext* enc_ctx = null; ofmt_ctx->oformat->flags |= InteropHelper.AVFMT_NOTIMESTAMPS; for (i = 0; i < ifmt_ctx->nb_streams; i++) { AVStream* in_stream = ifmt_ctx->streams[i]; if (in_stream->codec->codec_type == AVMediaType.AVMEDIA_TYPE_DATA) // skip these continue; AVStream* out_stream = ffmpeg.avformat_new_stream(ofmt_ctx, in_stream->codec->codec); InteropHelper.Check(out_stream); InteropHelper.Check(ffmpeg.avcodec_copy_context(out_stream->codec, in_stream->codec)); out_stream->codec->codec_tag = 0; if ((ofmt_ctx->oformat->flags & InteropHelper.AVFMT_GLOBALHEADER) != 0) { out_stream->codec->flags |= InteropHelper.AV_CODEC_FLAG_GLOBAL_HEADER; } if (i == audio_stream_index) { // create audio encoder from audio decoder AVCodec* enc = ffmpeg.avcodec_find_encoder(dec_ctx->codec_id); InteropHelper.Check(enc); enc_ctx = ffmpeg.avcodec_alloc_context3(enc); InteropHelper.Check(enc_ctx); enc_ctx->sample_rate = dec_ctx->sample_rate; enc_ctx->channel_layout = dec_ctx->channel_layout; enc_ctx->channels = ffmpeg.av_get_channel_layout_nb_channels(enc_ctx->channel_layout); enc_ctx->sample_fmt = enc->sample_fmts[0]; enc_ctx->time_base.num = 1; enc_ctx->time_base.den = enc_ctx->sample_rate; InteropHelper.Check(ffmpeg.avcodec_open2(enc_ctx, enc, null)); } } // dump output ffmpeg.av_dump_format(ofmt_ctx, 0, out_filename, 1); if ((ofmt_ctx->oformat->flags & InteropHelper.AVFMT_NOFILE) == 0) { // open output file InteropHelper.Check(ffmpeg.avio_open(&ofmt_ctx->pb, out_filename, InteropHelper.AVIO_FLAG_WRITE)); } // write output file header InteropHelper.Check(ffmpeg.avformat_write_header(ofmt_ctx, null)); // read all packets and process AVFrame* frame = ffmpeg.av_frame_alloc(); AVFrame* filt_frame = ffmpeg.av_frame_alloc(); while (true) { AVStream* in_stream; AVStream* out_stream; AVPacket pkt; ret = ffmpeg.av_read_frame(ifmt_ctx, &pkt); if (ret < 0) break; in_stream = ifmt_ctx->streams[pkt.stream_index]; if (in_stream->codec->codec_type == AVMediaType.AVMEDIA_TYPE_DATA) continue; // audio stream? we need to pass it through our filter graph if (pkt.stream_index == audio_stream_index) { // decode audio (packet -> frame) int got_frame = 0; InteropHelper.Check(ffmpeg.avcodec_decode_audio4(dec_ctx, frame, &got_frame, &pkt)); if (got_frame > 0) { // add the frame into the filter graph InteropHelper.Check(ffmpeg.av_buffersrc_add_frame(buffersrc_ctx, frame)); while (true) { // get the frame out from the filter graph ret = ffmpeg.av_buffersink_get_frame(buffersink_ctx, filt_frame); const int EAGAIN = 11; if (ret == -EAGAIN) break; InteropHelper.Check(ret); // encode audio (frame -> packet) AVPacket enc_pkt = new AVPacket(); int got_packet = 0; InteropHelper.Check(ffmpeg.avcodec_encode_audio2(enc_ctx, &enc_pkt, filt_frame, &got_packet)); enc_pkt.stream_index = pkt.stream_index; InteropHelper.Check(ffmpeg.av_interleaved_write_frame(ofmt_ctx, &enc_pkt)); ffmpeg.av_frame_unref(filt_frame); } } } else { // write other (video) streams out_stream = ofmt_ctx->streams[pkt.stream_index]; pkt.pts = ffmpeg.av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, AVRounding.AV_ROUND_NEAR_INF | AVRounding.AV_ROUND_PASS_MINMAX); pkt.dts = ffmpeg.av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, AVRounding.AV_ROUND_NEAR_INF | AVRounding.AV_ROUND_PASS_MINMAX); pkt.duration = ffmpeg.av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base); pkt.pos = -1; InteropHelper.Check(ffmpeg.av_interleaved_write_frame(ofmt_ctx, &pkt)); } ffmpeg.av_packet_unref(&pkt); } // write trailer, close file ffmpeg.av_write_trailer(ofmt_ctx); ffmpeg.avformat_close_input(&ifmt_ctx); if ((ofmt_ctx->oformat->flags & InteropHelper.AVFMT_NOFILE) == 0) { ffmpeg.avio_closep(&ofmt_ctx->pb); } ffmpeg.avformat_free_context(ofmt_ctx); ffmpeg.av_frame_free(&filt_frame); ffmpeg.av_frame_free(&frame); ffmpeg.avfilter_graph_free(&filter_graph); return; } static unsafe AVFilterGraph* init_filter_graph(AVFormatContext* format, AVCodecContext* codec, int audio_stream_index, AVFilterContext** buffersrc_ctx, AVFilterContext** buffersink_ctx, double volumeRatio) { // create graph var filter_graph = ffmpeg.avfilter_graph_alloc(); InteropHelper.Check(filter_graph); // add input filter var abuffersrc = ffmpeg.avfilter_get_by_name("abuffer"); if (abuffersrc == null) InteropHelper.CheckTag("\x00F8FIL"); string args = string.Format("sample_fmt={0}:channel_layout={1}:sample_rate={2}:time_base={3}/{4}", (int)codec->sample_fmt, codec->channel_layout, codec->sample_rate, format->streams[audio_stream_index]->time_base.num, format->streams[audio_stream_index]->time_base.den); InteropHelper.Check(ffmpeg.avfilter_graph_create_filter(buffersrc_ctx, abuffersrc, "IN", args, null, filter_graph)); // add volume filter var volume = ffmpeg.avfilter_get_by_name("volume"); if (volume == null) InteropHelper.CheckTag("\x00F8FIL"); AVFilterContext* volume_ctx; InteropHelper.Check(ffmpeg.avfilter_graph_create_filter(&volume_ctx, volume, "VOL", "volume=" + volumeRatio.ToString(CultureInfo.InvariantCulture), null, filter_graph)); // add output filter var abuffersink = ffmpeg.avfilter_get_by_name("abuffersink"); if (abuffersink == null) InteropHelper.CheckTag("\x00F8FIL"); InteropHelper.Check(ffmpeg.avfilter_graph_create_filter(buffersink_ctx, abuffersink, "OUT", "", null, filter_graph)); // connect input -> volume -> output InteropHelper.Check(ffmpeg.avfilter_link(*buffersrc_ctx, 0, volume_ctx, 0)); InteropHelper.Check(ffmpeg.avfilter_link(volume_ctx, 0, *buffersink_ctx, 0)); InteropHelper.Check(ffmpeg.avfilter_graph_config(filter_graph, null)); return filter_graph; } } 

它使用从AutoGen派生的实用程序InteropHelper类:

 public class InteropHelper { [DllImport("kernel32", SetLastError = true)] public static extern bool SetDllDirectory(string lpPathName); public static readonly int AVERROR_EOF = -GetTag("EOF "); public static readonly int AVERROR_UNKNOWN = -GetTag("UNKN"); public static readonly int AVFMT_GLOBALHEADER = 0x0040; public static readonly int AVFMT_NOFILE = 0x0001; public static readonly int AVIO_FLAG_WRITE = 2; public static readonly int AV_CODEC_FLAG_GLOBAL_HEADER = (1 << 22); public static readonly int AV_ROUND_ZERO = 0; public static readonly int AV_ROUND_INF = 1; public static readonly int AV_ROUND_DOWN = 2; public static readonly int AV_ROUND_UP = 3; public static readonly int AV_ROUND_PASS_MINMAX = 8192; public static readonly int AV_ROUND_NEAR_INF = 5; public static readonly int AVFMT_NOTIMESTAMPS = 0x0080; public static unsafe void Check(void* ptr) { if (ptr != null) return; const int ENOMEM = 12; Check(-ENOMEM); } public static unsafe void Check(IntPtr ptr) { if (ptr != IntPtr.Zero) return; Check((void*)null); } // example: "\x00F8FIL" is "Filter not found" (check libavutil/error.h) public static void CheckTag(string tag) { Check(-GetTag(tag)); } public static int GetTag(string tag) { var bytes = new byte[4]; for (int i = 0; i < 4; i++) { bytes[i] = (byte)tag[i]; } return BitConverter.ToInt32(bytes, 0); } public static void Check(int res) { if (res >= 0) return; string err = "ffmpeg error " + res; string text = GetErrorText(res); if (!string.IsNullOrWhiteSpace(text)) { err += ": " + text; } throw new Exception(err); } public static string GetErrorText(int res) { IntPtr err = Marshal.AllocHGlobal(256); try { ffmpeg.av_strerror(res, err, 256); return Marshal.PtrToStringAnsi(err); } finally { Marshal.FreeHGlobal(err); } } } 

我不知道你在使用什么API,但ffmpeg有一个允许增加或减少音频的命令:

减少一半:

 ffmpeg -i input.wav -af "volume=0.5" output.wav 

增加50%:

 ffmpeg -i input.wav -af "volume=1.5" output.wav 

或以dB为单位:

 ffmpeg -i input.wav -af "volume=10dB" output.wav 

希望它能帮到你