18.FFmpeg学习笔记 - 编码音频

本篇文章要把pcm原始音频数据编码为AAC格式的音频数据。由于aac格式只支持fltp格式的pcm数据,也就是float planar格式,所以,打算输入两个pcm原始音频文件,一个是左声道,一个是右声道,两文件都是f32le、单声道、采样率48000的音频文件。这样读入两个文件的内容,分别放入AVFrame的data[0]和data[1]当中。

编码为AAC格式,还要加入ADTS头,才能播放。

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>


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;
}

int find_sample_index(int samplerate)
{
    int adts_sample_rates[] = {96000,882000,64000,48000,441000,32000,24000,22050,16000,12000,11025,8000,7350,0,0,0};
    int i;
    for(i=0; i < 16;i++)
    {
        if(samplerate == adts_sample_rates[i])
            return i;
    }
    return 16 - 1;
}


static void encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt,
                   FILE *output)
{
    int ret;
    
    /* send the frame for encoding */
    ret = avcodec_send_frame(ctx, frame);
    if (ret < 0) {
        fprintf(stderr, "Error sending the frame to the encoder\n");
        exit(1);
    }
    
    /* read all the available output packets (in general there may be any
     * number of them */
    while (ret >= 0) {
        ret = avcodec_receive_packet(ctx, pkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;
        else if (ret < 0) {
            fprintf(stderr, "Error encoding audio frame\n");
            exit(1);
        }
        
        char bits[7] = {0};
        int aac_frame_length = 7 + pkt->size;
        int sample_index = find_sample_index(ctx->sample_rate);
        int channels = ctx->channels;
        if (channels == 8) {
            channels = 7;
        }
        
        bits[0] = 0xff;
        bits[1] = 0xf9;
        bits[2] = (ctx->profile << 6);
        bits[2] |= (sample_index << 2);
        bits[2] |= (channels >> 2);
        bits[3] |= ((channels << 6) & 0xC0);
        bits[3] |= (aac_frame_length >> 11);
        bits[4] = ((aac_frame_length >> 3) & 0xFF);
        bits[5] = ((aac_frame_length << 5) & 0xE0);
        bits[5] |= (0x7FF >> 6);
        bits[6] = 0xfc;
        
        fwrite(bits, 1, 7, output);
     
        fwrite(pkt->data, 1, pkt->size, output);
        av_packet_unref(pkt);
    }
}

void encode_audio(void)
{
    const char *infilename_left = "/Users/zhw/Desktop/sintel_f32le_left_1_48000.pcm";
    const char *infilename_right = "/Users/zhw/Desktop/sintel_f32le_right_1_48000.pcm";
    const char *outfilename = "/Users/zhw/Desktop/result.aac";
    const AVCodec *codec;
    AVCodecContext *c= NULL;
    AVFrame *frame;
    AVPacket *pkt;
    int ret;
    FILE *out_file, *in_file_left, *in_file_right;
    
    /* 编码为aac格式 */
    codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }
    
    c = avcodec_alloc_context3(codec);
    if (!c) {
        fprintf(stderr, "Could not allocate audio codec context\n");
        exit(1);
    }
    
    /* 设置码率 */
    c->bit_rate = 64000;
    
    /* 检查编码器是否支持给定的sample_fmt */
    c->sample_fmt = AV_SAMPLE_FMT_FLTP;
    if (!check_sample_fmt(codec, c->sample_fmt)) {
        fprintf(stderr, "Encoder does not support sample format %s",
                av_get_sample_fmt_name(c->sample_fmt));
        exit(1);
    }
    
    /* 设置采样率,声道等参数 */
    c->sample_rate    = 48000;
    c->channel_layout = AV_CH_LAYOUT_STEREO;
    c->channels       = av_get_channel_layout_nb_channels(c->channel_layout);
    
    /* 打开编码器 */
    if (avcodec_open2(c, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }
    
    in_file_left = fopen(infilename_left, "rb");
    if (!in_file_left) {
        fprintf(stderr, "Could not open %s\n", infilename_left);
        exit(1);
    }
    
    in_file_right = fopen(infilename_right, "rb");
    if (!in_file_right) {
        fprintf(stderr, "Could not open %s\n", infilename_right);
        exit(1);
    }
    
    out_file = fopen(outfilename, "wb");
    if (!out_file) {
        fprintf(stderr, "Could not open %s\n", outfilename);
        exit(1);
    }
    
    /* 创建pkt,用于存放编码后的输出数据 */
    pkt = av_packet_alloc();
    if (!pkt) {
        fprintf(stderr, "could not allocate the packet\n");
        exit(1);
    }
    
    /* 创建frame,存放编码前端原始数据 */
    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate audio frame\n");
        exit(1);
    }
    
    frame->nb_samples     = c->frame_size;
    frame->format         = c->sample_fmt;
    frame->channel_layout = c->channel_layout;
    
    /*设置完frame->nb_samples,frame->format,frame->channel_layout等参数后,根据这些参数创建buffer,
     即frame->data  */
    ret = av_frame_get_buffer(frame, 0);
    if (ret < 0) {
        fprintf(stderr, "Could not allocate audio data buffers\n");
        exit(1);
    }
    
    int bytes_per_sample = av_get_bytes_per_sample(c->sample_fmt);
    
    while (!feof(in_file_left) && !feof(in_file_right)) {
        //确保frame是可写的
        ret = av_frame_make_writable(frame);
        if (ret < 0)
            exit(1);
        fread(frame->data[0], 1, c->frame_size * bytes_per_sample, in_file_left);
        fread(frame->data[1], 1, c->frame_size * bytes_per_sample, in_file_right);
        encode(c, frame, pkt, out_file);
    }
    
    /* flush encoder中的剩余数据 */
    encode(c, NULL, pkt, out_file);
    
    fclose(in_file_left);
    fclose(in_file_right);
    fclose(out_file);
    
    av_frame_free(&frame);
    av_packet_free(&pkt);
    avcodec_free_context(&c);
    
    printf("finish encode\n");
    
}

猜你喜欢

转载自blog.csdn.net/whoyouare888/article/details/94715948