使用C++ 封装一个FFmpeg通用性Muxer和Encoder组件-4主函数 主要逻辑的构建书写

要测试代码 获取测试数据的方式


源码github

//ffmpeg -i sound_in_sync_test.mp4 -pix_fmt yuv420p 720x576_yuv420p.yuv
//ffmpeg -i sound_in_sync_test.mp4 -vn -ar 44100 -ac 2 -f s16le 44100_2_s16le.pcm

3个输入参数

1 in_pcm 2 In_yuv 3 out_mp4

if(argc != 4) {
    
    
        printf("usage -> exe in.yuv in.pcm out.mp4");
        return -1;
    }
    // 1. 打开yuv pcm文件
    char *in_yuv_name = argv[1];
    char *in_pcm_name = argv[2];
    char *out_mp4_name = argv[3];
    FILE *in_yuv_fd = NULL;
    FILE *in_pcm_fd = NULL;
    //1. 打开测试文件
    // 打开YUV文件
    in_yuv_fd = fopen(in_yuv_name, "rb");
    if( !in_yuv_fd )
    {
    
    
        printf("Failed to open %s file\n", in_yuv_name);
        return -1;
    }

    // 打开PCM文件
    in_pcm_fd = fopen(in_pcm_name, "rb");
    if( !in_pcm_fd )
    {
    
    
        printf("Failed to open %s file\n", in_pcm_name);
        return -1;
    }

初始化编码器

初始化视频编码器

   int ret = 0;
    // 2. 初始化编码器,包括视频、音频编码器, 分配yuv、pcm的帧buffer
    // 2.1 初始化video
    // 初始化编码器
    int yuv_width = YUV_WIDTH;
    int yuv_height = YUV_HEIGHT;
    int yuv_fps = YUV_FPS;
    int video_bit_rate = VIDEO_BIT_RATE;
    VideoEncoder video_encoder;
    ret = video_encoder.InitH264(yuv_width, yuv_height, yuv_fps, video_bit_rate);
    if(ret < 0)
    {
    
    
        printf("video_encoder.InitH264 failed\n");
        return -1;
    }
    // 分配yuv buf
    int y_frame_size = yuv_width * yuv_height;
    int u_frame_size = yuv_width * yuv_height /4;
    int v_frame_size = yuv_width * yuv_height /4;
    int yuv_frame_size = y_frame_size + u_frame_size + v_frame_size;
    uint8_t *yuv_frame_buf = (uint8_t *)malloc(yuv_frame_size);
    if(!yuv_frame_buf)
    {
    
    
        printf("malloc(yuv_frame_size)\n");
        return -1;
    }

初始化音频编码器

  // 2.2 初始化audio
    // 初始化音频编码器
    int pcm_channels= PCM_CHANNELS;
    int pcm_sample_rate = PCM_SAMPLE_RATE;
    int pcm_sample_format = PCM_SAMPLE_FORMAT;
    int audio_bit_rate = AUDIO_BIT_RATE;
    AudioEncoder audio_encoder;
    ret = audio_encoder.InitAAC(pcm_channels, pcm_sample_rate, audio_bit_rate);
    if(ret < 0)
    {
    
    
        printf("audio_encoder.InitAAC failed\n");
        return -1;
    }
    // 分配pcm buf
    // pcm_frame_size  = 单个采样点占用的字节 * 通道数量 * 每个通道有多少给采用点
    int pcm_frame_size = av_get_bytes_per_sample((AVSampleFormat)pcm_sample_format)
                         *pcm_channels * audio_encoder.GetFrameSize();
    if(pcm_frame_size <= 0) {
    
    
        printf("pcm_frame_size <= 0\n");
        return -1;
    }
    uint8_t *pcm_frame_buf = (uint8_t *)malloc(pcm_frame_size);
    if(!pcm_frame_buf)
    {
    
    
        printf("malloc(pcm_frame_size)\n");
        return -1;
    }

