Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Samples count is always zero in write callback for Ogg FLAC #661

Open
njlarsson opened this issue Dec 18, 2023 · 0 comments
Open

Samples count is always zero in write callback for Ogg FLAC #661

njlarsson opened this issue Dec 18, 2023 · 0 comments

Comments

@njlarsson
Copy link

njlarsson commented Dec 18, 2023

The documentation of FLAC__StreamEncoderWriteCallback says “Unlike when writing to native FLAC, when writing to Ogg FLAC the write callback will be called twice when writing each audio frame; once for the page header, and once for the page body. When writing the page header, the samples argument to the write callback will be 0.” Unfortunately, it appears that the samples argument is always 0 with Ogg FLAC.

The code below demonstrates the problem, encoding a buffer of 100 zero samples with native and Ogg FLAC. It prints (where both should be 100):

Total samples output without ogg: 100
Total samples output with ogg: 0

I tested it with the same results with FLAC 1.4.3 installed via Homebrew in macOS, and with 1.3.3 on Ubuntu (1.3.3-2ubuntu0.2, the version that standard package management on Ubuntu 22.04.3 LTS supplies for package libflac-dev).

This breaks the use of Ogg FLAC in my application, since I need to determine the number of samples actually output in order not to flood the recipient of the stream. (The number of output samples is sometimes much smaller than the number of processed samples for highly compressible audio.)

#include <stdlib.h>
#include <stdio.h>
#include "FLAC/stream_encoder.h"

long outsamples;

static FLAC__StreamEncoderWriteStatus
write_cb(const FLAC__StreamEncoder *encoder,
         const FLAC__byte buffer[],
         size_t bytes,
         uint32_t samples,
         uint32_t current_frame,
         void *handle)
{
    outsamples += samples;
    return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
}

int main(int argc, char *argv[]) {
    const int samples = 100;
    FLAC__int32 **buffer;
    buffer = malloc(2 * sizeof(FLAC__int32 *));
    if (buffer == NULL) {
        fprintf(stderr, "Failed to allocate buffer");
        exit(1);
    }
    for (int c = 0; c < 2; c++) {
        if ((buffer[c] = malloc(samples * sizeof **buffer)) == NULL) {
            fprintf(stderr, "Failed to allocate buffer");
            exit(1);
        }
        for (int i = 0; i < samples; i++) {
            buffer[c][i] = 0;
        }
    }

    for (int ogg = 0; ogg < 2; ogg++) {
        FLAC__StreamEncoder *encoder;
        if ((encoder = FLAC__stream_encoder_new()) == NULL) {
            fprintf(stderr, "Failed to create FLAC encoder");
            exit(1);
        }
        if (!(FLAC__stream_encoder_set_streamable_subset(encoder, true) &&
              FLAC__stream_encoder_set_compression_level(encoder, 5) &&
              FLAC__stream_encoder_set_channels(encoder, 2) &&
              FLAC__stream_encoder_set_bits_per_sample(encoder, 16) &&
              FLAC__stream_encoder_set_sample_rate(encoder, 44100))) {
            fprintf(stderr, "Failed to set encoder parameters\n");
            exit(1);
        }
        
        FLAC__StreamEncoderInitStatus status = ogg ?
            FLAC__stream_encoder_init_ogg_stream(encoder, NULL, write_cb, NULL, NULL, NULL, NULL) :
            FLAC__stream_encoder_init_stream(encoder, write_cb, NULL, NULL, NULL, NULL);
        if (status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
            fprintf(stderr, "Failed to initialize encoder: %s\n", FLAC__StreamEncoderInitStatusString[status]);
            exit(1);
        }

        outsamples = 0;
        if (!FLAC__stream_encoder_process(encoder, (const FLAC__int32 *const *) buffer, samples)) {
            fprintf(stderr, "Process failed: %s\n", FLAC__StreamEncoderStateString[FLAC__stream_encoder_get_state(encoder)]);
            exit(1);
        }
        if (!FLAC__stream_encoder_finish(encoder)) {
            fprintf(stderr, "Finish failed: %s\n", FLAC__StreamEncoderStateString[FLAC__stream_encoder_get_state(encoder)]);
            exit(1);
        }
        fprintf(stdout, "Total samples output %s ogg: %ld\n", ogg ? "with" : "without", outsamples);
        FLAC__stream_encoder_delete(encoder);
    }
    
    return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant