音视频开发:音频fdk-aac编码

编码的大概流程见下图

1.获取编码器: avcodec_find_encoder_by_name("libfdk_aac")

2.检查PCM格式是否被编码器支持

3.创建编码上下文: AVCodecContext *ctx = avcodec_alloc_context3(codec)

4.给上下文设置参数

5.打开编码器: avcodec_open2

6.创建AVFrame: av_frame_alloc

7.创建AVPacket: av_packet_alloc

8.打开文件

9.读取数据,放入AVFrame

10.把AVFrame数据放入编码器: avcodec_send_frame

11.从编码器中获取编码好的数据并放入AVPacket: avcodec_receive_packet

12.写入文件

13.全部读完以后刷新缓冲区

14.释放资源

话不多说,上代码

#include <libavutil/samplefmt.h>
#include <libavcodec/avcodec.h>

typedef struct {

    NSString *filePath;

    int sampleRate;

    enum AVSampleFormat sampleFmt;

    int chLayout;

} AudioEncodeSpec;

//检查编码器是否支持当前采样格式
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;

}

//音频编码
static int encode(AVCodecContext *ctx,

                  AVFrame *frame,

                  AVPacket *pkt,

                  NSFileHandle *writeHandle) {

    int ret = avcodec_send_frame(ctx, frame);

    if (ret < 0) {

        NSLog(@"avcodec_send_frame error");

        return ret;

    }

    //不断从编码器取出编码后的数据

    while (true) {

        ret = avcodec_receive_packet(ctx, pkt);

        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {

            //继续读取数据

            return 0;

        } else if (ret < 0) {//其它

            return ret;

        }

        [writeHandle seekToEndOfFile];

        [writeHandle writeData:[NSData dataWithBytes:pkt->data length:pkt->size]];

        //释放

        av_packet_unref(pkt);

    }

}

+ (void)aacEncode:(AudioEncodeSpec)spec outFilename:(NSString *)outFilename
{

    //返回结果

    int ret = 0;

    //编码器

    AVCodec *codec = avcodec_find_encoder_by_name("libfdk_aac");

    if (!codec) {

        NSLog(@"找不到编码器");

        return;

    }
    //检查输入格式是否支持,fdk-aac只支持16位整数

    if (!check_sample_fmt(codec, spec.sampleFmt)) {

        NSLog(@"采样格式不支持");

        return;

    }

    //编码上下文

    AVCodecContext *ctx = avcodec_alloc_context3(codec);

    if (!ctx) {

        NSLog(@"编码上下文创建失败");

        return;

    }

    //设置PCM参数

    ctx->sample_rate = spec.sampleRate;

    ctx->sample_fmt = spec.sampleFmt;

    ctx->channel_layout = spec.chLayout;

    //固定比特率

    ctx->bit_rate = 44100;

    //规格

    ctx->profile = FF_PROFILE_AAC_HE;

    //打开编码器

    ret = avcodec_open2(ctx, codec, NULL);

    if (ret < 0) {

        NSLog(@"打开编码器失败");

        avcodec_free_context(&ctx);

        return;

    }

    

    //存放编码前的数据

    AVFrame *frame = av_frame_alloc();

    if (!frame) {

        NSLog(@"av_frame_alloc error");

        avcodec_free_context(&ctx);

        return;

    }

    //frame缓冲区的样本帧数量

    frame->nb_samples = ctx->frame_size;

    //采样格式

    frame->format = ctx->sample_fmt;

    //声道布局

    frame->channel_layout = ctx->channel_layout;

    

    //创建缓冲区

    ret = av_frame_get_buffer(frame, 0);

    if (ret < 0) {

        NSLog(@"av_frame_get_buffer error");

        avcodec_free_context(&ctx);

        av_frame_free(&frame);

        return;

    }

    //存放编码后的数据

    AVPacket *pkt = av_packet_alloc();

    if (!pkt) {

        NSLog(@"av_packet_alloc error");

        avcodec_free_context(&ctx);

        av_frame_free(&frame);

        return;

    }


    //创建输出的文件

    NSString *outFilePath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:outFilename];

    NSFileManager *writeManager = [NSFileManager defaultManager];

    if ([writeManager fileExistsAtPath:outFilePath]) {

        [writeManager removeItemAtPath:outFilePath error:nil];

    }

    [writeManager createFileAtPath:outFilePath contents:nil attributes:nil];

    NSFileHandle *writeHandle = [NSFileHandle fileHandleForWritingAtPath:outFilePath];

    

    //打开输入文件

    NSFileHandle *readHandle = [NSFileHandle fileHandleForReadingAtPath:spec.filePath];

    [readHandle seekToFileOffset:0];

    

    NSData *readData = [readHandle readDataOfLength:frame->linesize[0]];

    int len = (int)[readData length];

    frame->data[0] = (uint8_t *)[readData bytes];

    while (len > 0) {

        //从文件中读取的数据,不足以填充frame缓冲区

        if (len < frame->linesize[0]) {

            int bytes = av_get_bytes_per_sample(frame->format);

            int ch = av_get_channel_layout_nb_channels(frame->channel_layout);

            //设置真正有效的样本帧数量

            frame->nb_samples = ret / (bytes * ch);

        }

        if (encode(ctx, frame, pkt, writeHandle) < 0) {

            break;

        }

        readData = [readHandle readDataOfLength:frame->linesize[0]];

        frame->data[0] = (uint8_t *)[readData bytes];

        len = (int)[readData length];

    }

    //刷新缓冲区

    encode(ctx, NULL, pkt, writeHandle);

    //释放资源

    [readHandle closeFile];

    [writeHandle closeFile];

    av_frame_free(&frame);

    av_packet_free(&pkt);

    avcodec_free_context(&ctx);           

    NSLog(@"faac编码完成");

}

本文福利, 免费领取C++音视频学习资料包+学习路线大纲、技术视频/代码,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓ 

猜你喜欢

转载自blog.csdn.net/m0_73443478/article/details/134710327