ffmpeg的编码过程

263 阅读2分钟

编码器内部有一个缓冲区, 对于音频来说, 输入一帧数据就能够输出一帧数据, 但是对于视频来说, 需要输入多帧数据才能输出一帧数据, 所以无论对于音频还是视频, 都需要输入一部分数据之后才会输出数据.

创建编码器: 说是创建 其实是查找编码器, 因为所有的编解码器都是一开始就注册好的, 需要使用哪个编码器 就从列表中查找到哪个编码器. 可以通过查找ID或者名字来找到相应编码器: avcodec_find_encoder / avcodec_find_encoder_by_name.

创建上下文: avcodec_alloc_context3, “3”表示对老版本的区分, 老版本继续支持, 新版本接口增加编号. 

打开编码器: avcodec_open2, “2”的解释同上.

staticAVCodecContext* open_coder(void) {    // 创建编解码器    AVCodec *codec = avcodec_find_encoder_by_name("libfdk_aac");//    AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_AAC);    // 创建codec 上下文    AVCodecContext *codec_ctx   = avcodec_alloc_context3(codec);    codec_ctx -> sample_fmt     = AV_SAMPLE_FMT_S16;                // 输入音频采样大小 必须按照fdkaac库设置相应的值:s16    codec_ctx -> channel_layout = AV_CH_LAYOUT_STEREO;              // 输入音频声道布局    codec_ctx -> channels       = 2;                                // 输入音频声道数    codec_ctx -> sample_rate    = 44100;                            // 输入音频码率    codec_ctx -> bit_rate       = 0;                                // 设置为0 profile 才会生效    codec_ctx -> profile        = FF_PROFILE_AAC_HE;                // 选择好编码器后 会生成对应大小的码率 32k      /* open it */    if (avcodec_open2(codec_ctx, codec, NULL) < 0) {        fprintf(stderr, "Could not open codec\n");        exit(1);    }    return codec_ctx;}

输入输出数据

主要API: 

avcodec_send_frame : 输入frame

avcodec_receive_packet : 输出packet

AVFrame 存放未编码的数据pcm, AVPacket存放编码后的数据aac、opus

创建frame

staticAVFrame* create_frame(AVCodecContext *codec_ctx) {    // 音频输入数据 AVFrame *frame : 在堆中分配, AVFrame frame: 在栈中分配    AVFrame *frame = av_frame_alloc();    if (!frame) {        printf("error, no memory!");        goto __ERROR;    }    frame -> nb_samples     = 512;                          // 单通道每帧采样个数 机器读取到每个包的大小4096 位深32位 双声道, 4096/4/2 = 512    frame -> format         = codec_ctx -> sample_fmt;      // AV_SAMPLE_FMT_S16;    // 采样大小 16位 = 2byte    frame -> channel_layout = codec_ctx -> channel_layout;  // AV_CH_LAYOUT_STEREO;    av_frame_get_buffer(frame, 0);                          // 大小 = 512 * 2 * 2 = 2048    if (!frame->buf[0]) {        printf("error, av_frame_get_buffer \n");        goto __ERROR;    }    return frame;__ERROR:    if (frame) {        av_frame_free(&frame);    }    return NULL;}

// 将重采样的数据拷贝到 frame 中

memcpy((void *)frame->data[0], dst_data[0], dst_linesize);

编码

staticvoid encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, FILE *output) {    int ret = 0;    // 将数据送编码器    ret = avcodec_send_frame(ctx, frame);    if (ret < 0) {        fprintf(stderr, "Error sending the frame to the encoder [%d]\n", ret);    }    // 如果ret>=0 说明设置数据成功    while (ret >= 0) {        // 获取编码后的数据,如果成功 则重复获取 直到失败为止        ret = avcodec_receive_packet(ctx, pkt);        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {            // AVERROR 加负号 EAGAIN数据暂时不够进行编码, AVERROR_EOF 没有可用数据 需读取数据            return;        } else if (ret < 0) {            printf("error , encoding audio frame \n");            exit(-1);        }        fwrite(pkt->data, pkt->size, 1, output);        fflush(output); // fflush()会强制将缓冲区内的数据写回参数stream 指定的文件中。    }}