在上篇文章中,我们对一个视频进行了解码并重新编码,但是在处理音频时,我们使用了原视频的音频参数,因为对于不同的编码器,需要的音频帧大小不同,而 swr_convert 不会对音频帧的大小进行处理,本文增加了对于音频帧大小变化的情况的处理。

  1. 首先创建一个 AVAudioFifo 对象
    AVAudioFifo *av_audio_fifo_alloc(enum AVSampleFormat sample_fmt, int channels,
    int nb_samples);
    ​​nb_samples​​(初始分配的样本数)指定FIFO缓冲区初始分配的样本数量(每个声道),FIFO会在需要时自动扩展。如果预先知道需要处理的最大样本量,可以设置一个较大的初始值以避免多次重分配内存。
  2. 把重采样后的帧传入 fifo
    int av_audio_fifo_write(AVAudioFifo *af, void * const *data, int nb_samples);
  3. 判断 fifo 内的数据是否大于所需帧大小,如果是,则读出数据,并进行编码
    int av_audio_fifo_size(AVAudioFifo *af) // 获取帧大小
    int av_audio_fifo_read(AVAudioFifo *af, void * const *data, int nb_samples); // 从 fifo 读取数据
        if (m_audioCodecContext->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE) {
            ret = avcodec_send_frame(m_audioCodecContext, m_audioFrame);
            if (ret < 0) {
                showError(ret);
                return false;
            }

            while (true) {
                int ret = avcodec_receive_packet(m_audioCodecContext, m_packet);
                if(ret < 0) {
                    showError(ret);
                    break;
                }
                // 将数据包中的有效时间字段(时间戳/持续时间)从一个时基转换为 输出流的时间
                av_packet_rescale_ts(m_packet, m_videoCodecContext->time_base, m_audioStream->time_base);
                m_packet->stream_index = m_audioStream->index;
                av_interleaved_write_frame(m_formatContext, m_packet);   // 将数据包写入输出媒体文件
                av_packet_unref(m_packet);
            }
        } else {
            av_audio_fifo_write(m_fifo, (void**)m_audioFrame->data, m_audioFrame->nb_samples);
            while (av_audio_fifo_size(m_fifo) >= m_audioCodecContext->frame_size) {
                AVFrame* enc_frame = av_frame_alloc();
                enc_frame->nb_samples = m_audioCodecContext->frame_size;
                enc_frame->format = m_audioCodecContext->sample_fmt;
                enc_frame->ch_layout = m_audioCodecContext->ch_layout;
                enc_frame->sample_rate = m_audioCodecContext->sample_rate;

                av_frame_get_buffer(enc_frame, 0);
                av_audio_fifo_read(m_fifo, (void**)enc_frame->data, m_audioCodecContext->frame_size);

                // 送编码器处理
                avcodec_send_frame(m_audioCodecContext, enc_frame);
                av_frame_free(&enc_frame);

                while (true) {
                    int ret = avcodec_receive_packet(m_audioCodecContext, m_packet);
                    if(ret < 0) {
                        showError(ret);
                        break;
                    }
                    av_packet_rescale_ts(m_packet, m_audioCodecContext->time_base, m_audioStream->time_base);
                    m_packet->stream_index = m_audioStream->index;
                    av_interleaved_write_frame(m_formatContext, m_packet);
                    av_packet_unref(m_packet);
                }
            }
        }

代码中分成了两种情况进行处理,如果是支持可变帧大小的编码器,直接把帧发送给编码器,如果是固定帧大小的,则写入 fifo 缓冲区,再从 fifo 读取 帧。
完整代码如下:
解码器头文件

#ifndef VIDEODECODER_H
#define VIDEODECODER_H
#include <string>
#include <memory>
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/avutil.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavutil/channel_layout.h"
#include "libswresample/swresample.h"
#include "libavutil/opt.h"
}

using namespace std;
struct MFrame {
    AVFrame* frame_ptr;
    AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
};