初始化重采样器

// 初始化重采样
    AudioResampler audio_resampler;
    ret = audio_resampler.InitFromS16ToFLTP(pcm_channels, pcm_sample_rate,
                                            audio_encoder.GetChannels(), audio_encoder.GetSampleRate());
    if(ret < 0)
    {
    
    
        printf("audio_resampler.InitFromS16ToFLTP failed\n");
        return -1;
    }

Muxer初始化和创建新流

    // 3. mp4初始化 包括新建流,open io, send header
    Muxer mp4_muxer;
    ret = mp4_muxer.Init(out_mp4_name);
    if(ret < 0)
    {
    
    
        printf("mp4_muxer.Init failed\n");
        return -1;
    }

    ret = mp4_muxer.AddStream(video_encoder.GetCodecContext());
    if(ret < 0)
    {
    
    
        printf("mp4_muxer.AddStream video failed\n");
        return -1;
    }

    ret = mp4_muxer.AddStream(audio_encoder.GetCodecContext());
    if(ret < 0)
    {
    
    
        printf("mp4_muxer.AddStream audio failed\n");
        return -1;
    }

    ret = mp4_muxer.Open();
    if(ret < 0)
    {
    
    
        printf("mp4_muxer.Open failed\n");
        return -1;
    }

    ret = mp4_muxer.SendHeader();
    if(ret < 0)
    {
    
    
        printf("mp4_muxer.SendHeader failed\n");
        return -1;
    }

编码


 // 4. 在while循环读取yuv、pcm进行编码然后发送给MP4 muxer   以及 PTS 计算
    // 4.1 时间戳相关
    int64_t audio_time_base = AUDIO_TIME_BASE;
    int64_t video_time_base = VIDEO_TIME_BASE;
    double audio_pts = 0;
    double video_pts = 0;
    double audio_frame_duration = 1.0 * audio_encoder.GetFrameSize()/pcm_sample_rate*audio_time_base;
    double video_frame_duration = 1.0/yuv_fps * video_time_base;

    int audio_finish = 0;   // 两者都为0的时候才结束while循环
    int video_finish = 0;

    size_t read_len = 0;
    AVPacket *packet =  NULL;
    std::vector<AVPacket*> packets;
    int audio_index = mp4_muxer.GetAudioStreamIndex();
    int video_index = mp4_muxer.GetVideoStreamIndex();
    while (1) {
    
    
        if(audio_finish && video_finish) {
    
    
            break;
        }
        printf("apts:%0.0lf vpts:%0.0lf\n", audio_pts/1000, video_pts/1000);
        if((video_finish != 1 && audio_pts > video_pts)   // audio和vidoe都还有数据,优先audio(audio_pts > video_pts)
           ||  (video_finish != 1 && audio_finish == 1)) {
    
    
           // 读取 从 yuv 文件中读取一帧文件 
            read_len = fread(yuv_frame_buf, 1, yuv_frame_size, in_yuv_fd);
            if(read_len < yuv_frame_size) {
    
    
                video_finish = 1;
                printf("fread yuv_frame_buf finish\n");
            }

            if(video_finish != 1) {
    
    
//                packet = video_encoder.Encode(yuv_frame_buf, yuv_frame_size, video_index,
//                                              video_pts, video_time_base);
				// 也是在这里绑定了 packet 和 新创建的流
                ret = video_encoder.Encode(yuv_frame_buf, yuv_frame_size,
                                           video_index, video_pts, video_time_base,
                                           packets);
            }else {
    
    
//                packet = video_encoder.Encode(NULL, 0, video_index,
//                                              video_pts, video_time_base);
                printf("flush video encoder\n");
                //传入空就是冲刷编码器  
                ret = video_encoder.Encode(NULL, 0,
                                           video_index, video_pts, video_time_base,
                                           packets);
            }
            video_pts += video_frame_duration;  // 叠加pts
            if(ret >= 0) {
    
    
                for(int i = 0; i < packets.size(); i++) {
    
    

                    ret = mp4_muxer.SendPacket(packets[i]);
                    printf("123");
                }
            }
            packets.clear();
        } else if(audio_finish != 1) {
    
    
            read_len = fread(pcm_frame_buf, 1, pcm_frame_size, in_pcm_fd);
            if(read_len < pcm_frame_size) {
    
    
                audio_finish = 1;
                printf("fread pcm_frame_buf finish\n");
            }

            if(audio_finish != 1) {
    
    
                AVFrame *fltp_frame = AllocFltpPcmFrame(pcm_channels, audio_encoder.GetFrameSize());
                ret = audio_resampler.ResampleFromS16ToFLTP(pcm_frame_buf, fltp_frame);
                if(ret < 0)
                    printf("ResampleFromS16ToFLTP error\n");
//                packet = audio_encoder.Encode(fltp_frame, audio_index,
//                                              audio_pts, audio_time_base);
                    ret = audio_encoder.Encode(fltp_frame,audio_index,audio_pts,audio_time_base,packets);

                FreePcmFrame(fltp_frame);
            }else {
    
    
                printf("flush audio encoder\n");
//                packet = audio_encoder.Encode(NULL,video_index,
//                                              audio_pts, audio_time_base);
                ret = audio_encoder.Encode(NULL,audio_index,audio_pts,audio_time_base,packets);

            }
            audio_pts += audio_frame_duration;  // 叠加pts


            if(ret >= 0) {
    
    
                for(int i = 0; i < packets.size(); i++) {
    
    
                    ret = mp4_muxer.SendPacket(packets[i]);
                }
            }
            packets.clear();
        }
    }
    ret = mp4_muxer.SendTrailer();
    if(ret < 0)
    {
    
    
        printf("mp4_muxer.SendTrailer failed\n");
    }

    printf("write mp4 finish\n");

    if(yuv_frame_buf)
        free(yuv_frame_buf);
    if(pcm_frame_buf)
        free(pcm_frame_buf);
    if(in_yuv_fd)
        fclose(in_yuv_fd);
    if(in_pcm_fd)
        fclose(in_pcm_fd);

    return 0;
}

