ffmpeg4.2.2 音频编码,pcm编码成AAC

      新版的ffmpeg 编码AAC只支持的AV_SAMPLE_FMT_FLTP,老版本的是AV_SAMPLE_FMT_S16,如果输入的PCM数据是AV_SAMPLE_FMT_S16的,avcodec_encode_audio2会返回-22错误.

#include <libavcodec/avcodec.h>
#include <libavutil/samplefmt.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>


void encode(AVCodecContext* codecCtx,AVFrame* frame,AVPacket* packet,AVFormatContext* formatCtx);

static int ptsIndex = 0;

int main(int argc,char* argv[])
{
    AVCodecContext* codecCtx = NULL;
    AVCodec* codec = NULL;
    int ret = 0;
    AVFrame* frame = NULL;
    AVPacket* packet = NULL;
    AVFormatContext* formatCtx = NULL;
    AVStream* stream = NULL;

    /* 根据传入的音频类型ID寻找并返回对应解码器 */
    codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
    if(!codec)
    {
        printf("codec not find\n");
        exit(1);
    }

    /* 给编解码上下文分配内存并赋默认值  */
    codecCtx = avcodec_alloc_context3(codec);
    if(!codecCtx)
    {
        printf("Could not allocate audio codec context\n");
        exit(1);
    }

    /* 设置码率,大概为62kb/s */
    codecCtx->bit_rate = 64000;
    /* 采样格式,类似于图像存储格式,位数越多,表示值越精确 */
    codecCtx->sample_fmt = AV_SAMPLE_FMT_FLTP;

    /*  检查编码器是否支持这种采样格式 */
    const enum AVSampleFormat* sampleFmt = codec->sample_fmts;
 
    while(*sampleFmt != AV_SAMPLE_FMT_NONE)
    {
        if(*sampleFmt == codecCtx->sample_fmt)
            break;
        sampleFmt++;
    }
    if(*sampleFmt != codecCtx->sample_fmt)
    {
        printf("Encoder not support sample format,%d %d\n",*sampleFmt,codecCtx->sample_fmt);
        exit(1);
    }

    /* 采样率:采样设备每秒抽取样本的次数;下面操作是选择最高的采样率  */
    if(!codec->supported_samplerates)
        codecCtx->sample_rate = 44100;
    
    const int* tempPtr = codec->supported_samplerates;
    int bestSampleRate = 0;
    while(*tempPtr)
    {
        if(!bestSampleRate || abs(44100 - *tempPtr) < abs(44100 - bestSampleRate))
            bestSampleRate = *tempPtr;
        tempPtr++;
    }
    codecCtx->sample_rate = bestSampleRate;

    /* 通道布局:是一个64位整数,每个值为1的位对应一个通道;
     下面操作从编码器中选择具有最高通道计数的布局 */
    if(!codec->channel_layouts)
        codecCtx->channel_layout = AV_CH_LAYOUT_STEREO;
    else
    {     
        const uint64_t* tempPtr2 = codec->channel_layouts;
        int bestNbChannel = 0;
        uint64_t bestChLayout = 0;
        while(*tempPtr2)
        {
            int nbChannel = av_get_channel_layout_nb_channels(*tempPtr2);
            if(nbChannel > bestNbChannel)
            {
                bestChLayout = *tempPtr2;
                bestNbChannel = nbChannel;
            }
            tempPtr2++;
        }
        codecCtx->channel_layout = bestChLayout;
    }

    /*具体通道个数*/
    codecCtx->channels = av_get_channel_layout_nb_channels(codecCtx->channel_layout);

    /*初始化AVCodecContext,各种分配内存和检查,初始化到具体的编码器库*/
    ret = avcodec_open2(codecCtx,codec,NULL);
    if(ret != 0)
    {
        fprintf(stderr, "Could not open codec\n");
        exit(1);       
    }

    /* 新版的ffmpeg 编码AAC只支持的AV_SAMPLE_FMT_FLTP,老版本的是AV_SAMPLE_FMT_S16 
     如果输入的PCM数据是AV_SAMPLE_FMT_S16的,avcodec_encode_audio2会返回-22错误 */

    const char* inFilename = "source/test.pcm";
    const char* outFilename = "source/test.aac";
    FILE* inFile = fopen(inFilename,"rb");
    
    packet = av_packet_alloc();
    frame = av_frame_alloc();

    frame->nb_samples = codecCtx->frame_size;
    frame->format = codecCtx->sample_fmt;
    frame->channel_layout = codecCtx->channel_layout;

    ret = av_frame_get_buffer(frame,0);
    if (ret < 0)
    {
        fprintf(stderr, "Could not allocate audio data buffers\n");
        exit(1);
    }

    /* 分配内存,根据格式名(第三个参数)或者文件名(第四个参数)获取输出格式 */
    avformat_alloc_output_context2(&formatCtx,NULL,NULL,outFilename);
    if(!formatCtx)
    {
        printf("could not alloc output context,exit!!\n");
        exit(1);
    }

    stream = avformat_new_stream(formatCtx,codec);
    if(avcodec_parameters_from_context(stream->codecpar,codecCtx) < 0)
    {
        fprintf(stderr,"copy parameters fail!\n");
        exit(1);
    }

    /* AVFMT_NOFILE跟是否是一个真实的文件无关 */
    if(!(formatCtx->oformat->flags & AVFMT_NOFILE))
    {
        if(avio_open2(&formatCtx->pb,outFilename,AVIO_FLAG_WRITE,NULL,NULL))
        {
            printf("avio open fail\n");
            exit(1);
        }
    }

    /* 每个通道占的字节数 */
    int dataSize = av_get_bytes_per_sample(codecCtx->sample_fmt);

    /*  针对不同的编码格式,写不同的文件头*/
    if(avformat_write_header(formatCtx,NULL) < 0)
    {
        fprintf(stderr, "Error occurred when opening output file: %s\n",av_err2str(ret));
        goto end;
    }

    while(!feof(inFile))
    {
         /* 确保frame是可写的,例如编码器要缓存此帧,下面函数就会分配新的缓冲区,并复制参数 */
        ret = av_frame_make_writable(frame);
        if(ret < 0)
        {
            printf("%s %d ret:%d\n",__FUNCTION__,__LINE__,ret);        
            goto end;
        }

        for(int i = 0;i < codecCtx->frame_size;++i)
            for(int j = 0; j < codecCtx->channels;++j)
                fread(frame->data[j]+dataSize*i,1,dataSize,inFile);


        encode(codecCtx,frame,packet,formatCtx);
    }

    /* 发送一个空帧,可以防止编码器中数据未处理完 */
    encode(codecCtx,NULL,packet,formatCtx);

    /*  针对不同的编码格式,写不同的文件尾 */   
    if(av_write_trailer(formatCtx) != 0)
        printf("write file trail fail\n");

    printf("%s %d  end!!!\n",__FUNCTION__,__LINE__);
end:
    if(inFile)
        fclose(inFile);
    avcodec_free_context(&codecCtx);
    avformat_free_context(formatCtx);
    av_frame_free(&frame);
    av_packet_free(&packet);


    return 0;
}


void encode(AVCodecContext* codecCtx,AVFrame* frame,AVPacket* packet,AVFormatContext* formatCtx)
{
    int ret = 0;
    
    ret = avcodec_send_frame(codecCtx,frame);
    if(ret < 0)
    {
        printf(" send frame to encodec faile! ret:%d\n",ret);
        exit(1) ;
    }

    while(ret >= 0)
    {
        ret = avcodec_receive_packet(codecCtx,packet);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;
        else if (ret < 0) {
            fprintf(stderr, "Error encoding audio frame\n");
            exit(1);
        }

        packet->pts = ptsIndex*100;
        packet->dts = ptsIndex*100;
        packet->pos = -1;
        packet->stream_index = 0;

        if(av_interleaved_write_frame(formatCtx,packet) != 0)
        {
            printf("write frame fail\n");
            break;
        }

        ptsIndex++;

        av_packet_unref(packet);
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值