2016-06-01 30 views
5

Ich verwende derzeit C# ruft die FFmpeg-APIs auf, um Video und Audio zu verarbeiten. Ich habe den folgenden Code, um das Audio aus einem Video zu extrahieren und in eine Datei zu schreiben.Erhöhen/Verringern der Lautstärke mit 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); 
} 

Dies funktioniert alles korrekt. Ich verwende derzeit das FFmpeg.AutoGen-Projekt für den API-Zugriff.

Ich möchte in der Lage sein, die Lautstärke des Audios zu erhöhen/zu verringern, bevor es in die Datei geschrieben wird, aber ich kann anscheinend keinen Befehl oder irgendeine Hilfe mit diesem finden. Muß es manuell gemacht werden?

Update 1:

Nach etwas Hilfe empfängt, ist dies die Klasse Layout habe ich:

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 
} 

Die Adjust() Funktion, nachdem ein Rahmen decodiert wird aufgerufen wird. Ich erhalte derzeit einen Fehler von -22, wenn av_buffersrc_add_frame() aufgerufen wird. Dies zeigt an, dass ein Parameter ungültig ist, aber nach dem Debuggen kann ich nichts sehen, was dies verursachen würde.

Dies ist, wie der Code aufgerufen wird:

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: es

rissig. Die Option "channel_layout" in der Filterargument-Zeichenfolge soll hexadezimal sein. Dies ist, was die Zeichenfolge Formatierung aussehen sollte:

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

Welche Wrapper tun Sie nutzen ? –

+0

Verwenden des FFmpeg.AutoGen-Projekts – williamtroup

+0

Sie müssen die Frames an den Audiofilter "volume" übergeben (wobei der Wörterbuchschlüssel volume auf den von Ihnen benötigten Wert gesetzt ist), ähnlich wie hier: https: // github. com/FFmpeg/FFmpeg/blob/Master/doc/Beispiele/filter_audio.c –

Antwort

1

Was hilft benötigen Sie einen Filtergraph zu tun, bauen und verarbeiten, um das Audio-Stream durch dieses Graphen. In Ihrem Fall ist die Grafik nur INPUT ("abuffer") -> VOLUME -> OUTPUT ("abuffersink"). Hier ist eine Beispielkonsole App, die das demonstriert. Es basiert lose auf ffmpeg Proben filtering_audio, filter_audio und remuxing.

Sie können es wie folgt verwenden:

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

Und hier ist der Code:

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

Es ein Dienstprogramm verwendet InteropHelper Klasse von AutoGen des abgeleitet:

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

Vielen Dank für Ihre Antwort. av_buffersrc_add_frame() gibt immer -22 zurück, wenn ich diesen Code in meinem benutze. Kann nicht verstehen, warum, ich habe im Wesentlichen alle von Ihnen für die Sicherheitsmaßnahme kopiert. – williamtroup

+0

-22 ist ungültiger Parameter. Hast du meinen ganzen Code ausprobiert, wie es bei einer bestehenden Datei ist? –

+0

Ja, Ihr Code ist in Ordnung. Ich habe Audio-Decodierung und Konvertierung (dann schreiben) arbeiten für Ewigkeiten. Für die Argumente für "Argumente" werden sie in der Eingabedatei AvCodecContext aufgebaut, die die gleiche wie deine ist, also nicht ganz sicher, was passiert. – williamtroup

0

Ich weiß nicht, was API Sie verwenden, aber ffmpeg hat einen Befehl, der Audio-zu erhöhen oder verringern erlaubt:

Abnahme auf die Hälfte:

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

Zunahme von 50%:

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

oder in dB:

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

Hofft es Ihnen

+0

Nein, ich habe Angst, ich möchte nicht verwenden Konsole. – williamtroup