Ensuring Precise 1-Second Audio File Splitting in GStreamer

Hello,

I am trying to create an audio pipeline that precisely splits audio into raw aac 1-second files. Here is my current GStreamer pipeline:

gst-launch-1.0 -e audiotestsrc wave=4 is-live=true ! queue ! audio/x-raw, format=S16LE, layout=interleaved, rate=44100, channels=2 ! voaacenc ! aacparse ! audio/mpeg, mpegversion=4, stream-format=adts ! multifilesink location=a_temp%06d.aac next-file="max-duration" max-file-duration=1000000000

After examining the generated files, I noticed that they are not exactly 1 second long. Running ffprobe on one of the files shows that the duration is approximately 1.02 seconds instead of precisely 1 second.

ffprobe a_temp000002.aac

ls -al --time-style=full-iso

Issue

Despite setting max-file-duration=1000000000 (1 second), the files exceed this duration.

Question

How can I modify my pipeline to ensure each file is exactly 1.00 seconds long?
Any insights or suggestions would be greatly appreciated!

AAC will (typically) encode frames of 1024 samples, so your chunk size will be a multiple of 1024 samples.

In your case it’s probably 44 frames at 1024 samples = 45056 samples, divided by 44100 samples per second = 1.021678005s

I think the only way to get exactly 1 second chunks is to find an AAC encoder that encodes 960 samples per frame in combination with a 48kHz sample rate.

I don’t think any of the libraries/encoders available in GStreamer can support that out of the box though.

1 Like

Hello,
@tpm Thank you for your insightful response. You are absolutely right.

After discussing with my team, we decided to implement this logic accordingly. I have rewritten the pipeline in Python and added a probe function connected to the last multifilesink element:

aac_multifilesink_sink_pad = aac_sink.get_static_pad("sink")
aac_multifilesink_sink_pad.add_probe(
            Gst.PadProbeType.BUFFER,
            audio_time_test_probe_function,
            logger,
            aac_sink
        )

total_packets = 0
frame_number = 0
previous_timestamp = 0

def audio_time_test_probe_function(pad: Gst.Pad, info: Gst.PadProbeInfo, logger: logging.Logger, aac_sink: Gst.Element) -> Gst.PadProbeReturn:
    global frame_number, previous_timestamp, total_packets

    gst_buffer = info.get_buffer()
    if not gst_buffer:
        logger.error("Unable to get GstBuffer for audio probe function")
        return Gst.PadProbeReturn.OK
    
    timestamp = gst_buffer.pts
    frame_stamp = 1 / (44100 / 1024) * 1000000000
    total_packets += 1

    if timestamp + frame_stamp > (frame_number + 1) * 1000000000:
        timeastmp_diff = timestamp - previous_timestamp
        logger.info(f"Saving file {frame_number} with timestamp {timestamp} | {timeastmp_diff} | {total_packets}")
        
        frame_number += 1
        previous_timestamp = timestamp
        total_packets = 0

    return Gst.PadProbeReturn.OK

I would like to trigger file saving inside the if statement. Given that the multifilesink element (aac_sink) is passed as a parameter to the function, how can I manually trigger file saving on multifilesink? I also thought about using splitmuxsink and running emit("split-now") however because of the mp4mux inside the splitmuxsink, it does not return raw aac files, but encapsulates them in mp4.