FFMPEG 音频编码

tech2025-01-19  2

FFMPEG 两套API对PCM数据进行编码

1.avcodec_send_frame  &  avcodec_receive_packet2.

2. ffmpeg中解码的API之前的是avcodec_decode_video2()和avcodec_decode_audio2(),现在使用avcodec_send_packet()/ avcodec_receive_frame()来代替原有的接口。

program1 : 使用API avcodec_send_frame   & avcodec_receive_packet2

FFMPEG 编码PCM数据为MP3格式,设置codec的参数为PCM的数据,8K,mono,f16le(AV_SAMPLE_FMT_S16P)

注意点:    encode(codec_ctx, NULL, pkt, fp_out); 

编码后的数据经常会少最后几帧,很多情况就是因为没有处理好缓冲帧的问题,ffmpeg内部会缓冲几帧,要想取出来就需要传递空的AVFrame进去。

#include <stdio.h> #define __STDC_CONSTANT_MACROS extern "C" { #include "libavformat/avformat.h" #include "libavformat/avio.h" #include "libavdevice/avdevice.h" #include "libavcodec/avcodec.h" #include "libswscale/swscale.h" #include "libswresample/swresample.h" #include "libavutil/imgutils.h" } static void encode(AVCodecContext *cdc_ctx, AVFrame *frame, AVPacket *pkt, FILE *fp_out) { int ret = 0; if ((ret = avcodec_send_frame(cdc_ctx, frame)) < 0) { fprintf(stderr, "avcodec_send_frame failed.\n"); exit(1); } while ((ret = avcodec_receive_packet(cdc_ctx, pkt)) >= 0) { printf("Write (size=%d) packet.\n", pkt->size); fwrite(pkt->data, 1, pkt->size, fp_out); av_packet_unref(pkt); } if ((ret != AVERROR(EAGAIN)) && (ret != AVERROR_EOF)) { fprintf(stderr, "avcodec_receive_packet failed.\n"); exit(1); } } int main(int argc, char *argv[]) { avdevice_register_all(); //查找编码器 AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_MP3); //codec_ctx = avcodec_find_encoder_by_name("libfdk_aac"); if (!codec) { printf("can't find encoder\n"); return -1; } AVCodecContext *codec_ctx = NULL; if ((codec_ctx = avcodec_alloc_context3(codec)) == NULL) { fprintf(stderr, "avcodec_alloc_context3 failed.\n"); } codec_ctx->bit_rate = 64000; codec_ctx->sample_fmt = AV_SAMPLE_FMT_S16P; codec_ctx->sample_rate = 8000; codec_ctx->channel_layout = av_get_channel_layout("mono"); codec_ctx->channels = av_get_channel_layout_nb_channels(codec_ctx->channel_layout); //打开编码器 if (avcodec_open2(codec_ctx, codec, NULL) < 0) { printf("can't open encoder\n"); } AVPacket *pkt = NULL; if ((pkt = av_packet_alloc()) == NULL) { fprintf(stderr, "av_packet_alloc failed.\n"); } AVFrame *frame = av_frame_alloc(); if (!frame) { printf("can't alloc frame\n"); return -1; } frame->nb_samples = codec_ctx->frame_size; frame->format = AV_SAMPLE_FMT_S16P; frame->channel_layout = codec_ctx->channel_layout; int ret = -1; if ((ret = av_frame_get_buffer(frame, 0)) < 0) { fprintf(stderr, "av_frame_get_buffer failed.\n"); } const char *input_file = "/home/lili/Videos/beijingbeijing_8k_16bits_memo.pcm"; const char *output_file = "/home/lili/Videos/output.mp3"; FILE *fp_in = NULL; if ((fp_in = fopen(input_file, "rb")) == NULL) { fprintf(stderr, "fopen %s failed.\n", input_file); } FILE *fp_out = NULL; if ((fp_out = fopen(output_file, "wb")) == NULL) { fprintf(stderr, "fopen %s failed.\n", output_file); } int data_size = av_get_bytes_per_sample(codec_ctx->sample_fmt); while (feof(fp_in) == 0) { int i = 0, ch = 0; if ((ret = av_frame_make_writable(frame)) < 0) { fprintf(stderr, "frame is not writable.\n"); } //Packed: L R L R L R L R //Planar: L L L L R R R R for (i = 0; i < frame->nb_samples; i++) { for (ch = 0; ch < codec_ctx->channels; ch++) { fread(frame->data[ch] + data_size * i, 1, data_size, fp_in); } } encode(codec_ctx, frame, pkt, fp_out); } encode(codec_ctx, NULL, pkt, fp_out); fclose(fp_out); fclose(fp_in); av_frame_free(&frame); av_packet_free(&pkt); avcodec_close(codec_ctx); avcodec_free_context(&codec_ctx); return 0; }

 

 上面的程序如果修改成AAC编码?

 

 

 

