[Libav-user] How to reset AVFormatContext so it could be reused?

Ragnar Rüütel ragnar.ruutel at gmail.com
Thu Nov 19 11:02:04 CET 2015


Hi all,

I'm trying to write a small tool which reads pcm16 audio from files,
converts them to webm/ogg format in buffer and passes it to another program
through socket (currently just dumps it on disk). I have it working with
one file but when I start to encode second file the avcodec_encode_audio2
function never sets got_packet variable to 1 (see the code below). I
suppose the reason is that AVFormatContext holds some state the needs to be
reset before it can be used again.

For sake of brevity the code sample here isn't complete but should give
pretty good overview about how I'm encoding audio. The my_iocontext class
is just holder for AVIOContext*, internal buffer and implements write and
seek functions for AVIOContext (allocated with avio_alloc_context function).

Any help is greatly appreciated.

With Regards,
Ragnar

//--------------------------------------------------------------

class audio_encoder
{
    AVFormatContext * formatContext;
    AVOutputFormat * outputFormat;
    AVStream * stream;
    AVCodecContext * codecContext;
    SwrContext * swr_ctx;
public:
    audio_encoder() {}
    ~audio_encoder() {
        avcodec_close(codecContext);
        swr_free(&swr_ctx);
        avformat_free_context(formatContext);
    }
    bool initialize();
    std::vector<unsigned char> encode( std::vector<unsigned char> audio );
};

bool audio_encoder::initialize()
{
    avformat_alloc_output_context2(&formatContext, NULL, NULL, ".webm");
    if (!formatContext) {
        return false;
    }

    outputFormat = formatContext->oformat;
    if ( outputFormat->audio_codec == AV_CODEC_ID_NONE ) {
        std::cout << "Audio codec not found" << std::endl;
        return false;
    }
    outputFormat->video_codec = AV_CODEC_ID_NONE;
    outputFormat->subtitle_codec = AV_CODEC_ID_NONE;

    AVCodec * codec = avcodec_find_encoder(outputFormat->audio_codec);
    if (!codec) {
        std::cout << "Could not find encoder for: " <<
avcodec_get_name(outputFormat->audio_codec) << std::endl;
        return false;
    }

    stream = avformat_new_stream(formatContext, codec);
    if (!stream) {
        std::cout << "Could not allocate stream" << std::endl;
        return false;
    }
    stream->id = formatContext->nb_streams-1;

    codecContext = stream->codec;
    codecContext->sample_fmt  = codec->sample_fmts ? codec->sample_fmts[0]
: AV_SAMPLE_FMT_FLTP;
    codecContext->bit_rate    = 64000;
    codecContext->sample_rate = 16000;

    codecContext->channels        =
av_get_channel_layout_nb_channels(codecContext->channel_layout);
    codecContext->channel_layout = AV_CH_LAYOUT_MONO;
    codecContext->channels        =
av_get_channel_layout_nb_channels(codecContext->channel_layout);
    stream->time_base = av_make_q( 1, codecContext->sample_rate );

    if (formatContext->oformat->flags & AVFMT_GLOBALHEADER) {
        codecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    }

    int ret = avcodec_open2(codecContext, codec, nullptr );
    if (ret < 0) {
        std::cout << "Could not open audio codec: " << av_err2str(ret) <<
std::endl;
        return false;
    }

    swr_ctx = swr_alloc();
    if (!swr_ctx) {
        std::cout << "Could not allocate resampler context" << std::endl;
        return false;
    }

    av_opt_set_int       (swr_ctx, "in_channel_count",
codecContext->channels,       0);
    av_opt_set_int       (swr_ctx, "in_sample_rate",
codecContext->sample_rate,    0);
    av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt",      AV_SAMPLE_FMT_S16,
0);
    av_opt_set_int       (swr_ctx, "out_channel_count",
 codecContext->channels,       0);
    av_opt_set_int       (swr_ctx, "out_sample_rate",
 codecContext->sample_rate,    0);
    av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt",
codecContext->sample_fmt,     0);

    if ((ret = swr_init(swr_ctx)) < 0) {
        std::cout << "Failed to initialize the resampling context" <<
std::endl;
        return false;
    }

    return true;
}


