音视频开发十三:原始音频数据编码

编码流程

  1. 查找编码器

  2. 创建编码器上下文

  3. 设置编码器参数

    编码的一些信息,

  4. 编码器与编码器上下文绑定在一起

  5. 创建输出编码后的文件

  6. 创建(获取)AVFrame

    这个是原始的帧数据。

  7. 创建AVPacket

    编码后的是音频帧是保存在数据包中的。一个数据包包括多个数据帧。

  8. 生成(获取)音频内容。

  9. 进行编码。

    对音频内容进行编码。

  10. 得到编码后数据写入到输出文件

流程图如下:

实现代码

自定义生成音频数据,生成编码格式为aac的编码文件

#include <libavutil/log.h>
#include <libavutil/opt.h>
#include <libavutil/samplefmt.h>
#include <libavcodec/avcodec.h>
#include <stdio.h>
#include <libavutil/avutil.h>
#include <libavformat/avformat.h>
#include <SDL.h>
#include <stdbool.h>

// 从编码器中获取支持的最佳的simple_rate
static int select_best_sample_rate(const AVCodec* codec) {
    
    
    const int* p;
    int best_samplerate = 0;

    // codec->supported_samplerates 属性是指向表示编解码器支持哪些采样率的整数指针数组的指针
    if (!codec->supported_samplerates) {
    
    // 如果该编码器不支持采样率
        return 44100;
    }
    p = codec->supported_samplerates;
    // 以44100为标杆,谁离它近 选谁。
    while (*p) {
    
    
        if (!best_samplerate || abs(44100 - *p) < abs(44100 - best_samplerate)) {
    
    
            best_samplerate = *p;
        }
        p++;
    }
    return best_samplerate;
}

// 检查次编码器codec 是否支持sample_fmt这种采样格式
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, FILE* out) {
    
    
    int ret = -1;

    // 把数据帧送入编码器
    ret = avcodec_send_frame(ctx, frame);
    if (ret < 0) {
    
    
        av_log(NULL, AV_LOG_ERROR, "Failed to send frame to encoder: %s!\n", av_err2str(ret));
        goto _END;
    }

    while (ret >= 0) {
    
    
        ret = avcodec_receive_packet(ctx, pkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
    
    
            return 0;
        }
        else if (ret < 0) {
    
    
            return -1; //退出tkyc
        }
        av_log(NULL, AV_LOG_DEBUG, "ptk.size:%d\n", pkt->size);
        fwrite(pkt->data, 1, pkt->size, out);
        av_packet_unref(pkt);
    }
_END:
    return 0;
}