extern "C" { #include "libavformat/avformat.h" #include "libavcodec/avcodec.h" } /* check that a given sample format is supported by the encoder */ static int check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt) { const enum AVSampleFormat *p = codec->sample_fmts; while (*p != AV_SAMPLE_FMT_NONE) { if (*p == sample_fmt) return 1; p++; } return 0; } /* just pick the highest supported samplerate */ static int select_sample_rate(const AVCodec *codec) { const int *p; int best_samplerate = 0; if (!codec->supported_samplerates) return 44100; p = codec->supported_samplerates; while (*p) { if (!best_samplerate || abs(44100 - *p) < abs(44100 - best_samplerate)) best_samplerate = *p; p++; } return best_samplerate; } /* select layout with the highest channel count */ static int select_channel_layout(const AVCodec *codec) { const uint64_t *p; uint64_t best_ch_layout = 0; int best_nb_channels = 0; if (!codec->channel_layouts) return AV_CH_LAYOUT_STEREO; p = codec->channel_layouts; while (*p) { int nb_channels = av_get_channel_layout_nb_channels(*p); if (nb_channels > best_nb_channels) { best_ch_layout = *p; best_nb_channels = nb_channels; } p++; } return best_ch_layout; } int main() { av_register_all(); AVCodec * codec = avcodec_find_encoder(AV_CODEC_ID_MP2); if (codec == NULL) { fprintf(stderr, "Codec not found!\n"); return -1; } AVCodecContext *codec_ctx = avcodec_alloc_context3(codec); if (codec_ctx == NULL) { fprintf(stderr, "Could not allocate audio codec context!\n"); return -1; } //put sample param codec_ctx->bit_rate = 64000; /* check that the encoder supports s16 pcm input */ codec_ctx->sample_fmt = AV_SAMPLE_FMT_S16; if (!check_sample_fmt(codec, codec_ctx->sample_fmt)) { fprintf(stderr, "Encoder does not support sample format %s", av_get_sample_fmt_name(codec_ctx->sample_fmt)); exit(1); } /* select other audio parameters supported by the encoder */ codec_ctx->sample_rate = select_sample_rate(codec); codec_ctx->channel_layout = select_channel_layout(codec); codec_ctx->channels = av_get_channel_layout_nb_channels(codec_ctx->channel_layout); if (avcodec_open2(codec_ctx, codec, NULL) < 0) { fprintf(stderr, "Could not open codec!\n"); return -1; } AVFrame *frame = av_frame_alloc(); if (!frame) { fprintf(stderr, "Could not allocate audio frame\n"); return -1; } frame->nb_samples = codec_ctx->frame_size; frame->format = codec_ctx->sample_fmt; frame->channel_layout = codec_ctx->channel_layout; int ret = av_frame_get_buffer(frame, 0); if (ret < 0) { fprintf(stderr, "av_frame_get_buffer error!\n"); return -1; } const char *name = "./test.aac"; FILE *file = fopen(name, "wb"); if (!file) { fprintf(stderr, "Could not open %s\n", name); exit(1); } /* encode a single tone sound */ float t = 0.01, tincr; AVPacket pkt; tincr = 2 * M_PI * 440.0 / codec_ctx->sample_rate; for (int i = 0; i < 200; i++) { av_init_packet(&pkt); pkt.data = NULL; // packet data will be allocated by the encoder pkt.size = 0; /* make sure the frame is writable -- makes a copy if the encoder * kept a reference internally */ ret = av_frame_make_writable(frame); if (ret < 0) exit(1); uint16_t *samples = (uint16_t*)frame->data[0]; for (int j = 0; j < codec_ctx->frame_size; j++) { samples[2 * j] = (int)(sin(t) * 10000); for (int k = 1; k < codec_ctx->channels; k++) samples[2 * j + k] = samples[2 * j]; t += tincr; } /* encode the samples */ int got_output = 0; ret = avcodec_encode_audio2(codec_ctx, &pkt, frame, &got_output); if (ret < 0) { fprintf(stderr, "Error encoding audio frame\n"); exit(1); } if (got_output) { fwrite(pkt.data, 1, pkt.size, file); av_packet_unref(&pkt); } } /* get the delayed frames */ for (int got_output = 1; got_output;) { ret = avcodec_encode_audio2(codec_ctx, &pkt, NULL, &got_output); if (ret < 0) { fprintf(stderr, "Error encoding frame\n"); exit(1); } if (got_output) { fwrite(pkt.data, 1, pkt.size, file); av_packet_unref(&pkt); } } fclose(file); av_frame_free(&frame); avcodec_free_context(&codec_ctx); }

使用 avcodec_encode_audio2

#include <stdio.h> #include <stdlib.h> #include <string.h> extern "C" { #include <libavcodec/avcodec.h> } void encode_audio(const char *input_file, const char *output_file) { int ret = 0; int data_size = 0; AVCodec *codec = NULL; AVCodecContext *cdc_ctx = NULL; AVPacket *pkt = NULL; AVFrame *frame = NULL; FILE *fp_in, *fp_out; int got_data = 0; if ((codec = avcodec_find_encoder(AV_CODEC_ID_MP3)) == NULL) { fprintf(stderr, "avcodec_find_encoder_by_name failed.\n"); goto ret1; } if ((cdc_ctx = avcodec_alloc_context3(codec)) == NULL) { fprintf(stderr, "avcodec_alloc_context3 failed.\n"); goto ret1; } #if 1 /*encode short_mixed_stereo_48.pcm*/ cdc_ctx->bit_rate = 64000; cdc_ctx->sample_fmt = AV_SAMPLE_FMT_S16P; cdc_ctx->sample_rate = 48000; cdc_ctx->channel_layout = av_get_channel_layout("stereo"); cdc_ctx->channels = av_get_channel_layout_nb_channels(cdc_ctx->channel_layout); #else /*encode beijingbeijing_8k_16bits_memo.pcm*/ cdc_ctx->bit_rate = 64000; cdc_ctx->sample_fmt = AV_SAMPLE_FMT_S16P; cdc_ctx->sample_rate = 8000; cdc_ctx->channel_layout = av_get_channel_layout("mono"); cdc_ctx->channels = av_get_channel_layout_nb_channels(cdc_ctx->channel_layout); #endif if ((ret = avcodec_open2(cdc_ctx, codec, NULL)) < 0) { fprintf(stderr, "avcodec_open2 failed.\n"); goto ret2; } if ((pkt = av_packet_alloc()) == NULL) { fprintf(stderr, "av_packet_alloc failed.\n"); goto ret3; } if ((frame = av_frame_alloc()) == NULL) { fprintf(stderr, "av_frame_alloc failed.\n"); goto ret4; } frame->nb_samples = cdc_ctx->frame_size; frame->format = cdc_ctx->sample_fmt; frame->channel_layout = cdc_ctx->channel_layout; if ((ret = av_frame_get_buffer(frame, 0)) < 0) { fprintf(stderr, "av_frame_get_buffer failed.\n"); goto ret5; } if ((fp_in = fopen(input_file, "rb")) == NULL) { fprintf(stderr, "fopen %s failed.\n", input_file); goto ret5; } if ((fp_out = fopen(output_file, "wb")) == NULL) { fprintf(stderr, "fopen %s failed.\n", output_file); goto ret6; } data_size = av_get_bytes_per_sample(cdc_ctx->sample_fmt); fprintf(stderr, "data_size = %d\n", data_size); while (feof(fp_in) == 0) { int i = 0, ch = 0; if ((ret = av_frame_make_writable(frame)) < 0) { fprintf(stderr, "frame is not writable.\n"); goto ret7; } for (i = 0; i < frame->nb_samples; i++) { for (ch = 0; ch < cdc_ctx->channels; ch++) { fread(frame->data[ch] + data_size * i, 1, data_size, fp_in); } } if ((ret = avcodec_encode_audio2(cdc_ctx, pkt, frame, &got_data)) < 0) { fprintf(stderr, "avcodec_encode_audio2 failed.\n"); exit(1); } if(got_data) { int nwrite = fwrite(pkt->data, 1, pkt->size, fp_out); fprintf(stdout, "nwrite %d data.\n", nwrite); av_packet_unref(pkt); } } /* get the delayed frames */ for (int got_data = 1; got_data;) { ret = avcodec_encode_audio2(cdc_ctx, pkt, NULL, &got_data); if (ret < 0) { fprintf(stderr, "Error encoding frame\n"); exit(1); } if (got_data) { int nwrite = fwrite(pkt->data, 1, pkt->size, fp_out); fprintf(stdout, "nwrite %d data.\n", nwrite); av_packet_unref(pkt); } } fclose(fp_out); fclose(fp_in); av_frame_free(&frame); av_packet_free(&pkt); avcodec_close(cdc_ctx); avcodec_free_context(&cdc_ctx); return; ret7: fclose(fp_out); ret6: fclose(fp_in); ret5: av_frame_free(&frame); ret4: av_packet_free(&pkt); ret3: avcodec_close(cdc_ctx); ret2: avcodec_free_context(&cdc_ctx); ret1: exit(1); } int main(int argc, const char *argv[]) { encode_audio("/home/lili/Videos/short_mixed_stereo_48.pcm", "/home/lili/Videos/output.mp3"); return 0; }

 

最新回复(0)