std::vector<unsigned char> audio_encoder::encode( std::vector<unsigned
char> pcm_audio )
{

    my_iocontext io_ctx;
    formatContext->pb = io_ctx.get_avio();

    int ret = avformat_write_header(formatContext, nullptr);
    if (ret < 0) {
        std::cout << "Error occurred when opening output file: " <<
av_err2str(ret) << std::endl;
        return {};
    }

    AVFrame * frame = av_frame_alloc();
    if (!frame) {
        std::cout << "Error allocating an audio frame" << std::endl;
        return {};
    }
    frame->format = codecContext->sample_fmt;
    frame->channel_layout = codecContext->channel_layout;
    frame->sample_rate = codecContext->sample_rate;
    frame->nb_samples = codecContext->frame_size;
    if (frame->nb_samples) {
        if (av_frame_get_buffer(frame, 0) < 0) {
            std::cout << "Error allocating an audio buffer" << std::endl;
            return {};
        }
    }

    int samples_count = 0;
    unsigned int size_in_bytes = (codecContext->frame_size*2);
    unsigned int cycles = (pcm_audio.size() / size_in_bytes );

    for( int x = 0; x < cycles; x++) {

        int start_idx = (x*size_in_bytes);
        std::vector<unsigned char> chunk( pcm_audio.begin()+start_idx,
 pcm_audio.begin()+start_idx + size_in_bytes );


        int dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx,
codecContext->sample_rate) + codecContext->frame_size,
                                        codecContext->sample_rate,
codecContext->sample_rate, AV_ROUND_UP);

        int ret = av_frame_make_writable(frame);
        if (ret < 0) {
            break;
        }

        ret = swr_convert(swr_ctx, frame->data, dst_nb_samples, (const
uint8_t **)&chunk, codecContext->frame_size); //in_frame->data
        if (ret < 0) {
            std::cout << "Error while converting" << std::endl;
            break;
        }

        frame->pts = av_rescale_q(samples_count, av_make_q( 1,
codecContext->sample_rate ), codecContext->time_base);
        samples_count += dst_nb_samples;

        int got_packet = 0;
        AVPacket pkt = { 0 };
        av_init_packet( &pkt );
        ret = avcodec_encode_audio2(codecContext, &pkt, frame, &got_packet);
        if (ret < 0) {
            std::cout << "Error encoding audio frame: " << av_err2str(ret)
<< std::endl;
            break;
        }

        if (got_packet) {
            ret = write_frame(formatContext, &codecContext->time_base,
stream, &pkt);
            if (ret < 0) {
                std::cout << "Error while writing audio frame: " <<
av_err2str(ret) << std::endl;
                break;
            }
        }
    }

    int got_packet = 0;
    do {
        AVPacket pkt = { 0 };
        av_init_packet(&pkt);
        int ret = avcodec_encode_audio2(codecContext, &pkt, nullptr,
&got_packet);
        if (ret < 0) {
            std::cout << "Error encoding audio frame: " << av_err2str(ret)
<< std::endl;
            break;
        }

        if (got_packet) {
            ret = write_frame(formatContext, &codecContext->time_base,
stream, &pkt);
            if (ret < 0) {
                std::cout << "Error while writing audio frame: " <<
 av_err2str(ret) << std::endl;
                break;
            }
        }
    } while ( got_packet );

    av_write_trailer(formatContext);

    av_frame_free( &frame );

    return io_ctx.get_data();
}


int main(int argc, char **argv)
{

    std::ifstream input_file;
    input_file.open( "/tmp/input.wav", std::ios_base::in |
std::ios_base::binary );
    if ( !input_file.is_open() ) {
        printf("Unable to open input file.\n");
        return 1;
    }
    std::vector<unsigned char> pcm_audio( ( std::istreambuf_iterator<char>(
input_file ) ), ( std::istreambuf_iterator<char>() ) );


    av_register_all();
    av_log_set_level(AV_LOG_DEBUG);

    audio_encoder encoder;
    if (!encoder.initialize()) {
        std::cout << "Error initializing ..." << std::endl;
        exit(1);
    }

    std::vector<unsigned char> bufr = encoder.encode( pcm_audio );

    std::ofstream cmp( "/tmp/result.webm", std::ios_base::out |
std::ios_base::binary );
    cmp.write( ( char * )&bufr[0], bufr.size() );
    cmp.close();

    std::vector<unsigned char> bufr2 = encoder.encode( pcm_audio );

    std::ofstream cmp2( "/tmp/result2.webm", std::ios_base::out |
std::ios_base::binary );
    cmp2.write( ( char * )&bufr2[0], bufr2.size() );
    cmp2.close();

    return 0;
}
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://ffmpeg.org/pipermail/libav-user/attachments/20151119/03eaeceb/attachment.html>


More information about the Libav-user mailing list