struct VideoInfo {
    int videoBitRate = -1;
    int width = -1;
    int height = -1;
    int fps = -1;                   // 帧率
    int gopSize;                    // 关键帧间隔
    int maxBFrames;                 // 最大 B 帧数

    int audioBitRate;
    int sampleRate = -1;
    int sampleFmt;                  // enum AVSampleFormat
    int channels;
};

class VideoDecoder
{
public:
    VideoDecoder();
    bool open(const string& filePath);
    VideoInfo getVideoInfo() { return m_videoInfo;}
    void close();
    bool decode();
    MFrame read();

private:
    void showError(int ret);

private:
    AVFormatContext* m_formatContext = nullptr;
    int m_videoIndex = -1;                              // 视频流所在索引
    int m_audioIndex = -1;                              // 音频流所在索引

    AVCodecContext* m_videoCodecContext = nullptr;      // 视频解码器实例
    AVCodecContext* m_audioCodecContext = nullptr;      // 音频解码器实例

    AVPacket* m_packet = nullptr;                       // 数据包
    AVFrame* m_frame = nullptr;                         // 数据帧
    VideoInfo m_videoInfo;

    bool m_nextPacket = true;
};

#endif // VIDEODECODER_H



#endif // VIDEOENCODER_H

解码器源文件

#include "VideoDecoder.h"
#include <iostream>
VideoDecoder::VideoDecoder() {}

bool VideoDecoder::open(const string &filePath)
{
    int ret = avformat_open_input(&m_formatContext, filePath.c_str(), NULL, NULL);
    if (ret < 0) {
        showError(ret);
        close();
        return false;
    }

    // 查找流信息
    ret = avformat_find_stream_info(m_formatContext, NULL);
    if (ret < 0) {
        close();
        return false;
    }

    // 查找视频流
    m_videoIndex = av_find_best_stream(m_formatContext, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
    if (m_videoIndex == -1) {
        close();
        return false;
    }

    // 查找视频解码器
    AVStream *videoStream = m_formatContext->streams[m_videoIndex];
    const AVCodec *videoCodec = avcodec_find_decoder(videoStream->codecpar->codec_id);
    if (videoCodec == nullptr) {
        close();
        return false;
    }

    // 创建视频解码器
    m_videoCodecContext = avcodec_alloc_context3(videoCodec);
    if (m_videoCodecContext == nullptr) {
        close();
        return false;
    }
    // 把视频流中的编解码参数复制给解码器的实例
    avcodec_parameters_to_context(m_videoCodecContext, videoStream->codecpar);
    // 打开视频解码器实例
    ret = avcodec_open2(m_videoCodecContext, NULL, NULL);
    if (ret < 0) {
        close();
        return ret;
    }

    m_videoInfo.videoBitRate = m_videoCodecContext->bit_rate;
    m_videoInfo.width = m_videoCodecContext->width;
    m_videoInfo.height = m_videoCodecContext->height;
    m_videoInfo.fps = m_videoCodecContext->framerate.num / m_videoCodecContext->framerate.den ;
    m_videoInfo.gopSize = m_videoCodecContext->gop_size;
    m_videoInfo.maxBFrames = m_videoCodecContext->max_b_frames;

    // 打开音频解码器实例
    m_audioIndex = av_find_best_stream(m_formatContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    if (m_audioIndex >= 0) {
        AVStream *audioStream = m_formatContext->streams[m_audioIndex];
        const AVCodec *audioCodec = avcodec_find_decoder(audioStream->codecpar->codec_id);
        if (audioCodec == nullptr) {
            close();
            return false;
        }
        m_audioCodecContext = avcodec_alloc_context3(audioCodec);
        if (m_audioCodecContext == nullptr) {
            close();
            return false;
        }
        avcodec_parameters_to_context(m_audioCodecContext, audioStream->codecpar);
        ret = avcodec_open2(m_audioCodecContext, audioCodec, NULL);
        if (ret < 0) {
            close();
            return false;
        }

        m_videoInfo.sampleRate = m_audioCodecContext->sample_rate;
        m_videoInfo.sampleFmt = m_audioCodecContext->sample_fmt;
        m_videoInfo.audioBitRate = m_audioCodecContext->bit_rate;
        m_videoInfo.channels = m_audioCodecContext->ch_layout.nb_channels;
    }

    m_packet = av_packet_alloc();
    m_frame  = av_frame_alloc();

    return true;
}

void VideoDecoder::close() {
    if (m_formatContext != nullptr) {
        avformat_close_input(&m_formatContext);
    }

    if (m_videoCodecContext != nullptr) {
        avcodec_free_context(&m_videoCodecContext);
    }

    if (m_audioCodecContext != nullptr) {
        avcodec_free_context(&m_audioCodecContext);
    }

    if (m_packet != nullptr) {
        av_packet_free(&m_packet);
    }

    if (m_frame != nullptr) {
        av_frame_free(&m_frame);
    }
}

bool VideoDecoder::decode()
{
    while (av_read_frame(m_formatContext, m_packet) >= 0) { // 轮询数据包
        if (m_packet->stream_index == m_videoIndex) {
            int ret = avcodec_send_packet(m_videoCodecContext, m_packet);
            while ( ret >= 0) {
                av_frame_unref(m_frame);
                ret = avcodec_receive_frame(m_videoCodecContext, m_frame);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                    break;
                } else if (ret < 0) {
                    return false;
                }
            }
        } else if (m_packet->stream_index == m_audioIndex) {
            int ret = avcodec_send_packet(m_audioCodecContext, m_packet);
            while ( ret >= 0) {
                av_frame_unref(m_frame);
                ret = avcodec_receive_frame(m_audioCodecContext, m_frame);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                    break;
                } else if (ret < 0) {
                    return false;
                }
            }
        }
        // 清空 packet 内容
        av_packet_unref(m_packet);
    }
    return true;
}