/*
* 对音频原始数据进行编码生成aac
* 本次音频数据是自己生成的声音数据
*/
int encode_audio(int argc, char* argv[]) {
    
    

    int ret = -1;

    FILE* f = NULL;

    char* dst = NULL;
    char* codecName = NULL;

    const AVCodec* codec = NULL;
    AVCodecContext* ctx = NULL;
   

    AVFrame* frame = NULL;
    AVPacket* pkt = NULL;

    uint16_t* samples = NULL;

    av_log_set_level(AV_LOG_DEBUG);

    

    dst = "f:\\test_data\\encode_audio.aac";
    //codecName = argv[2];

    //2. 查找编码器
    //codec = avcodec_find_encoder_by_name(codecName);
    //codec = avcodec_find_encoder_by_name("libfdk_aac");
    //codec = avcodec_find_encoder_by_name("libfaac");
    codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
    if (!codec) {
    
    
        av_log(NULL, AV_LOG_ERROR, "don't find Codec: %s", codecName);
        goto _ERROR;
    }

    //3. 创建编码器上下文
    ctx = avcodec_alloc_context3(codec);
    if (!ctx) {
    
    
        av_log(NULL, AV_LOG_ERROR, "NO MEMRORY\n");
        goto _ERROR;
    }

    //4. 设置编码器参数 帧的宽高、帧率、gop这些对于音频都不太重要。
    // 对于音频来说,重要的是采样大小、采样率、通道
    // 一般是 16 32 64 128k
    ctx->bit_rate = 64000;
    //ctx->sample_fmt = AV_SAMPLE_FMT_S16;//AV_SAMPLE_FMT_FLTP 采样大小
    ctx->sample_fmt = AV_SAMPLE_FMT_FLTP;//AV_SAMPLE_FMT_FLTP 采样大小
    if (!check_sample_fmt(codec, ctx->sample_fmt)) {
    
     // 这个函数判断编码器支不支持采样大小。
        av_log(NULL, AV_LOG_ERROR, "Encoder does not support sample format!\n");
        goto _ERROR;
    }

    ctx->sample_rate = select_best_sample_rate(codec);
    //av_channel_layout_copy(&ctx->ch_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO); //AV_CHANNEL_LAYOUT_MONO
    av_channel_layout_copy(&ctx->ch_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_MONO); //AV_CHANNEL_LAYOUT_MONO

    //5. 编码器与编码器上下文绑定到一起
    ret = avcodec_open2(ctx, codec, NULL);
    if (ret < 0) {
    
    
        av_log(ctx, AV_LOG_ERROR, "Don't open codec: %s \n", av_err2str(ret));
        goto _ERROR;
    }

    //6. 创建输出文件
    f = fopen(dst, "wb");
    if (!f) {
    
    
        av_log(NULL, AV_LOG_ERROR, "Don't open file:%s", dst);
        goto _ERROR;
    }

    //7. 创建AVFrame
    frame = av_frame_alloc();
    if (!frame) {
    
    
        av_log(NULL, AV_LOG_ERROR, "NO MEMORY!\n");
        goto _ERROR;
    }
    // 同样在为frame中的data分配空间的时候,告诉它一些参数信息
    frame->nb_samples = ctx->frame_size;
    frame->format = AV_SAMPLE_FMT_S16; //AV_SAMPLE_FMT_FLTP
    av_channel_layout_copy(&frame->ch_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_MONO); //AV_CHANNEL_LAYOUT_MONO
    //av_channel_layout_copy(&frame->ch_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO); //AV_CHANNEL_LAYOUT_MONO
    frame->sample_rate = ctx->sample_rate;
    ret = av_frame_get_buffer(frame, 0);
    if (ret < 0) {
    
    
        av_log(NULL, AV_LOG_ERROR, "Could not allocate the video frame \n");
        goto _ERROR;
    }

    //8. 创建AVPacket
    pkt = av_packet_alloc();
    if (!pkt) {
    
    
        av_log(NULL, AV_LOG_ERROR, "NO MEMORY!\n");
        goto _ERROR;
    }

    //9. 生成音频内容,和视频相似,都是以帧为单位,区别的地方:
    // 音频的帧,它是流式的,也就是说我们在任何地点、任何位置都可以对这个数据进行切割。
    // 切割成指定大小的一帧数据,对于视频来说,每一帧都是一张图像。

    float t = 0;
    float tincr = 4 * M_PI * 440 / ctx->sample_rate;

    for (int i = 0; i < 200; i++) {
    
    
        ret = av_frame_make_writable(frame);
        if (ret < 0) {
    
    
            av_log(NULL, AV_LOG_ERROR, "Could not allocate space!\n");
            goto _ERROR;
        }

        // frame[0]是音频数据地址
        samples = (uint16_t*)frame->data[0]; //FLTP 32 (uint32_t*)
        for (int j = 0; j < ctx->frame_size; j++) {
    
    
            samples[2 * j] = (int)(sin(t) * 10000); //4
            for (int k = 1; k < ctx->ch_layout.nb_channels; k++) {
    
    
                samples[2 * j + k] = samples[2 * j]; //4
            }
            t += tincr;
        }
        encode(ctx, frame, pkt, f);
    }
    //10. 编码
    encode(ctx, NULL, pkt, f);
_ERROR:
    //ctx
    if (ctx) {
    
    
        avcodec_free_context(&ctx);
    }

    //avframe
    if (frame) {
    
    
        av_frame_free(&frame);
    }

    //avpacket
    if (pkt) {
    
    
        av_packet_free(&pkt);
    }

    //dst
    if (f) {
    
    
        fclose(f);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38056514/article/details/130190808