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?