MFrame VideoDecoder::read()
{
    MFrame outFrame;
    // 这里用 while 是因为在解码某个视频时,avcodec_send_packet 失败了,但是视频是能正常播放的,且跳过这个数据包后编解码都是正常的
    // 故错误后没有返回空值,而是继续读下一个数据包
    int ret = - 1;
    while (m_nextPacket && ret < 0) {
        av_packet_unref(m_packet);
        ret = av_read_frame(m_formatContext, m_packet);
        if (ret < 0) {
            return MFrame();
        }
        if (m_packet->stream_index == m_videoIndex) {
            ret = avcodec_send_packet(m_videoCodecContext, m_packet);
        } else {
            ret = avcodec_send_packet(m_audioCodecContext, m_packet);
        }

        if (ret < 0) {
            showError(ret);
        }
    }

    m_nextPacket = true;
    av_frame_unref(m_frame);
    if (m_packet->stream_index == m_videoIndex) {
        int ret = avcodec_receive_frame(m_videoCodecContext, m_frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            return read();
        } else if (ret < 0) {
            return MFrame();
        }
        m_nextPacket = false;
        outFrame.frame_ptr = m_frame;
        outFrame.type = AVMEDIA_TYPE_VIDEO;
        return outFrame;
    } else {
        int ret = avcodec_receive_frame(m_audioCodecContext, m_frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            return read();
        } else if (ret < 0) {
            showError(ret);
            return MFrame();
        }
        m_nextPacket = false;
        outFrame.frame_ptr = m_frame;
        outFrame.type = AVMEDIA_TYPE_AUDIO;
        return outFrame;
    }
    return MFrame();
}

void VideoDecoder::showError(int ret)
{
    char errorBuf[1024];
    av_strerror(ret, errorBuf, sizeof(errorBuf));
    std::cerr << errorBuf << std::endl;
}

编码器头文件

#ifndef VIDEOENCODER_H
#define VIDEOENCODER_H
#include <string>
#include <vector>
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/avutil.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavutil/channel_layout.h"
#include "libswresample/swresample.h"
#include "libavutil/opt.h"
#include "libavutil/audio_fifo.h"
}
#include "VideoDecoder.h"

