libavformat - Using custom writing callbacks

126 views Asked by At

I'm trying to use the callbacks in FFMpeg to use a custom output for my files (cipher the output as it is written). Using the custom callback for read works fine, but I have problems with the one used for writing.

When I write a file, I have my file written but no calls to my write callback.

Here is a minimal version of my code (the callbacks are just to check if they are called here in the code they don“t work as intended).

#include <libavcodec/avcodec.h>
#include <libavcodec/bsf.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>

#define DEFAULT_PAGE_CACHE_SIZE 4096

struct file_ffmpeg {
    AVFormatContext* fmt_ctx;
    AVPacket*        packet;

    struct {
        AVStream*     stream;
        AVCodecContext* codec_ctx;
        const AVCodec*  codec;
    } v;
    struct {
        AVStream*   stream;
        AVCodecContext* codec_ctx;
        const AVCodec* codec;
    } a;
    bool             is_open;
    bool             header_written;
    bool             eof;

    struct {
        unsigned char*                  context_buffer;
        void*                           file_stream;
    } internal;
};

void file_ffmpeg_destroy(struct file_ffmpeg* file_ffmpeg) {
    if (file_ffmpeg->a.codec_ctx) {
        avcodec_free_context(&file_ffmpeg->a.codec_ctx);
    }

    if (file_ffmpeg->v.codec_ctx) {
        avcodec_free_context(&file_ffmpeg->v.codec_ctx);
    }

    av_packet_free(&file_ffmpeg->packet);
    av_free(file_ffmpeg->internal.context_buffer);

    free(file_ffmpeg);
}

static int ffmpeg_read_cb(void* opaque, uint8_t* buffer, int buf_size);
static int ffmpeg_write_cb(void* opaque, uint8_t* buffer, int buf_size);
static int64_t ffmpeg_seek_cb(void* opaque, int64_t offset, int whence);

bool file_ffmpeg_init_cbs(struct file_ffmpeg* file_ffmpeg, bool is_writter, const char* filename) {

    if (is_writter) {
        int err = avformat_alloc_output_context2(&file_ffmpeg->fmt_ctx, NULL, NULL, filename);
        if (err < 0) {
            char      error_str[AV_ERROR_MAX_STRING_SIZE] = {0};
            av_strerror(err, error_str, AV_ERROR_MAX_STRING_SIZE);
            printf("Could not deduce output format from file name '%s': %s", filename, error_str);
            return false;
        }
    } else {
        file_ffmpeg->fmt_ctx = avformat_alloc_context();
    }

    if (!file_ffmpeg->fmt_ctx) {
        printf("Could not allocate avformatcontext for %s", is_writter ? "write" : "read");
        return false;
    }

    file_ffmpeg->internal.context_buffer = (unsigned char*)av_malloc(DEFAULT_PAGE_CACHE_SIZE);
    if (!file_ffmpeg->internal.context_buffer) {
        printf("Could not allocate context buffer for %s", is_writter ? "write" : "read");
        return false;
    }

    AVIOContext* avio = NULL;
    if (!is_writter) {
        avio = avio_alloc_context(file_ffmpeg->internal.context_buffer, DEFAULT_PAGE_CACHE_SIZE, 0, file_ffmpeg, &ffmpeg_read_cb, NULL,
                                  &ffmpeg_seek_cb);
    } else {
        avio = avio_alloc_context(file_ffmpeg->internal.context_buffer, DEFAULT_PAGE_CACHE_SIZE, 1, file_ffmpeg, NULL, &ffmpeg_write_cb,
                                  &ffmpeg_seek_cb);
    }

    if (!avio) {
        printf("Failed to allocate custom IO for %s", is_writter ? "write" : "read");
        return false;
    }

    file_ffmpeg->fmt_ctx->pb = avio;
    return true;
}

int main(int ac, char **av)
{
    const char *in = av[0];
    const char *output = av[1];
    printf("FFMPEG Version: %x: %s\n", avcodec_version(), avcodec_configuration());

    struct file_ffmpeg* ofile_ffmpeg = (struct file_ffmpeg*)malloc(sizeof(struct file_ffmpeg));

    if (!ofile_ffmpeg) {
        printf("Context malloc failed");
        return -1;
    }

    if (!file_ffmpeg_init_cbs(ofile_ffmpeg, true, output)) {
        file_ffmpeg_destroy(ofile_ffmpeg);
        return -1;
    }

    ofile_ffmpeg->a.codec = avcodec_find_encoder(AV_CODEC_ID_MP3);
    if(!ofile_ffmpeg->a.codec) {
        printf("Error: No codec matching 'mp3'. Check ffmpeg deps and configure args\n");
        file_ffmpeg_destroy(ofile_ffmpeg);
        return -1;
    }

    ofile_ffmpeg->a.stream = avformat_new_stream(ofile_ffmpeg->fmt_ctx, ofile_ffmpeg->a.codec);
    if (!ofile_ffmpeg->a.stream) {
        printf("Failed allocating output stream");
        file_ffmpeg_destroy(ofile_ffmpeg);
        return -1;
    }

    int err = 0;
    /* open the output file, if needed */
    if (!(ofile_ffmpeg->fmt_ctx->oformat->flags & AVFMT_NOFILE)) {
        err = avio_open(&ofile_ffmpeg->fmt_ctx->pb, output, AVIO_FLAG_WRITE);
        if (err < 0) {
            char      error_str[AV_ERROR_MAX_STRING_SIZE] = {0};
            av_strerror(err, error_str, AV_ERROR_MAX_STRING_SIZE);
            printf("Could not open output file '%s': %s", output, error_str);
            return -1;
        }
        printf("Opened output file '%s'", output);
    }

    //<... transcode some audio data ...>

    if (!ofile_ffmpeg->header_written) {
        err = avformat_write_header(ofile_ffmpeg->fmt_ctx, NULL);
        if (err < 0) {
            char      error_str[AV_ERROR_MAX_STRING_SIZE] = {0};
            printf("Error occurred when writing header: %s", av_make_error_string(error_str, sizeof(error_str), err));
            return -1;
        }
    }

    //<... create some audio packet ...>
    ofile_ffmpeg->packet = av_packet_alloc();
    if (!ofile_ffmpeg->packet) {
        printf("could not allocate audio packet");
        return -1;
    }

    // Write frame into file
    err = av_interleaved_write_frame(ofile_ffmpeg->fmt_ctx, ofile_ffmpeg->packet);
    if (err < 0) {
        char      error_str[AV_ERROR_MAX_STRING_SIZE] = {0};
        printf("Error occurred when writing frame: %s", av_make_error_string(error_str, sizeof(error_str), err));
        return -1;
    }

    return 0;
}

//// CALLBACKS ////

static int ffmpeg_read_cb(void* opaque, uint8_t* buffer, int buf_size) {
    struct file_ffmpeg* ifile_ffmpeg = (struct file_ffmpeg*)opaque;
    printf("callback called by ffmpeg (read)\n");
    return buf_size;
}

static int ffmpeg_write_cb(void* opaque, uint8_t* buffer, int buf_size) {
    struct file_ffmpeg* ofile_ffmpeg = (struct file_ffmpeg*)opaque;
    printf("callback called by ffmpeg (write)\n");
    return buf_size;
}

static int64_t ffmpeg_seek_cb(void* opaque, int64_t offset, int whence) {
    struct file_ffmpeg* sfile_ffmpeg = (struct file_ffmpeg*)opaque;
    printf("Callback seek call");
    return offset;
}

I expected to see the calls to my write callbacks.

Does anyone have used the write callbacks in libavformat? What have I done wrong?

0

There are 0 answers