2016-06-01 2 views
5

В настоящее время я использую C# для вызова API FFmpeg для обработки видео и аудио. У меня есть следующий код, чтобы извлечь аудио из видео и записать его в файл.Увеличение/уменьшение громкости звука с помощью FFmpeg

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 < 0 && bufferSinkGetFrameResult != -EAGAIN) 
      { 
      } 
     } 

     return returnFilteredFrame; 
    } 

    public void Dispose() 
    { 
     Cleanup(m_filterGraph); 
    } 

    #region Private Properties 

    private AVCodecContext* CodecContext { get; set; } 
    private AVStream* Stream { get; set; } 
    private float Volume { get; set; } 

    #endregion 

    #region Private Setup Helper Functions 

    private void Initialise() 
    { 
     m_filterGraph = GetAllocatedFilterGraph(); 

     string aBufferFilterArguments = string.Format("sample_fmt={0}:channel_layout={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); 

     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() вызывается после того, как кадр декодируется. В настоящее время я получаю ошибку -22 при вызове av_buffersrc_add_frame(). Это означает, что параметр недопустим, но после отладки я не вижу ничего, что могло бы вызвать это.

Это как называется код:

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); 
} 

Update 2:

Трещины его. Параметр «channel_layout» в строке аргумента фильтра должен быть шестнадцатеричным. Это то, что строка форматирования должен выглядеть следующим образом:

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); 
+0

Какой обертка сделать ты используешь ? –

+0

Использование проекта FFmpeg.AutoGen – williamtroup

+0

Вам необходимо передать кадры на «громкий» звуковой фильтр (с ключом словаря тома, установленным на нужное значение), что-то похожее на то, что сделано здесь: https: // github. com/FFmpeg/FFmpeg/blob/master/doc/examples/filter_audio.c –

ответ

1

Что вам нужно сделать, это построить граф фильтров и обработки звукового потока через этот график. В вашем случае график - это просто 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 <volume ratio>"); 
      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; 
    } 
} 

Он использует утилиту InteropHelper класс, производный от AutoGen'S:

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); 
     } 
    } 
} 
+0

Благодарим вас за ответ. av_buffersrc_add_frame() продолжает возвращать -22, когда я использую этот код в моей. Не могу понять, почему, я по существу скопировал все ваши меры безопасности. – williamtroup

+0

-22 - недопустимый параметр. вы пробовали мой весь код, как есть, с существующим файлом? –

+0

Да, ваш код в порядке. У меня было декодирование и конвертация аудио (затем запись), работающая целую вечность. Для аргументов для «args» они создаются с использованием во входном файле AvCodecContext, который является таким же, как ваш, поэтому не совсем уверен, что происходит. – williamtroup

0

Я не знаю, что API вы используете, но FFmpeg имеет команду, которая позволяет увеличить или уменьшить звук:

Снижение до половины:

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

Увеличение 50%:

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

или в дБ:

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

Надеется это помогает вам

+0

Нет смысла боюсь, я не хочу использовать консоль. – williamtroup

Смежные вопросы