struct VideoParams {
    int width;
    int height;
    int fps;
    int bitRate;
    int gopSize;
    int maxBFrames;
};

struct AudioParams {
    int bitRate;
    int sampleRate;
    int sampleFmt;
    int channels;
};

using namespace std;
class VideoEncoder
{
public:
    VideoEncoder();
    void setVideoParam(const VideoParams& param) { m_videoParam = param;}
    void setAudioParam(const AudioParams& param) { m_audioParam = param;}

    bool open(const string& filePath);
    bool write(const MFrame& frame);
    void close();

private:
    bool initSwsContext(const AVFrame* frame);
    bool initSwrContext(const AVFrame* frame);
    void clear();
    void showError(int ret);

private:
    VideoParams m_videoParam;
    AudioParams m_audioParam;

    AVFormatContext* m_formatContext = nullptr;
    AVCodecContext* m_videoCodecContext = nullptr;      // 视频编码器实例
    AVCodecContext* m_audioCodecContext = nullptr;      // 音频编码器实例
    AVStream * m_videoStream = nullptr;
    AVStream* m_audioStream = nullptr;
    AVPacket* m_packet = nullptr;                       // 数据包
    AVFrame* m_audioFrame = nullptr;                    // 数据帧
    AVFrame* m_videoFrame = nullptr;

    SwsContext* m_swsContext = nullptr;                 // 视频格式转换实例
    SwrContext* m_swrContext = nullptr;                 // 音频格式转换实例

    int m_audioPts = 0;
    int m_videoPts = 0;

    AVAudioFifo* m_fifo = nullptr;                      // 音频缓存区
};


#endif // VIDEOENCODER_H

编码器源文件

#include "VideoEncoder.h"
#include <iostream>
VideoEncoder::VideoEncoder() {}

bool VideoEncoder::open(const string &filePath)
{
    m_audioPts = 0;
    m_videoPts = 0;
    int ret = avformat_alloc_output_context2(&m_formatContext, nullptr, nullptr, filePath.c_str());
    if (ret < 0) {
        clear();
        return false;
    }

    // 打开输出文件
    ret = avio_open(&m_formatContext->pb, filePath.c_str(), AVIO_FLAG_WRITE);
    if (ret < 0) {
        clear();
        return false;
    }

    // 查找视频编码器
    const AVCodec* videoCodec = avcodec_find_encoder(m_formatContext->oformat->video_codec);
    if(videoCodec) {
        m_videoCodecContext = avcodec_alloc_context3(videoCodec);
        if(!m_videoCodecContext) {
            clear();
            return false;
        }

        // 设置编码器上下文参数
        m_videoCodecContext->width = m_videoParam.width;
        m_videoCodecContext->height = m_videoParam.height;
        m_videoCodecContext->time_base = {1, m_videoParam.fps};
        m_videoCodecContext->framerate = {m_videoParam.fps, 1};
        m_videoCodecContext->bit_rate = m_videoParam.bitRate;
        m_videoCodecContext->gop_size = m_videoParam.gopSize;
        m_videoCodecContext->max_b_frames = 0;
        m_videoCodecContext->pix_fmt = AV_PIX_FMT_YUV420P;

        ret = avcodec_open2(m_videoCodecContext, nullptr, nullptr);
        if(ret < 0) {
            clear();
            return false;
        }

        m_videoStream = avformat_new_stream(m_formatContext, nullptr);
        // 写入 packet 时有进行时间基转换,因此这里不需要设置,笔者设置了反而导致有的视频时长不对
        //m_videoStream->time_base = m_videoCodecContext->time_base;
        m_videoStream->codecpar->codec_tag = 0;
        if(!m_videoStream) {
            clear();
            return false;
        }
        ret = avcodec_parameters_from_context(m_videoStream->codecpar, m_videoCodecContext);
        if(ret < 0) {
            clear();
            return false;
        }
    }

    // 这里将音频编码器设置成和原视频一样,不同的音频编码器对应的样本数可能不同,因此需要额外处理
    const AVCodec* audioCodec = avcodec_find_encoder(m_formatContext->oformat->audio_codec);
    if(audioCodec) {
        m_audioCodecContext = avcodec_alloc_context3(audioCodec);
        m_audioCodecContext->bit_rate = m_audioParam.bitRate;
        m_audioCodecContext->sample_rate = m_audioParam.sampleRate;
        m_audioCodecContext->sample_fmt = (AVSampleFormat)m_audioParam.sampleFmt;
        m_audioCodecContext->time_base = {1, m_audioParam.sampleRate};
        m_audioCodecContext->codec_type = AVMEDIA_TYPE_AUDIO;

        AVChannelLayout layout = AV_CHANNEL_LAYOUT_STEREO;
        ret = av_channel_layout_copy(&m_audioCodecContext->ch_layout, &layout);
        if (ret < 0) {
            clear();
            return false;
        }
        ret = avcodec_open2(m_audioCodecContext, nullptr, nullptr);
        if(ret < 0) {
            clear();
            return false;
        }

        m_audioStream = avformat_new_stream(m_formatContext, nullptr);
       // m_audioStream->time_base = m_audioCodecContext->time_base;
        m_audioStream->codecpar->codec_tag = 0;
        if(!m_audioStream) {
            clear();
            return false;
        }
        ret = avcodec_parameters_from_context(m_audioStream->codecpar, m_audioCodecContext);
        if(ret < 0) {
            clear();
            return false;
        }

        m_fifo = av_audio_fifo_alloc(m_audioCodecContext->sample_fmt, m_audioCodecContext->ch_layout.nb_channels, 2048);
    }

    ret = avformat_write_header(m_formatContext, nullptr);
    if(ret < 0) {
        clear();
        return false;
    }

    m_packet = av_packet_alloc();
    m_audioFrame = av_frame_alloc();
    m_videoFrame = av_frame_alloc();
    return true;
}

