ChinaFFmpeg

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 10102|回复: 0

[Windows] ffmpeg封装h264、aac为mp4,视频播放卡顿

[复制链接]
发表于 2019-11-17 22:03:46 | 显示全部楼层 |阅读模式
参考雷神的mux代码,合成h264文件与aac文件为mp4,合成之后的mp4文件在Chrome以及mac的播放器播放会有卡顿,不知道什么原因,求助高手。代码:

// mux.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
extern "C"
{
#include "libavformat/avformat.h"
}

int main()
{
        int ret = 0;
        unsigned int iloop = 0;
        int video_index = -1, audio_index = -1;

        int64_t video_pts = 0, audio_pts = 0;
        int64_t frame_index = 0;
        int64_t frame_gap = 0;

        const char* raw_video_file = "output.h264";
        const char* raw_audio_file = "output.aac";
        const char* out_video_file = "output.mp4";

        AVStream* out_stream_video;
        AVStream* out_stream_audio;
        AVStream* in_stream;

        AVFormatContext* ifmt_video = NULL;
        AVFormatContext* ifmt_audio = NULL;
        AVFormatContext* ofmt_ctx = NULL;

        AVPacket* avpkt = av_packet_alloc();

        // 打开输入视频文件
        ret = avformat_open_input(&ifmt_video, raw_video_file, NULL, NULL);
        if (ret < 0)
        {
                printf("avformat_open_input video fail,err=%d", ret);
                ret = -1;
                goto end;
        }

        ret = avformat_find_stream_info(ifmt_video, NULL);
        if (ret < 0)
        {
                printf("avformat_find_stream_info video fail,err=%d", ret);
                ret = -2;
                goto end;
        }
        av_dump_format(ifmt_video,0,raw_video_file,0);

        // 打开输入音频文件
        ret = avformat_open_input(&ifmt_audio, raw_audio_file, NULL, NULL);
        if (ret < 0)
        {
                printf("avformat_open_input audio fail,err=%d", ret);
                ret = -3;
                goto end;
        }

        ret = avformat_find_stream_info(ifmt_audio, NULL);
        if (ret < 0)
        {
                printf("avformat_find_stream_info audio fail,err=%d", ret);
                ret = -4;
                goto end;
        }
        av_dump_format(ifmt_audio, 0, raw_audio_file, 0);

        // 打开输出文件
        ret = avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_video_file);
        if (ret < 0)
        {
                printf("avformat_alloc_output_context2 fail,err=%d", ret);
                ret = -5;
                goto end;
        }

        // 从视频文件中查找视频流,然后在输出文件添加流
        for (iloop = 0; iloop < ifmt_video->nb_streams; iloop++)
        {
                if (ifmt_video->streams[iloop]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
                {
                        out_stream_video = avformat_new_stream(ofmt_ctx, NULL);
                        in_stream = ifmt_video->streams[iloop];
                        avcodec_parameters_copy(out_stream_video->codecpar, in_stream->codecpar);
                        out_stream_video->codecpar->codec_tag = 0;
                        video_index =iloop;
                }
                else
                {
                        continue;
                }
        }
        //}

        // 从音频文件中查找音频流,然后在输出文件添加流
        for (iloop = 0; iloop < ifmt_audio->nb_streams; iloop++)
        {
                if (ifmt_audio->streams[iloop]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
                {
                        out_stream_audio = avformat_new_stream(ofmt_ctx, NULL);
                        in_stream = ifmt_audio->streams[iloop];
                        avcodec_parameters_copy(out_stream_audio->codecpar, in_stream->codecpar);
                        out_stream_audio->codecpar->codec_tag = 0;
                        audio_index = iloop;
                }
                else
                {
                        continue;
                }
        }
        if (!(ofmt_ctx->flags & AVFMT_NOFILE))
        {
                avio_open(&ofmt_ctx->pb, out_video_file, AVIO_FLAG_WRITE);
        }

        // 写header
        ret = avformat_write_header(ofmt_ctx, NULL);
        if (ret < 0)
        {
                printf("avformat_write_header fail,err=%d\n", ret);
                ret = -6;
                goto end;
        }

        //封装
        while (true)
        {
                if (av_compare_ts(video_pts, ifmt_video->streams[video_index]->time_base, audio_pts, ifmt_audio->streams[audio_index]->time_base) <= 0)
                {
                        // 写视频
                        ret = av_read_frame(ifmt_video,avpkt);
                        if (ret < 0)
                        {
                                break;
                        }
                        if (avpkt->pts == AV_NOPTS_VALUE)
                        {
                                // h264视频文件没有pts,需要手动设置
                                frame_gap = (int64_t)(AV_TIME_BASE / av_q2d(ifmt_video->streams[video_index]->r_frame_rate));
                                avpkt->pts = av_rescale_q_rnd(frame_gap * frame_index, AVRational{1,AV_TIME_BASE}, ifmt_video->streams[video_index]->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                                avpkt->dts = avpkt->pts;
                                avpkt->duration = av_rescale_q_rnd(frame_gap, AVRational{ 1,AV_TIME_BASE }, ifmt_video->streams[video_index]->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                                /*avpkt->pts = (int64_t)(frame_index * frame_gap) / (int64_t)(av_q2d(ifmt_video->streams[video_index]->time_base) * AV_TIME_BASE);
                                avpkt->dts = avpkt->pts;
                                avpkt->duration = (int64_t)frame_gap / (int64_t)(av_q2d(ifmt_video->streams[video_index]->time_base) * AV_TIME_BASE);*/
                                frame_index++;
                                video_pts = avpkt->pts;
                        }
                        avpkt->stream_index = 0;
                        av_packet_rescale_ts(avpkt, ifmt_video->streams[video_index]->time_base, ofmt_ctx->streams[0]->time_base);
                        ret = av_interleaved_write_frame(ofmt_ctx, avpkt);
                        if (ret < 0)
                        {
                                printf("av_interleaved_write_frame video fail,err=%d\n", ret);
                                break;
                        }
                }
                else
                {
                        // 写音频
                        ret = av_read_frame(ifmt_audio, avpkt);
                        if (ret < 0)
                        {
                                break;
                        }
                        audio_pts = avpkt->pts;
                        avpkt->stream_index = 1;

                        // aac文件是含有pts信息的
                        av_packet_rescale_ts(avpkt, ifmt_audio->streams[audio_index]->time_base, ofmt_ctx->streams[1]->time_base);
                       
                        av_interleaved_write_frame(ofmt_ctx, avpkt);
                }
                av_packet_unref(avpkt);
        }

        av_write_trailer(ofmt_ctx);

end:
        avformat_free_context(ifmt_video);
        avformat_free_context(ifmt_audio);
        //avio_close(ofmt_ctx->pb);
        avformat_free_context(ofmt_ctx);

        return ret;
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧:
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|Archiver|ChinaFFmpeg

GMT+8, 2024-4-20 09:10 , Processed in 0.049561 second(s), 14 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表