全部代码

//
// Created by zyc on 2022/4/8.
//

#include "Muxer.h"


Muxer::Muxer()
{
    
    

}

Muxer::~Muxer()
{
    
    

}

int Muxer::Init(const char *url)
{
    
    
    int ret = avformat_alloc_output_context2(&fmt_ctx_, NULL, NULL,url);
    if(ret < 0) {
    
    
        char errbuf[1024] = {
    
    0};
        av_strerror(ret, errbuf, sizeof(errbuf) - 1);
        printf("avformat_alloc_output_context2 failed:%s\n", errbuf);
        return -1;
    }
    url_ = url;
    return 0;
}

void Muxer::DeInit()
{
    
    
    if(fmt_ctx_) {
    
    
        avformat_close_input(&fmt_ctx_);
    }
    url_ = "";
    aud_codec_ctx_ = NULL;
    aud_stream_ = NULL;
    audio_index_ = -1;

    vid_codec_ctx_ = NULL;
    vid_stream_ = NULL;
    video_index_ = -1;
}

int Muxer::AddStream(AVCodecContext *codec_ctx)
{
    
    
    if(!fmt_ctx_) {
    
    
        printf("fmt ctx is NULL\n");
        return -1;
    }
    if(!codec_ctx) {
    
    
        printf("codec ctx is NULL\n");
        return -1;
    }
    AVStream *st = avformat_new_stream(fmt_ctx_, NULL);
    if(!st) {
    
    
        printf("avformat_new_stream failed\n");
        return -1;
    }
    //    st->codecpar->codec_tag = 0;
    // 从编码器上下文复制
    avcodec_parameters_from_context(st->codecpar, codec_ctx);
    av_dump_format(fmt_ctx_, 0, url_.c_str(), 1);

    // 判断当前的是视频流还是音频流
    if(codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
    
    
        aud_codec_ctx_ = codec_ctx;
        aud_stream_ = st;
        audio_index_ = st->index;
    }  else if(codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
    
    
        vid_codec_ctx_ = codec_ctx;
        vid_stream_ = st;
        video_index_ = st->index;
    }
    return 0;
}

