Audio encoding process-----PCM encoding to AAC format

1. Audio encoding process

Insert image description here

2. Explanation of the meaning of coding function API

av_frame_make_writable ensures that the frame data is writable, avoiding data copying if possible. If the frame is writable, no operation is performed, if not, a new buffer is allocated and the data is copied. Returns: 0 on success, negative AVERROR on error.

The function av_samples_fill_arrays is to write the frame data you input into the AVFrame format for output, which is convenient for subsequent encoding send and recvice use.

3. Audio encoding practical demo PCM to AAC format

Coding notes:

  1. Encoder-related configurations must be set correctly, such as code rate, sampling rate, channels, sampling format, etc.
  2. You also need to set the format of AVFrame, mainly the sampling rate, channel, and sampling format.
  3. If the data is not in FLTP format, it needs to be converted to FLTP format.
  4. If you want to encode the PCM-encoded data into AAC format, you must add adtsheader in front.
#ifndef MAINBACK_C
#define MAINBACK_C
#endif // MAINBACK_C
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/frame.h>
#include <libavutil/samplefmt.h>
#include <libavutil/opt.h>
void f32le_convert_to_fltp(float *f32le, float *fltp, int nb_samples) {
    
    
    float *fltp_l = fltp;   // 左通道
    float *fltp_r = fltp + nb_samples;   // 右通道
    for(int i = 0; i < nb_samples; i++) {
    
    
        fltp_l[i] = f32le[i*2];     // 0 1   - 2 3
        fltp_r[i] = f32le[i*2+1];   // 可以尝试注释左声道或者右声道听听声音
    }
}
static void get_adts_header(AVCodecContext *ctx, uint8_t *adts_header, int aac_length)
{
    
    
    uint8_t freq_idx = 0;    //0: 96000 Hz  3: 48000 Hz 4: 44100 Hz
    switch (ctx->sample_rate) {
    
    
        case 96000: freq_idx = 0; break;
        case 88200: freq_idx = 1; break;
        case 64000: freq_idx = 2; break;
        case 48000: freq_idx = 3; break;
        case 44100: freq_idx = 4; break;
        case 32000: freq_idx = 5; break;
        case 24000: freq_idx = 6; break;
        case 22050: freq_idx = 7; break;
        case 16000: freq_idx = 8; break;
        case 12000: freq_idx = 9; break;
        case 11025: freq_idx = 10; break;
        case 8000: freq_idx = 11; break;
        case 7350: freq_idx = 12; break;
        default: freq_idx = 4; break;
    }
    uint8_t chanCfg = ctx->channels;
    uint32_t frame_length = aac_length + 7;
    adts_header[0] = 0xFF;
    adts_header[1] = 0xF1;
    adts_header[2] = ((ctx->profile) << 6) + (freq_idx << 2) + (chanCfg >> 2);
    adts_header[3] = (((chanCfg & 3) << 6) + (frame_length  >> 11));
    adts_header[4] = ((frame_length & 0x7FF) >> 3);
    adts_header[5] = (((frame_length & 7) << 5) + 0x1F);
    adts_header[6] = 0xFC;
}
int encode(AVCodecContext* codec_ctx,AVFrame* frame,AVPacket* pkt,FILE* out)
{
    
    
    int ret=0;
    ret = avcodec_send_frame(codec_ctx,frame);
    if(ret<0){
    
    
        printf("avcodec_send_frame failed\n");
        return ret;
    }

    while(1)
    {
    
    
        ret=avcodec_receive_packet(codec_ctx,pkt);
        if(ret==AVERROR(EAGAIN)||ret==AVERROR(AVERROR_EOF))
            break;
        if(ret<0)return ret;
        if(codec_ctx->flags&AV_CODEC_FLAG_GLOBAL_HEADER)
        {
    
    
            char header[7]={
    
    0};
            get_adts_header(codec_ctx,header,pkt->size);
            int size=fwrite(header,1,7,out);
            if(size!=7)
            {
    
    
                printf("fwrite error\n");
                return 0;
            }
        }

        fwrite(pkt->data,1,pkt->size,out);
    }

}
//编码流程  先初始化编码器,读取原始文件,send,recvice,写入
int main(int argc,char* argv[])
{
    
    
    if(argv<3)
    {
    
    
        printf("argv<3\n");
        return -1;
    }
    char* pcm_path=argv[1];
    char* out_path=argv[2];
    FILE* pcm_file=fopen(pcm_path,"rb");
    FILE* out_file=fopen(out_path,"wb");
    AVCodec* codec=avcodec_find_encoder(AV_CODEC_ID_AAC);
    AVCodecContext* codec_ctx=avcodec_alloc_context3(codec);

    codec_ctx->bit_rate=100000;
    codec_ctx->channel_layout=AV_CH_LAYOUT_STEREO;
    codec_ctx->channels=av_get_channel_layout_nb_channels(codec_ctx->channel_layout);
    codec_ctx->sample_fmt=AV_SAMPLE_FMT_FLTP;
//    codec_ctx->codec_id=AV_CODEC_ID_AAC;
//    codec_ctx->codec_type=AVMEDIA_TYPE_AUDIO;
    codec_ctx->sample_rate=48000;
    codec_ctx->profile=FF_PROFILE_AAC_LOW;
    codec_ctx->flags|=AV_CODEC_FLAG_GLOBAL_HEADER;
    avcodec_open2(codec_ctx,codec,NULL);

    AVFrame* frame=av_frame_alloc();
    AVPacket* pkt=av_packet_alloc();
//    frame->channel_layout=codec_ctx->channel_layout;
    frame->channels=codec_ctx->channels;
//    frame->sample_rate=codec_ctx->sample_rate;
    frame->format=codec_ctx->sample_fmt;
    frame->nb_samples=codec_ctx->frame_size;
    av_frame_get_buffer(frame,0);//分配frame的buff
    int frame_bytes=av_get_bytes_per_sample(frame->format)*frame->channels*frame->nb_samples;
    uint8_t* pcm_buff=av_malloc(frame_bytes);
    memset(pcm_buff,0,frame_bytes);
    uint8_t *pcm_temp_buf = (uint8_t *)malloc(frame_bytes);
    memset(pcm_temp_buf,0,frame_bytes);
    int pts=0;
    int ret=0;
    while(1)
    {
    
    
        int rsize=fread(pcm_buff,1,frame_bytes,pcm_file);
        ret = av_frame_make_writable(frame);
        if(ret != 0)
            printf("av_frame_make_writable failed, ret = %d\n", ret);
        f32le_convert_to_fltp(pcm_buff,pcm_temp_buf,frame->nb_samples);
        int need_size=av_samples_fill_arrays(frame->data,frame->linesize,pcm_temp_buf,frame->channels,frame->nb_samples,frame->format,0);
        if(rsize!=need_size)break;
        frame->pts=pts;
        pts+=1;
        printf("frame pts:%d\n",pts);
        encode(codec_ctx,frame,pkt,out_file);
    }
    encode(codec_ctx,NULL,pkt,out_file);
    return 0;
}

Guess you like

Origin blog.csdn.net/m0_60565784/article/details/131605920