bool VideoEncoder::write(const MFrame& frame)
{
    if (frame.type == AVMEDIA_TYPE_VIDEO) {
        if (m_swsContext == nullptr) {
            initSwsContext(frame.frame_ptr);
        }
        av_frame_unref(m_videoFrame);
        m_videoFrame->format = m_videoCodecContext->pix_fmt;
        m_videoFrame->width = m_videoCodecContext->width;
        m_videoFrame->height = m_videoCodecContext->height;
        m_videoFrame->pts = m_videoPts++;
        int ret = av_frame_get_buffer(m_videoFrame, 0);
        if (ret < 0) {
            showError(ret);
            return false;
        }
        ret = sws_scale(
            m_swsContext,
            frame.frame_ptr->data, frame.frame_ptr->linesize,
            0, frame.frame_ptr->height,
            m_videoFrame->data, m_videoFrame->linesize);

        ret = avcodec_send_frame(m_videoCodecContext, m_videoFrame);
        if (ret < 0) {
            showError(ret);
            return false;
        }

        while (true) {
            int ret = avcodec_receive_packet(m_videoCodecContext, m_packet);
            if(ret < 0) {
                showError(ret);
                break;
            }
            // 将数据包中的有效时间字段(时间戳/持续时间)从一个时基转换为 输出流的时间
            av_packet_rescale_ts(m_packet, m_videoCodecContext->time_base, m_videoStream->time_base);
            // 不设置流索引的话会导致数据异常
            m_packet->stream_index = m_videoStream->index;
            av_interleaved_write_frame(m_formatContext, m_packet);   // 将数据包写入输出媒体文件
            av_packet_unref(m_packet);
        }

    } else if (frame.type == AVMEDIA_TYPE_AUDIO){
        if (m_swrContext == nullptr) {
            initSwrContext(frame.frame_ptr);
        }
        av_frame_unref(m_audioFrame);
        m_audioFrame->format = m_audioCodecContext->sample_fmt;
        int ret = av_channel_layout_copy(&m_audioFrame->ch_layout, &m_audioCodecContext->ch_layout);
        if (ret < 0) {
            showError(ret);
            return false;
        }

        // 计算输出样本数(使用输入帧的样本数)
        int out_nb_samples = swr_get_out_samples(m_swrContext, frame.frame_ptr->nb_samples);
        if (out_nb_samples < 0) {
            av_frame_free(&m_audioFrame);
            return false;
        }
        m_audioFrame->nb_samples = out_nb_samples;

        // 分配缓冲区
        ret = av_frame_get_buffer(m_audioFrame, 0);
        if (ret < 0) {
            showError(ret);
            av_frame_free(&m_audioFrame);
            return false;
        }

        // 进行音频重采样
        out_nb_samples = swr_convert(m_swrContext,
                          m_audioFrame->data, out_nb_samples,
                          (const uint8_t **)frame.frame_ptr->data, frame.frame_ptr->nb_samples);
        if (out_nb_samples < 0) {
            av_frame_free(&m_audioFrame);
            showError(ret);
            return false;
        }
        m_audioFrame->nb_samples = out_nb_samples;
        m_audioFrame->pts = m_audioPts;
        m_audioPts += m_audioFrame->nb_samples;

        if (m_audioCodecContext->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE) {
            ret = avcodec_send_frame(m_audioCodecContext, m_audioFrame);
            if (ret < 0) {
                showError(ret);
                return false;
            }

            while (true) {
                int ret = avcodec_receive_packet(m_audioCodecContext, m_packet);
                if(ret < 0) {
                    showError(ret);
                    break;
                }
                // 将数据包中的有效时间字段(时间戳/持续时间)从一个时基转换为 输出流的时间
                av_packet_rescale_ts(m_packet, m_videoCodecContext->time_base, m_audioStream->time_base);
                m_packet->stream_index = m_audioStream->index;
                av_interleaved_write_frame(m_formatContext, m_packet);   // 将数据包写入输出媒体文件
                av_packet_unref(m_packet);
            }
        } else {
            av_audio_fifo_write(m_fifo, (void**)m_audioFrame->data, m_audioFrame->nb_samples);
            while (av_audio_fifo_size(m_fifo) >= m_audioCodecContext->frame_size) {
                AVFrame* enc_frame = av_frame_alloc();
                enc_frame->nb_samples = m_audioCodecContext->frame_size;
                enc_frame->format = m_audioCodecContext->sample_fmt;
                enc_frame->ch_layout = m_audioCodecContext->ch_layout;
                enc_frame->sample_rate = m_audioCodecContext->sample_rate;

                av_frame_get_buffer(enc_frame, 0);
                av_audio_fifo_read(m_fifo, (void**)enc_frame->data, m_audioCodecContext->frame_size);

                // 送编码器处理
                avcodec_send_frame(m_audioCodecContext, enc_frame);
                av_frame_free(&enc_frame);

                while (true) {
                    int ret = avcodec_receive_packet(m_audioCodecContext, m_packet);
                    if(ret < 0) {
                        showError(ret);
                        break;
                    }
                    av_packet_rescale_ts(m_packet, m_audioCodecContext->time_base, m_audioStream->time_base);
                    m_packet->stream_index = m_audioStream->index;
                    av_interleaved_write_frame(m_formatContext, m_packet);
                    av_packet_unref(m_packet);
                }
            }
        }
    }
    return true;
}

