ac-ffmpeg 0.18.1

Simple and safe Rust interface for FFmpeg libraries.
Documentation
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>

#include <stdlib.h>

const AVOutputFormat* ffw_guess_output_format(
    const char* short_name,
    const char* file_name,
    const char* mime_type) {
    return av_guess_format(short_name, file_name, mime_type);
}

typedef struct Muxer {
    AVFormatContext* fc;
    AVDictionary* options;
    int initialized;
} Muxer;

Muxer* ffw_muxer_new();
unsigned ffw_muxer_get_nb_streams(const Muxer*);
AVStream* ffw_muxer_get_stream(Muxer* muxer, unsigned stream_index);
int ffw_muxer_new_stream(Muxer*, const AVCodecParameters*);
int ffw_muxer_init(Muxer*, AVIOContext*, const AVOutputFormat*);
int ffw_muxer_get_option(Muxer*, const char*, uint8_t**);
int ffw_muxer_set_initial_option(Muxer*, const char*, const char*);
int ffw_muxer_set_option(Muxer*, const char*, const char*);
int ffw_muxer_set_metadata(Muxer*, const char*, const char*);
int ffw_muxer_write_frame(Muxer*, AVPacket*, uint32_t, uint32_t);
int ffw_muxer_interleaved_write_frame(Muxer*, AVPacket*, uint32_t, uint32_t);
int ffw_muxer_free(Muxer*);

Muxer* ffw_muxer_new() {
    Muxer* muxer = malloc(sizeof(Muxer));
    if (muxer == NULL) {
        return NULL;
    }

    muxer->fc = NULL;
    muxer->options = NULL;
    muxer->initialized = 0;

    muxer->fc = avformat_alloc_context();
    if (muxer->fc == NULL) {
        goto err;
    }

    return muxer;

err:
    ffw_muxer_free(muxer);

    return NULL;
}

unsigned ffw_muxer_get_nb_streams(const Muxer* muxer) {
    return muxer->fc->nb_streams;
}

AVStream* ffw_muxer_get_stream(Muxer* muxer, unsigned stream_index) {
    return muxer->fc->streams[stream_index];
}

int ffw_muxer_new_stream(Muxer* muxer, const AVCodecParameters* params) {
    AVStream* s;
    int ret;

    s = avformat_new_stream(muxer->fc, NULL);
    if (s == NULL) {
        return AVERROR(ENOMEM);
    }

    ret = avcodec_parameters_copy(s->codecpar, params);
    if (ret < 0) {
        return ret;
    }

    s->codecpar->codec_tag = 0;

    return s->index;
}

int ffw_muxer_init(
    Muxer* muxer,
    AVIOContext* avio_context,
    const AVOutputFormat* format) {
    int ret;

    muxer->fc->pb = avio_context;
    muxer->fc->oformat = (AVOutputFormat*)format;

    ret = avformat_write_header(muxer->fc, &muxer->options);
    if (ret < 0) {
        return ret;
    }

    muxer->initialized = 1;

    av_dict_free(&muxer->options);

    return ret;
}

int ffw_muxer_set_initial_option(Muxer* muxer, const char* key, const char* value) {
    return av_dict_set(&muxer->options, key, value, 0);
}

int ffw_muxer_set_option(Muxer* muxer, const char* key, const char* value) {
    return av_opt_set(muxer->fc, key, value, AV_OPT_SEARCH_CHILDREN);
}

int ffw_muxer_set_url(Muxer* muxer, const char* url) {
    av_freep(&muxer->fc->url);
    muxer->fc->url = av_strdup(url);
    return muxer->fc->url ? 0 : AVERROR(ENOMEM);
}

int ffw_muxer_set_metadata(Muxer* muxer, const char* key, const char* value) {
    return av_dict_set(&muxer->fc->metadata, key, value, 0);
}

static int ffw_rescale_packet_timestamps(Muxer* muxer, AVPacket* packet, uint32_t src_tb_num, uint32_t src_tb_den) {
    AVStream* stream;
    AVRational src_tb;

    unsigned stream_index;

    if (packet == NULL) {
        return 0;
    }

    stream_index = packet->stream_index;

    if (stream_index > muxer->fc->nb_streams) {
        return AVERROR(EINVAL);
    }

    stream = muxer->fc->streams[stream_index];

    src_tb.num = src_tb_num;
    src_tb.den = src_tb_den;

    av_packet_rescale_ts(packet, src_tb, stream->time_base);

    return 0;
}

int ffw_muxer_write_frame(Muxer* muxer, AVPacket* packet, uint32_t tb_num, uint32_t tb_den) {
    int ret = ffw_rescale_packet_timestamps(muxer, packet, tb_num, tb_den);

    if (ret < 0) {
        return ret;
    }

    return av_write_frame(muxer->fc, packet);
}

int ffw_muxer_interleaved_write_frame(Muxer* muxer, AVPacket* packet, uint32_t tb_num, uint32_t tb_den) {
    int ret = ffw_rescale_packet_timestamps(muxer, packet, tb_num, tb_den);

    if (ret < 0) {
        return ret;
    }

    return av_interleaved_write_frame(muxer->fc, packet);
}

int ffw_muxer_free(Muxer* muxer) {
    int ret = 0;

    if (muxer == NULL) {
        return 0;
    }

    if (muxer->initialized) {
        ret = av_write_trailer(muxer->fc);
    }

    avformat_free_context(muxer->fc);
    av_dict_free(&muxer->options);

    free(muxer);

    return ret;
}