You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于FFmpeg.AutoGen实现音频文件按时间段分割的示例请求

Using FFmpeg.AutoGen to Split Audio Files by Time Ranges

Great question! Using FFmpeg.AutoGen to split audio files by specific time ranges (and further into fixed-duration segments) is totally feasible—and efficient, since we can leverage stream copying to avoid re-encoding whenever possible. Below is a complete, commented implementation tailored to your needs, supporting MP3, OGG, WAV, and all other FFmpeg-compatible audio formats.

Prerequisites

  • Install the FFmpeg.AutoGen package via your NuGet package manager (search for FFmpeg.AutoGen).
  • Ensure FFmpeg binaries (like ffmpeg.dll, avcodec.dll) are present in your application's output directory. You can include them manually or use the FFmpeg.AutoGen.Binary NuGet package to bundle them automatically.
  • Initialize FFmpeg at the start of your application:
    FFmpegBinariesHelper.RegisterFFmpegBinaries();
    ffmpeg.av_register_all();
    ffmpeg.avformat_network_init();
    

Core Implementation

This method will take an input audio file, target a specific time window, and split that window into smaller segments of your desired duration:

using System;
using FFmpeg.AutoGen;

public static void SplitAudioSegments(
    string inputPath,
    string outputPrefix,
    TimeSpan overallStartTime,
    TimeSpan overallEndTime,
    TimeSpan segmentDuration)
{
    // Convert time values to FFmpeg's internal timestamp units (AV_TIME_BASE)
    long overallStartTs = (long)(overallStartTime.TotalSeconds * ffmpeg.AV_TIME_BASE);
    long overallEndTs = (long)(overallEndTime.TotalSeconds * ffmpeg.AV_TIME_BASE);
    long segmentDurationTs = (long)(segmentDuration.TotalSeconds * ffmpeg.AV_TIME_BASE);

    // Open input format context
    AVFormatContext* inputCtx = null;
    if (ffmpeg.avformat_open_input(&inputCtx, inputPath, null, null) != 0)
        throw new Exception("Failed to open input file");

    try
    {
        // Retrieve stream information
        if (ffmpeg.avformat_find_stream_info(inputCtx, null) < 0)
            throw new Exception("Failed to retrieve stream info");

        // Find the first audio stream
        int audioStreamIndex = -1;
        for (int i = 0; i < inputCtx->nb_streams; i++)
        {
            if (inputCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
            {
                audioStreamIndex = i;
                break;
            }
        }

        if (audioStreamIndex == -1)
            throw new Exception("No audio stream found in input file");

        AVStream* audioStream = inputCtx->streams[audioStreamIndex];
        long currentSegmentStartTs = overallStartTs;
        int segmentNumber = 1;

        // Iterate to create each segment
        while (currentSegmentStartTs < overallEndTs)
        {
            long currentSegmentEndTs = Math.Min(currentSegmentStartTs + segmentDurationTs, overallEndTs);
            string outputPath = $"{outputPrefix}{segmentNumber:D3}.{GetFileExtension(inputCtx)}";
            segmentNumber++;

            // Create output format context
            AVFormatContext* outputCtx = null;
            if (ffmpeg.avformat_alloc_output_context2(&outputCtx, null, null, outputPath) < 0)
                throw new Exception("Failed to create output context");

            try
            {
                // Create output audio stream
                AVStream* outputStream = ffmpeg.avformat_new_stream(outputCtx, null);
                if (outputStream == null)
                    throw new Exception("Failed to create output stream");

                // Copy codec parameters from input to output
                if (ffmpeg.avcodec_parameters_copy(outputStream->codecpar, audioStream->codecpar) < 0)
                    throw new Exception("Failed to copy codec parameters");

                // Open output file
                if ((outputCtx->oformat->flags & ffmpeg.AVFMT_NOFILE) == 0)
                {
                    if (ffmpeg.avio_open(&outputCtx->pb, outputPath, ffmpeg.AVIO_FLAG_WRITE) < 0)
                        throw new Exception($"Failed to open output file: {outputPath}");
                }

                // Write output header
                if (ffmpeg.avformat_write_header(outputCtx, null) < 0)
                    throw new Exception("Failed to write output header");

                // Seek to the start of the current segment
                if (ffmpeg.av_seek_frame(inputCtx, audioStreamIndex, currentSegmentStartTs, ffmpeg.AVSEEK_FLAG_BACKWARD) < 0)
                    throw new Exception("Failed to seek to segment start");

                AVPacket packet = new AVPacket();
                ffmpeg.av_init_packet(&packet);
                packet.data = null;
                packet.size = 0;

                try
                {
                    while (ffmpeg.av_read_frame(inputCtx, &packet) == 0)
                    {
                        if (packet.stream_index != audioStreamIndex)
                        {
                            ffmpeg.av_packet_unref(&packet);
                            continue;
                        }

                        // Convert packet timestamp to output stream time base
                        long packetTs = ffmpeg.av_rescale_q(packet.pts, audioStream->time_base, ffmpeg.AV_TIME_BASE_Q);

                        // Stop if we've passed the segment end time
                        if (packetTs > currentSegmentEndTs)
                        {
                            ffmpeg.av_packet_unref(&packet);
                            break;
                        }

                        // Only write packets that fall within the segment's time window
                        if (packetTs >= currentSegmentStartTs)
                        {
                            // Rescale timestamps for output stream
                            packet.pts = ffmpeg.av_rescale_q(packet.pts, audioStream->time_base, outputStream->time_base);
                            packet.dts = ffmpeg.av_rescale_q(packet.dts, audioStream->time_base, outputStream->time_base);
                            packet.duration = ffmpeg.av_rescale_q(packet.duration, audioStream->time_base, outputStream->time_base);
                            packet.stream_index = 0;

                            if (ffmpeg.av_interleaved_write_frame(outputCtx, &packet) < 0)
                                throw new Exception("Failed to write packet to output");
                        }

                        ffmpeg.av_packet_unref(&packet);
                    }
                }
                finally
                {
                    ffmpeg.av_packet_unref(&packet);
                }

                // Write output trailer
                ffmpeg.av_write_trailer(outputCtx);
            }
            finally
            {
                // Clean up output context
                if ((outputCtx->oformat->flags & ffmpeg.AVFMT_NOFILE) == 0 && outputCtx->pb != null)
                    ffmpeg.avio_close(outputCtx->pb);

                ffmpeg.avformat_free_context(outputCtx);
            }

            currentSegmentStartTs = currentSegmentEndTs;
        }
    }
    finally
    {
        // Clean up input context
        ffmpeg.avformat_close_input(&inputCtx);
    }
}

// Helper method to get the output file extension matching the input format
private static string GetFileExtension(AVFormatContext* ctx)
{
    return ctx->iformat->name switch
    {
        "mp3" => "mp3",
        "ogg" => "ogg",
        "wav" => "wav",
        _ => "m4a" // Fallback format if unknown
    };
}

Example Usage

To split the audio from 25:00 to 31:12 into 2-minute segments:

SplitAudioSegments(
    inputPath: "your_audio_file.mp3",
    outputPrefix: "audio_segment_",
    overallStartTime: TimeSpan.FromMinutes(25),
    overallEndTime: TimeSpan.FromMinutes(31) + TimeSpan.FromSeconds(12),
    segmentDuration: TimeSpan.FromMinutes(2)
);

This will generate files like audio_segment_001.mp3, audio_segment_002.mp3, etc., with the last segment being shorter if the remaining time is less than 2 minutes.

Key Notes

  • Stream Copy: This implementation uses stream copying (no re-encoding) which is fast and preserves audio quality. It works for formats where the codec is compatible between input and output (which is true for your target formats).
  • Accurate Seeking: The AVSEEK_FLAG_BACKWARD flag ensures we seek to the closest keyframe before the segment start, then discard early packets until we hit the exact start time for precision.
  • Error Handling: The code includes basic error handling—you can extend it with more specific exception messages or logging as needed.

内容的提问来源于stack exchange,提问作者Sven

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:59:29