void VideoEncoder::close()
{
    if (av_audio_fifo_size(m_fifo) > 0) {
        AVFrame* last_frame = av_frame_alloc();
        last_frame->nb_samples = m_audioCodecContext->frame_size;
        last_frame->format = m_audioCodecContext->sample_fmt;
        last_frame->ch_layout = m_audioCodecContext->ch_layout;  // 关键修改
        av_frame_get_buffer(last_frame, 0);

        // 读取并填充静音
        int read = av_audio_fifo_read(m_fifo, (void**)last_frame->data, av_audio_fifo_size(m_fifo));
        for (int ch = 0; ch < m_audioCodecContext->ch_layout.nb_channels; ch++) {
            int size = av_get_bytes_per_sample(m_audioCodecContext->sample_fmt);
            memset(last_frame->data[ch] + read * size, 0,
                   (m_audioCodecContext->frame_size - read) * size);
        }

        avcodec_send_frame(m_audioCodecContext, last_frame);
        av_frame_free(&last_frame);
        while (avcodec_receive_packet(m_videoCodecContext, m_packet) == 0) {
            av_packet_rescale_ts(m_packet, m_videoCodecContext->time_base, m_videoStream->time_base);
            av_interleaved_write_frame(m_formatContext, m_packet);
            av_packet_unref(m_packet);
        }
    }
    av_audio_fifo_free(m_fifo);

    if (m_videoCodecContext != nullptr) {
        avcodec_send_frame(m_videoCodecContext, nullptr);
        while (avcodec_receive_packet(m_videoCodecContext, m_packet) == 0) {
            av_packet_rescale_ts(m_packet, m_videoCodecContext->time_base, m_videoStream->time_base);
            av_interleaved_write_frame(m_formatContext, m_packet);
            av_packet_unref(m_packet);
        }
    }

    if (m_audioCodecContext != nullptr) {
        avcodec_send_frame(m_audioCodecContext, nullptr);
        while (avcodec_receive_packet(m_audioCodecContext, m_packet) == 0) {
            av_packet_rescale_ts(m_packet, m_audioCodecContext->time_base, m_audioStream->time_base);
            av_interleaved_write_frame(m_formatContext, m_packet);
            av_packet_unref(m_packet);
        }
    }

    if (m_formatContext != nullptr) {
        int ret = av_write_trailer(m_formatContext);
        if(ret < 0) {
            showError(ret);
            return;
        }
    }
}