int Muxer::SendHeader()
{
    
    
    if(!fmt_ctx_) {
    
    
        printf("fmt ctx is NULL\n");
        return -1;
    }
    int ret = avformat_write_header(fmt_ctx_, NULL);
    if(ret < 0) {
    
    
        char errbuf[1024] = {
    
    0};
        av_strerror(ret, errbuf, sizeof(errbuf) - 1);
        printf("avformat_write_header failed:%s\n", errbuf);
        return -1;
    }
    return 0;
}

int Muxer::SendPacket(AVPacket *packet)
{
    
    
    int stream_index = packet->stream_index;
    printf("index:%d, pts:%lld\n", stream_index, packet->pts);

    if(!packet || packet->size <= 0 || !packet->data) {
    
    
        printf("packet is null\n");
        if(packet)
            av_packet_free(&packet);

        return -1;
    }

    AVRational src_time_base;   // 编码后的包
    AVRational dst_time_base;   // mp4输出文件对应流的time_base
    if(vid_stream_ && vid_codec_ctx_ && stream_index == video_index_) {
    
    
        src_time_base = vid_codec_ctx_->time_base;
        dst_time_base = vid_stream_->time_base;
    } else if(aud_stream_ && aud_codec_ctx_ && stream_index == audio_index_) {
    
    
        src_time_base = aud_codec_ctx_->time_base;
        dst_time_base = aud_stream_->time_base;
    }
    // 时间基转换
    packet->pts = av_rescale_q(packet->pts, src_time_base, dst_time_base);
    packet->dts = av_rescale_q(packet->dts, src_time_base, dst_time_base);
    packet->duration = av_rescale_q(packet->duration, src_time_base, dst_time_base);

    int ret = 0;
    ret = av_interleaved_write_frame(fmt_ctx_, packet); // 不是立即写入文件,内部缓存,主要是对pts进行排序
    //    ret = av_write_frame(fmt_ctx_, packet);
    av_packet_free(&packet);
    if(ret == 0) {
    
    
        return 0;
    } else {
    
    
        char errbuf[1024] = {
    
    0};
        av_strerror(ret, errbuf, sizeof(errbuf) - 1);
        printf("av_write_frame failed:%s\n", errbuf);
        return -1;
    }
}

int Muxer::SendTrailer()
{
    
    
    if(!fmt_ctx_) {
    
    
        printf("fmt ctx is NULL\n");
        return -1;
    }
    int ret = av_write_trailer(fmt_ctx_);
    if(ret != 0) {
    
    
        char errbuf[1024] = {
    
    0};
        av_strerror(ret, errbuf, sizeof(errbuf) - 1);
        printf("av_write_trailer failed:%s\n", errbuf);
        return -1;
    }
    return 0;
}

int Muxer::Open()
{
    
    
    int ret = avio_open(&fmt_ctx_->pb, url_.c_str(), AVIO_FLAG_WRITE);
    if(ret < 0) {
    
    
        char errbuf[1024] = {
    
    0};
        av_strerror(ret, errbuf, sizeof(errbuf) - 1);
        printf("avio_open %s failed:%s\n",url_.c_str(), errbuf);
        return -1;
    }
    return 0;
}

int Muxer::GetAudioStreamIndex()
{
    
    
    return audio_index_;
}


int Muxer::GetVideoStreamIndex()
{
    
    
    return video_index_;
}























猜你喜欢

转载自blog.csdn.net/qq_33329316/article/details/124159788