#define AL_LOG_SECTION "ff_encoder" #include #include #include "encoder.h" #define FRAME_DURATION 20 #define SAMPLES_PER_FRAME (48000 / 1000.0 * FRAME_DURATION) #define CHANNELS 2 static void free_frame_data(struct camu_ff_encoder *enc) { if (enc->frame && enc->frame->nb_samples > 0) av_freep(&enc->frame->data[0]); } bool camu_ff_encoder_init(struct camu_ff_encoder *enc, char *encoder_name, char *ext, u32 bitrate) { al_memset(enc, 0, sizeof(struct camu_ff_encoder)); const AVCodec *codec = avcodec_find_encoder_by_name(encoder_name); if (!codec) { log_error("Failed to find codec by name %s.", encoder_name); goto err; } enc->codec_context = avcodec_alloc_context3(codec); if (!enc->codec_context) { log_error("Failed to alloc codec context."); goto err; } enc->codec_context->sample_rate = 48000; camu_default_channel_layout(&AV_CODECPAR_CHANNEL_LAYOUT(enc->codec_context), CHANNELS); enc->codec_context->sample_fmt = AV_SAMPLE_FMT_FLT; enc->codec_context->bit_rate = bitrate; enc->codec_context->frame_size = SAMPLES_PER_FRAME; enc->codec_context->time_base = (AVRational){ 1, enc->codec_context->sample_rate }; enc->codec_context->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; enc->output_format = av_guess_format(encoder_name, ext, NULL); if (enc->output_format && enc->output_format->flags & AVFMT_GLOBALHEADER) { enc->codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; } AVDictionary *opts = NULL; char kbps[16]; al_snprintf(kbps, sizeof(kbps), "%uk", bitrate / 1000); av_dict_set(&opts, "b", kbps, 0); av_dict_set(&opts, "vbr", "off", 0); av_dict_set(&opts, "compression_level", "10", 0); av_dict_set(&opts, "frame_duration", "20", 0); av_dict_set(&opts, "application", "audio", 0); if (avcodec_open2(enc->codec_context, codec, &opts) < 0) { log_error("Failed to open codec."); av_dict_free(&opts); goto err; } av_dict_free(&opts); log_info("Encoder: %s (%s) %sbps.", codec->name, codec->long_name ? codec->long_name : codec->name, kbps); AVCodecContext *out_codec = enc->codec_context; AVFrame *frame = av_frame_alloc(); frame->format = out_codec->sample_fmt; frame->sample_rate = out_codec->sample_rate; camu_copy_channel_layout(&AV_CODECPAR_CHANNEL_LAYOUT(frame), &AV_CODECPAR_CHANNEL_LAYOUT(out_codec)); av_samples_alloc(frame->data, NULL, AV_CODECPAR_CHANNELS(out_codec), SAMPLES_PER_FRAME, (enum AVSampleFormat)frame->format, 1); frame->nb_samples = SAMPLES_PER_FRAME; frame->pts = 0; enc->fmt.format = frame->format; enc->fmt.sample_rate = frame->sample_rate; camu_copy_channel_layout(&enc->fmt.channel_layout, &AV_CODECPAR_CHANNEL_LAYOUT(frame)); enc->fmt.channel_count = AV_CODECPAR_CHANNELS(frame); enc->frame = frame; return true; err: camu_ff_encoder_close(enc); return false; } void camu_ff_encoder_push(struct camu_ff_encoder *enc, u8 **data, s32 sample_count) { al_assert(sample_count == SAMPLES_PER_FRAME * CHANNELS); enc->frame->data[0] = data[0]; s32 ret = avcodec_send_frame(enc->codec_context, enc->frame); if (ret < 0 && ret != AVERROR_EOF) { log_error("Error sending frame to the encoder (%s).", av_err2str(ret)); return; } AVPacket pkt = { 0 }; for (;;) { ret = avcodec_receive_packet(enc->codec_context, &pkt); if (ret < 0) { if (!(ret = AVERROR_EOF || ret == AVERROR(EAGAIN))) { log_error("Error receiving packet from the encoder (%s).", av_err2str(ret)); } return; } enc->callback(enc->userdata, &pkt); av_packet_unref(&pkt); enc->frame->pts += SAMPLES_PER_FRAME; } } void camu_ff_encoder_reset(struct camu_ff_encoder *enc) { avcodec_flush_buffers(enc->codec_context); } void camu_ff_encoder_close(struct camu_ff_encoder *enc) { if (enc->codec_context) avcodec_free_context(&enc->codec_context); free_frame_data(enc); if (enc->frame) av_frame_free(&enc->frame); }