bool VideoEncoder::initSwsContext(const AVFrame *frame)
{
    m_swsContext = sws_getContext(
        frame->width, frame->height, (AVPixelFormat)frame->format,
        m_videoCodecContext->width, m_videoCodecContext->height, m_videoCodecContext->pix_fmt,
        SWS_BILINEAR, nullptr, nullptr, nullptr);
    return m_swsContext != nullptr;
}

bool VideoEncoder::initSwrContext(const AVFrame *frame)
{
    int ret = swr_alloc_set_opts2(&m_swrContext,
                                  &m_audioCodecContext->ch_layout,
                                  m_audioCodecContext->sample_fmt,
                                  m_audioCodecContext->sample_rate,
                                  &frame->ch_layout,
                                  (AVSampleFormat)frame->format,
                                  frame->sample_rate,
                                  0, NULL);
    if (ret < 0) {
        showError(ret);
        return false;
    }

    ret = swr_init(m_swrContext);
    if (ret < 0) {
        showError(ret);
        swr_free(&m_swrContext);
        m_swrContext = nullptr;
        return false;
    }
    return true;
}

void VideoEncoder::clear()
{
    if (m_formatContext != nullptr) {
        avformat_close_input(&m_formatContext);
    }

    if (m_videoCodecContext != nullptr) {
        avcodec_free_context(&m_videoCodecContext);
    }

    if (m_audioCodecContext != nullptr) {
        avcodec_free_context(&m_audioCodecContext);
    }

    if (m_swsContext != nullptr) {
        sws_free_context(&m_swsContext);
    }

    if (m_swrContext != nullptr) {
        swr_free(&m_swrContext);
    }

    if (m_packet != nullptr) {
        av_packet_free(&m_packet);
    }

    if (m_videoFrame != nullptr) {
        av_frame_free(&m_videoFrame);
    }

    if (m_audioFrame != nullptr) {
        av_frame_free(&m_audioFrame);
    }
}

void VideoEncoder::showError(int ret)
{
    char errorBuf[1024];
    av_strerror(ret, errorBuf, sizeof(errorBuf));
    std::cerr << errorBuf << std::endl;
}

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